eklavya-mcp 1.0.1 → 1.1.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 CHANGED
@@ -34,7 +34,9 @@ State lives in `~/.eklavya/knowledge.db` (SQLite, WAL). Override with `EKLAVYA_H
34
34
  ```bash
35
35
  eklavya doctor # check the install
36
36
  eklavya config get
37
- eklavya config set mode enforced [--repo]
37
+ eklavya config set mode enforced [--repo] # how hard it pushes
38
+ eklavya config set focus concept # what it teaches
39
+ eklavya config set focus learn --topic caching
38
40
  eklavya export-rules --out .cursor/rules/eklavya.md
39
41
  eklavya db-path
40
42
  ```
@@ -12,7 +12,7 @@
12
12
  # Keep PINNED_VERSION in step with .claude-plugin/plugin.json and
13
13
  # mcp/package.json. A test asserts all three match.
14
14
 
15
- PINNED_VERSION="1.0.1"
15
+ PINNED_VERSION="1.1.0"
16
16
 
17
17
  MCP_DIR=$(cd "$(dirname "$0")/.." && pwd)
18
18
  LOCAL_SERVER="$MCP_DIR/dist/server.js"
@@ -22,7 +22,7 @@ Each concept needs a `context`: one line naming the actual decision, in the actu
22
22
  - Good: `"set httpOnly on the refresh cookie in auth.ts:42"`
23
23
  - Useless: `"used cookies"`
24
24
 
25
- If a concept has no slug yet, just log it — unknown slugs are created. Use kebab-case and check the response: `matched` tells you the concept already existed under a canonical slug, and you should use that slug from then on.
25
+ If a concept has no slug yet, just log it — unknown slugs are created. Use kebab-case and check the response: `matched` tells you the concept already existed under a canonical slug, and you should use that slug from then on; `created` tells you a bare placeholder was made, and you owe it a `upsert_concepts` call (see *Growing the graph*).
26
26
 
27
27
  Never interrupt the implementation to teach. Logging is silent. Teaching happens after the work is done, or when the developer asks.
28
28
 
@@ -35,7 +35,9 @@ error, and do not mention hooks or exit codes. Just run the quiz below, then
35
35
  finish your turn normally.
36
36
 
37
37
  It fires at most once per batch of work, so this is your one chance to teach
38
- what the task covered. If the developer declines, record the skip and let it go.
38
+ what the task covered. If the developer declines outright, record it and let it
39
+ go. If they say they do not know, that is not a decline — that is the whole
40
+ reason you are here. Teach it.
39
41
 
40
42
  ## Before you teach or quiz
41
43
 
@@ -54,9 +56,12 @@ Then call `get_session_quiz_plan`. It returns the concepts worth asking about *a
54
56
  | `tier_to_ask` | The difficulty to pitch at. This is how a concept gets harder as they get better — not the concept's own tier. |
55
57
  | `description` | The canonical one-line meaning of the concept. Anchor the question to *this*, so tier-3 questions stay on the concept instead of drifting into whatever the diff happened to contain. |
56
58
  | `context` | The real decision in the real file. This is what makes the question grounded. |
57
- | `asked_before` | Questions this developer has **already been asked** about this concept, with the tier and grade each got. |
59
+ | `asked_before` | Questions this developer has **already been asked** about this concept, with the tier, grade and `outcome` each got. |
60
+ | `already_taught` | They blanked on this before and you explained it. The next question is a follow-up, not a first encounter. |
58
61
  | `prereqs_unmet` | Prerequisites they have not mastered yet. |
59
62
  | `last_grade` | How the last attempt went. |
63
+ | `framing` | What this focus requires of the question. Authoritative — see *Focus*. |
64
+ | `bridge_context` | `learn` focus: the session's work touched this topic concept, and here is the code. |
60
65
 
61
66
  If `questions_needed` is 0, say nothing and move on. `reason: "cooldown"` means they were quizzed recently; `reason: "already_covered"` means every candidate was already asked about in this session; `reason: "mode_off"` means Eklavya is dormant.
62
67
 
@@ -69,6 +74,7 @@ This is the promise the whole tool rests on, and `asked_before` is how you keep
69
74
  - **A question in `asked_before` is spent.** Not "reword it" — spent. Ask a different thing about the same concept.
70
75
  - If they scored 4 or 5 on it, `tier_to_ask` has already moved up: the new question should be asking for something the old one did not (mechanism → judgement → failure mode).
71
76
  - If they scored 1 or 2 on it, come at the *same* level from a different angle. Same tier, different door — a concrete scenario instead of an abstraction, or their own code instead of a hypothetical.
77
+ - If they scored 0 with `outcome: "dont_know"`, you already taught this. Ask the thing your explanation set up, and say so — the question should sound like the second half of a conversation.
72
78
  - If `asked_before` is empty, you have a clean slate; use `tier_to_ask` and `description`.
73
79
  - `record_attempt` returns `repeat_question: true` if you asked something already on record. Treat it as a mistake you just made, and do not do it again in the same quiz.
74
80
 
@@ -91,6 +97,8 @@ The `known` list in the profile is the other half: **never ask about a slug in `
91
97
  - Grounded: *"I set `httpOnly: true` on the refresh cookie in `auth.ts` but left the access token in memory. What attack is that split defending against, and what does it cost us?"*
92
98
  - Textbook, avoid: *"What is an httpOnly cookie?"*
93
99
 
100
+ **…unless the focus says otherwise.** See *Focus* below. The rule above is `project` focus, which is the default and most of the time. `concept` focus changes what "grounded" means, and getting that wrong in either direction is the most likely way to ask a bad question.
101
+
94
102
  **Match the tier.** This is the difference between a quiz that teaches and one that annoys:
95
103
 
96
104
  | Tier | Asks for | Shape |
@@ -105,11 +113,11 @@ Definitions are tier 1 **only**. If you find yourself asking "what is X" at tier
105
113
 
106
114
  ## Grading
107
115
 
108
- Call `record_attempt` for **every** answer, including skips — pass `question` verbatim, because that text is what stops the same question coming back later. Grade honestly on SM-2's 0–5:
116
+ Call `record_attempt` for **every** answer, including blanks and skips — pass `question` verbatim, because that text is what stops the same question coming back later. Pass `outcome` as well: `answered`, `dont_know`, or `declined`. Grade honestly on SM-2's 0–5:
109
117
 
110
118
  | Grade | Means |
111
119
  |---|---|
112
- | 0 | skipped, or no attempt |
120
+ | 0 | no answer — either a blank ("I don't know") or a decline. Pass `outcome` to say which |
113
121
  | 1 | wrong, and the misconception is load-bearing |
114
122
  | 2 | wrong, but the shape of the idea is there |
115
123
  | 3 | correct, but hesitant or incomplete — got there slowly |
@@ -118,18 +126,80 @@ Call `record_attempt` for **every** answer, including skips — pass `question`
118
126
 
119
127
  Before you pick a number, state to yourself what in their answer justifies it. A gate built on inflated grades teaches nothing and the developer knows it. Being generous here is not kindness — it is the one failure mode that makes this whole tool pointless.
120
128
 
121
- Then give feedback in **four sentences or fewer**. Correct the specific thing they got wrong; don't re-teach the whole topic.
129
+ Then give feedback. **Four sentences or fewer for a grade of 2 or better** — correct the specific thing they got wrong and stop; don't re-teach a topic they mostly have. A blank is a different job, and has its own section below.
130
+
131
+ If they answer and get it wrong, do not immediately give the answer. Ask one narrower question that isolates the gap. If they miss that too, then teach it as below.
132
+
133
+ ## When they say "I don't know"
134
+
135
+ **This is the most important thing in this file.** A blank is not a skip. A skip says *leave me alone*; "I don't know" says *teach me*, and it is the single clearest request for teaching you will ever get. Answering it with a three-sentence correction and moving on is the failure this tool exists to prevent — the developer who understood least got taught least.
136
+
137
+ Both record as grade 0. What separates them is `outcome`, and what you do next.
138
+
139
+ **Teach it. Properly, in this order:**
140
+
141
+ 1. **Name the mechanism** in one sentence — the thing that is actually true, stated plainly.
142
+ 2. **Show the code.** Quote the two or three real lines from the diff that make it true. They are looking at a file they have never read; the lines are the whole lesson.
143
+ 3. **Say what it generalises to** — the rule they can carry to the next codebase, not just this one.
144
+ 4. **One-line takeaway.** What to remember if they forget everything else.
145
+
146
+ Six to ten sentences. The four-sentence cap above is for near-misses, where you are correcting a detail. Here there is no detail to correct: the topic *is* the gap.
122
147
 
123
- If they get it wrong, do not immediately give the answer. Ask one narrower question that isolates the gap. If they miss that too, then explain.
148
+ **Then record and move on.** `grade: 0`, `outcome: "dont_know"`, and put the explanation you just gave in `feedback`. Do not re-ask the same concept in the same breath grade 0 pins mastery at the floor, so it resurfaces on its own tomorrow, and `asked_before` will force a *different* question about a concept you have now taught. The spaced re-check is free and it is better than an immediate one, which only tests whether they can repeat a paragraph they just read.
149
+
150
+ **Never offer to stop because they are blanking.** Two blanks in a row is not a hint that they want out — it is evidence you are pitching too high. Drop a tier and keep going. Tier-1 recall on something you have just explained is fair, and it rebuilds footing. If they want to stop, they will say so; wait to be told.
151
+
152
+ **Never dump the remaining answers as a list.** If the quiz ends early, it ends. A wall of four explanations at the door is not teaching, it is a receipt.
153
+
154
+ **When `already_taught` is true** on a plan item, they blanked on this before and you explained it. Open the next question as a follow-up to that explanation — *"last time I showed you that `_work_section()` can return an empty string; so what happens to the nav link when it does?"* — not as a first encounter. Building on a lesson is what makes it stick; asking cold throws it away.
124
155
 
125
156
  ## Growing the graph
126
157
 
127
- When work touches a concept with no slug and no near match, call `upsert_concepts` with a sensible `domain`, a `tier`, and at least one `prerequisite_of` edge to something that already exists. An isolated node teaches nothing about what to learn next.
158
+ `log_session_concepts` creates any slug it does not recognise, which is what keeps logging cheap but it creates it bare: tier 2, domain `general`, no edges. That is a placeholder, not a concept.
159
+
160
+ **When the response reports `created` slugs, follow it with `upsert_concepts`** giving each one a real `domain`, an honest `tier`, and at least one `prerequisite_of` edge to something that already exists. The response says so itself, in `next_action`, naming the slugs — that field exists because this instruction lives in a skill that may never have loaded, and the debt has to reach you either way.
161
+
162
+ This is not tidiness. `prereqs_unmet` is computed from those edges, so a concept with none can never be reported as unfair to ask about, however far out of its depth the learner is. Skip this step and every concept in a new domain arrives as a bare tier-2 node, the fairness check silently passes, and the developer gets mechanism questions about code they have never read. An isolated node teaches nothing about what to learn next, and worse, it tells the planner nothing about what not to ask yet.
163
+
164
+ ## Focus
165
+
166
+ Eklavya has two dials and they are not the same thing. **Mode** is how hard to push. **Focus** is what to teach. Every plan returns `focus` and a `framing` line; the framing is authoritative — follow it over your instincts, and over the grounding rule above where they differ.
167
+
168
+ ### project (the default)
169
+
170
+ What the sections above describe. The code is the subject. Name the file, the line, the decision.
171
+
172
+ ### concept
173
+
174
+ The same subject matter, asked so the answer transfers to a different codebase. **This does not mean textbook questions.** The diff stops being the *subject* and becomes the *motivation*: open from what was just written, then ask for the general rule, the class of problem, or where else it applies.
175
+
176
+ - Right: *"We gave the profile cache a 60s TTL in `profile.ts`. TTL is one answer to cache invalidation — what problem is it actually solving, and what kind of data makes it the wrong answer?"*
177
+ - Wrong, because it is `project` focus wearing a hat: *"Why did we pick 60s rather than 30s here?"* — a fine question, but the answer is about this file and dies with it.
178
+ - Also wrong, and the failure this focus invites: *"What is a TTL?"* That is tier-1 recall. Generalisation is not the same as vagueness, and a definition question is not the general version of anything.
179
+
180
+ The test: **could a correct answer be reused on a different project?** If not, you have written a `project` question. Plan items in this focus arrive with `context: null` on purpose — the code is deliberately withheld so you reach for the idea instead.
181
+
182
+ Items with `reason: "concept_widening"` are prerequisites and domain siblings the task did not touch directly. They are the ideas the diff is an instance of. Ask about them on their own terms.
183
+
184
+ ### learn
185
+
186
+ The developer named a topic. Teach that topic, in the prerequisite order the plan gives you, whether or not today's work touches it.
187
+
188
+ - When an item carries **`bridge_context`**, the session's work *did* touch that concept, and that string is the real code. Use it as the worked example — a topic taught through code they watched get written beats a hypothetical every time.
189
+ - When it does not, teach it on its own terms. **Do not force a link to unrelated work.** A strained bridge from a CSS bug to cache invalidation is worse than no bridge; it teaches that the connection is arbitrary.
190
+
191
+ `reason: "no_topic"` means the focus is `learn` but nothing was set — ask what they want to learn and set it before quizzing. `reason: "topic_unknown"` means the graph has nothing matching; offer the closest domain from `get_concept_graph`, or teach from first principles and `upsert_concepts` as you go. Do not invent questions about concepts that do not exist.
192
+
193
+ **Focus never changes when you interrupt.** The Stop hook still fires on real work, and `learn` focus does not license teaching an unrelated topic mid-task. Topic study on demand is `/eklavya:learn`, which the developer asks for.
128
194
 
129
195
  ## Mode
130
196
 
131
- - **ambient** — offer. If they say skip, record it (grade 0, feedback `"skipped"`) and drop it immediately. Do not ask twice. Do not guilt them.
197
+ - **ambient** — offer. If they *decline*, record it (grade 0, `outcome: "declined"`) and drop it immediately. Do not ask twice. Do not guilt them. A decline is not the same as "I don't know" — see below.
132
198
  - **enforced** — the quiz is required before committing. Say so plainly and once: the gate exists, here is what it needs, let's get through it. Supportive, not punitive. Never imply they are being punished.
199
+
200
+ A blank grades 0, and 0 never passes the gate — so a session answered entirely with "I don't know" would leave nothing to ask and a commit that can never go through. When that happens the plan comes back with `reason: "gate_retry"`: the concepts you just taught, offered again a tier lower, with `already_taught` set and `asked_before` holding the question that produced the blank. **This is a second lap, not a re-ask.** Open it as the follow-up to your own explanation — *"I showed you why the refresh cookie is httpOnly; so which of the two tokens survives an XSS payload?"* — and ask something the first question did not. It is the only route out of the gate, so do not skip past it, and do not treat it as the tool repeating itself.
201
+
202
+ A concept they explicitly **declined** is not offered again. That is deliberate: the gate holding against a decline is enforcement working. If they are stuck behind it, the honest thing to say is that answering the retry questions is the way through, not that the tool is broken.
133
203
  - **off** — do nothing at all.
134
204
 
135
205
  ## The bar
package/dist/cli.js CHANGED
@@ -17,6 +17,8 @@ Usage:
17
17
  eklavya export-rules [--out <file>] Write the tutor pedagogy as a Cursor rules file
18
18
  eklavya config get Show the effective configuration
19
19
  eklavya config set <key> <value> Change a setting (add --repo to scope it to this repo)
20
+ e.g. mode ambient|enforced|off, focus project|concept|learn
21
+ add --topic <topic> when setting focus to "learn"
20
22
  eklavya doctor Check that everything is wired up
21
23
  eklavya db-path Print the database location
22
24
 
@@ -76,11 +78,31 @@ function configCommand(args) {
76
78
  if (!(key in DEFAULT_CONFIG)) {
77
79
  fail(`Unknown setting "${key}". Known: ${Object.keys(DEFAULT_CONFIG).join(', ')}`);
78
80
  }
81
+ // `focus learn` is useless without a topic, so let one call say both rather
82
+ // than leaving a state that the planner will only refuse later.
83
+ const topicIndex = args.indexOf('--topic');
84
+ const topic = topicIndex === -1 ? undefined : args[topicIndex + 1];
85
+ if (topicIndex !== -1 && !topic)
86
+ fail('--topic needs a value.');
87
+ if (topic !== undefined && key !== 'focus' && key !== 'focus_topic') {
88
+ fail('--topic only applies when setting focus.');
89
+ }
90
+ if (key === 'focus' && value === 'learn' && topic === undefined) {
91
+ fail('focus "learn" needs a topic: eklavya config set focus learn --topic <topic>');
92
+ }
93
+ // A topic is always a string. Number-parsing it would turn a topic like "html5"
94
+ // -- or worse, "2" -- into something `coerce` then silently drops.
95
+ const isTopicKey = key === 'focus_topic';
79
96
  let parsed = value;
80
- if (value === 'true' || value === 'false')
97
+ if (isTopicKey)
98
+ parsed = value;
99
+ else if (value === 'true' || value === 'false')
81
100
  parsed = value === 'true';
82
101
  else if (value !== '' && !Number.isNaN(Number(value)))
83
102
  parsed = Number(value);
103
+ const patch = { [key]: parsed };
104
+ if (topic !== undefined && key === 'focus')
105
+ patch.focus_topic = topic;
84
106
  let target;
85
107
  if (scopeRepo) {
86
108
  if (!resolved.repoRoot)
@@ -90,8 +112,10 @@ function configCommand(args) {
90
112
  else {
91
113
  target = resolved.globalPath;
92
114
  }
93
- writeConfigFile(target, { [key]: parsed });
94
- process.stdout.write(`${key} = ${JSON.stringify(parsed)} -> ${target}\n`);
115
+ writeConfigFile(target, patch);
116
+ for (const [k, v] of Object.entries(patch)) {
117
+ process.stdout.write(`${k} = ${JSON.stringify(v)} -> ${target}\n`);
118
+ }
95
119
  }
96
120
  function doctor() {
97
121
  const file = dbPath();
@@ -115,7 +139,12 @@ function doctor() {
115
139
  lines.push(`database error: ${err instanceof Error ? err.message : String(err)}`);
116
140
  }
117
141
  const resolved = loadConfig();
118
- lines.push(`mode: ${resolved.config.mode}${resolved.repoPath ? ' (from this repo)' : ''}`);
142
+ const fromRepo = resolved.repoPath ? ' (from this repo)' : '';
143
+ lines.push(`mode: ${resolved.config.mode}${fromRepo}`);
144
+ lines.push(`focus: ${resolved.config.focus}${resolved.config.focus === 'learn' ? ` (${resolved.config.focus_topic ?? 'no topic set'})` : ''}${fromRepo}`);
145
+ if (resolved.overrides.length > 0) {
146
+ lines.push(`overridden by repo: ${resolved.overrides.join(', ')}`);
147
+ }
119
148
  process.stdout.write(`${lines.join('\n')}\n`);
120
149
  if (!ok)
121
150
  process.exit(1);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5F,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,KAAK,GAAG;;;;;;;;;;;;CAYb,CAAC;AAEF,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,IAAc;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,gFAAgF,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;IAEtF,MAAM,KAAK,GAAG;;;;;;;;;;;EAWd,KAAK,CAAC,IAAI,EAAE;CACb,CAAC;IAEA,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG;YAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC3C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,aAAa,CAAC,IAAc;IACnC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAE9B,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACtE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,QAAQ,CAAC,UAAU,IAAI,CAAC,CAAC;QAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,QAAQ,CAAC,QAAQ,IAAI,QAAQ,IAAI,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IAED,IAAI,MAAM,KAAK,KAAK;QAAE,IAAI,CAAC,0BAA0B,MAAM,IAAI,CAAC,CAAC;IACjE,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,SAAS;QAAE,IAAI,CAAC,yCAAyC,CAAC,CAAC;IACjF,IAAI,CAAC,CAAC,GAAG,IAAI,cAAc,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,oBAAoB,GAAG,aAAa,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,MAAM,GAAY,KAAK,CAAC;IAC5B,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO;QAAE,MAAM,GAAG,KAAK,KAAK,MAAM,CAAC;SAChE,IAAI,KAAK,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE9E,IAAI,MAAc,CAAC;IACnB,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAAE,IAAI,CAAC,0EAA0E,CAAC,CAAC;QACzG,MAAM,GAAG,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC;IAC/B,CAAC;IAED,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,MAAM;IACb,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;IACtB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,EAAE,GAAG,IAAI,CAAC;IAEd,KAAK,CAAC,IAAI,CAAC,aAAa,WAAW,EAAE,EAAE,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,QAAQ,GAAI,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,EAAoB,CAAC,CAAC,CAAC;QAC1F,MAAM,QAAQ,GAAI,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,EAAoB,CAAC,CAAC,CAAC;QAC1F,MAAM,KAAK,GACT,EAAE,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAC,GAAG,EAClF,CAAC,CAAC,CAAC;QACJ,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/E,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,EAAE,GAAG,KAAK,CAAC;QACX,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,aAAa,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE/F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,IAAI;IACX,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,cAAc;YACjB,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC3B,KAAK,QAAQ;YACX,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,QAAQ;YACX,OAAO,MAAM,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC;YACtC,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO;QACT;YACE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -4,6 +4,8 @@ import path from 'node:path';
4
4
  import { globalConfigPath } from './paths.js';
5
5
  export const DEFAULT_CONFIG = {
6
6
  mode: 'ambient',
7
+ focus: 'project',
8
+ focus_topic: null,
7
9
  pass_threshold: 0.7,
8
10
  max_questions_per_task: 4,
9
11
  min_minutes_between_quizzes: 20,
@@ -66,6 +68,14 @@ function coerce(raw, base) {
66
68
  const out = { ...base };
67
69
  if (raw.mode === 'ambient' || raw.mode === 'enforced' || raw.mode === 'off')
68
70
  out.mode = raw.mode;
71
+ if (raw.focus === 'project' || raw.focus === 'concept' || raw.focus === 'learn')
72
+ out.focus = raw.focus;
73
+ if (typeof raw.focus_topic === 'string' && raw.focus_topic.trim()) {
74
+ out.focus_topic = raw.focus_topic.trim();
75
+ }
76
+ else if (raw.focus_topic === null) {
77
+ out.focus_topic = null;
78
+ }
69
79
  if (typeof raw.pass_threshold === 'number' && raw.pass_threshold >= 0 && raw.pass_threshold <= 1) {
70
80
  out.pass_threshold = raw.pass_threshold;
71
81
  }
@@ -97,12 +107,14 @@ export function loadConfig(cwd = process.cwd()) {
97
107
  const globalRaw = readJson(globalPath) ?? {};
98
108
  const repoRaw = repoPath ? (readJson(repoPath) ?? {}) : {};
99
109
  const raw = { ...globalRaw, ...repoRaw };
110
+ const overrides = Object.keys(repoRaw).filter((key) => key in globalRaw && JSON.stringify(globalRaw[key]) !== JSON.stringify(repoRaw[key]));
100
111
  return {
101
112
  config: coerce(raw, DEFAULT_CONFIG),
102
113
  raw,
103
114
  globalPath,
104
115
  repoPath,
105
116
  repoRoot,
117
+ overrides,
106
118
  };
107
119
  }
108
120
  /** Writes via temp file + rename: the git hook may be reading mid-write. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAoC9C,MAAM,CAAC,MAAM,cAAc,GAAkB;IAC3C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,GAAG;IACnB,sBAAsB,EAAE,CAAC;IACzB,2BAA2B,EAAE,EAAE;IAC/B,eAAe,EAAE,CAAC,GAAG,CAAC;IACtB,KAAK,EAAE,KAAK;IACZ,4BAA4B,EAAE,CAAC;IAC/B,2BAA2B,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC;AAoBhD,SAAS,QAAQ,CAAC,CAAS;IACzB,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;QAC3C,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACnE,CAAC,CAAE,MAAkC;YACrC,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAIxD,+EAA+E;IAC/E,8EAA8E;IAC9E,uEAAuE;IACvE,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IAEnC,SAAS,CAAC;QACR,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC;YACjE,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YACvD,QAAQ,GAAG,GAAG,CAAC;YACf,8EAA8E;YAC9E,MAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,OAAO,EAAE;YAAE,MAAM;QAClD,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,MAAM,CAAC,GAA4B,EAAE,IAAmB;IAC/D,MAAM,GAAG,GAAkB,EAAE,GAAG,IAAI,EAAE,CAAC;IAEvC,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;QAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACjG,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,OAAO;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACvG,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QAClE,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;SAAM,IAAI,GAAG,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QACpC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,QAAQ,IAAI,GAAG,CAAC,cAAc,IAAI,CAAC,IAAI,GAAG,CAAC,cAAc,IAAI,CAAC,EAAE,CAAC;QACjG,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,cAAc,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,sBAAsB,KAAK,QAAQ,IAAI,GAAG,CAAC,sBAAsB,GAAG,CAAC,EAAE,CAAC;QACrF,GAAG,CAAC,sBAAsB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,2BAA2B,KAAK,QAAQ,IAAI,GAAG,CAAC,2BAA2B,IAAI,CAAC,EAAE,CAAC;QAChG,GAAG,CAAC,2BAA2B,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QAClG,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,eAA2B,CAAC;IACxD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IAC1D,IACE,OAAO,GAAG,CAAC,4BAA4B,KAAK,QAAQ;QACpD,GAAG,CAAC,4BAA4B,IAAI,CAAC,EACrC,CAAC;QACD,GAAG,CAAC,4BAA4B,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAClF,CAAC;IACD,IACE,OAAO,GAAG,CAAC,2BAA2B,KAAK,QAAQ;QACnD,GAAG,CAAC,2BAA2B,IAAI,CAAC,EACpC,CAAC;QACD,GAAG,CAAC,2BAA2B,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACpD,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAEnD,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,GAAG,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,OAAO,EAAE,CAAC;IAEzC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAC3C,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,IAAI,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CACtF,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC;QACnC,GAAG;QACH,UAAU;QACV,QAAQ;QACR,QAAQ;QACR,SAAS;KACV,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAA8B;IAC1E,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,CAAC;IAEzC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,IAAI,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAqB,EAAE,MAAc;IACnE,OAAO,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,15 @@
1
+ -- Grade 0 was carrying two opposite meanings: "I don't know, teach me" and
2
+ -- "not now, leave me alone". The tutor skill collapsed them into one row of its
3
+ -- grading table, so a learner asking to be taught was dropped exactly like one
4
+ -- asking to be left alone (observed: four blanks in one session, nothing
5
+ -- taught). SM-2 wants both at 0 -- neither is a recall success -- so the
6
+ -- distinction cannot live in `grade`.
7
+ --
8
+ -- It has to be recorded rather than inferred, because the useful question comes
9
+ -- a day later: "did I already teach this, or has it never been explained?"
10
+ -- `feedback` text cannot answer that reliably.
11
+ --
12
+ -- NULL means "recorded before this column existed"; readers must treat it as
13
+ -- unknown, not as any particular outcome.
14
+ ALTER TABLE attempts ADD COLUMN outcome TEXT
15
+ CHECK (outcome IN ('answered','dont_know','declined'));
@@ -0,0 +1,24 @@
1
+ -- `required` is derived from the concepts the session's *work* touched, but
2
+ -- `passed` was satisfiable by any concept in `session_concepts` -- and
3
+ -- `record_attempt` inserts a row there for anything it is asked about, so that
4
+ -- review debt is not free. The two ends never matched: answering concepts the
5
+ -- planner pulled in from elsewhere raised `passedCount` while leaving `required`
6
+ -- frozen, so a gate whose bar was set by today's diff could be cleared without
7
+ -- answering a single question about today's diff.
8
+ --
9
+ -- Focus modes make that the main path rather than a trickle. `concept` focus
10
+ -- widens selection to domain siblings and prerequisites on purpose, and `learn`
11
+ -- focus selects from a topic that may have nothing to do with the session at
12
+ -- all.
13
+ --
14
+ -- So the origin has to be recorded. 'work' is a concept the implementer logged
15
+ -- as part of the task; 'review' is one the tutor pulled in. Both still record
16
+ -- attempts, update mastery and count as `answered` -- review debt stays real --
17
+ -- but only 'work' can satisfy the bar the work itself set.
18
+ --
19
+ -- NULL means "recorded before this column existed". Those rows are read as
20
+ -- 'work', which is what they were in every case that mattered: before focus
21
+ -- modes there was nothing to widen with except same-domain review, and treating
22
+ -- them as 'review' would retroactively unpass gates that have already passed.
23
+ ALTER TABLE session_concepts ADD COLUMN origin TEXT
24
+ CHECK (origin IN ('work','review'));
package/dist/store.js CHANGED
@@ -43,8 +43,8 @@ export function lastAttempt(db, conceptId) {
43
43
  .get(conceptId);
44
44
  }
45
45
  export function recordAttemptRow(db, a) {
46
- db.prepare(`INSERT INTO attempts (concept_id, session_id, question, answer, grade, difficulty, feedback)
47
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(a.conceptId, a.sessionId, a.question, a.answer, a.grade, a.difficulty, a.feedback);
46
+ db.prepare(`INSERT INTO attempts (concept_id, session_id, question, answer, grade, difficulty, feedback, outcome)
47
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(a.conceptId, a.sessionId, a.question, a.answer, a.grade, a.difficulty, a.feedback, a.outcome);
48
48
  }
49
49
  /** Applies one grade end to end: attempt row, then recomputed SM-2 state. */
50
50
  export function gradeConcept(db, input) {
@@ -55,19 +55,32 @@ export function gradeConcept(db, input) {
55
55
  writeMastery(db, input.conceptId, after);
56
56
  return after;
57
57
  }
58
- export function logSessionConcept(db, sessionId, conceptId, context) {
59
- db.prepare(`INSERT INTO session_concepts (session_id, concept_id, context)
60
- VALUES (?, ?, ?)
58
+ export function logSessionConcept(db, sessionId, conceptId, context, origin = 'work') {
59
+ db.prepare(`INSERT INTO session_concepts (session_id, concept_id, context, origin)
60
+ VALUES (?, ?, ?, ?)
61
61
  ON CONFLICT(session_id, concept_id) DO UPDATE SET
62
- context = COALESCE(excluded.context, session_concepts.context)`).run(sessionId, conceptId, context);
62
+ context = COALESCE(excluded.context, session_concepts.context),
63
+ -- Work wins and never degrades: a concept quizzed as review debt and
64
+ -- then genuinely touched by the task is part of the task.
65
+ origin = CASE
66
+ WHEN excluded.origin = 'work' THEN 'work'
67
+ ELSE COALESCE(session_concepts.origin, excluded.origin)
68
+ END`).run(sessionId, conceptId, context, origin);
63
69
  }
64
- export function sessionConcepts(db, sessionId) {
70
+ /**
71
+ * `origin: 'work'` restricts this to what the task actually touched, excluding
72
+ * concepts a quiz pulled in as review debt. The gate's bar must be computed from
73
+ * that subset: `passedCount` only counts 'work', so letting review rows raise
74
+ * `required` would raise a bar that nothing the learner does can clear.
75
+ */
76
+ export function sessionConcepts(db, sessionId, origin) {
65
77
  return db
66
78
  .prepare(`SELECT c.*, sc.context, sc.ts AS logged_at
67
79
  FROM session_concepts sc JOIN concepts c ON c.id = sc.concept_id
68
80
  WHERE sc.session_id = ?
81
+ ${origin ? `AND COALESCE(sc.origin, 'work') = ?` : ''}
69
82
  ORDER BY sc.ts ASC, c.tier ASC, c.slug ASC`)
70
- .all(sessionId);
83
+ .all(...(origin ? [sessionId, origin] : [sessionId]));
71
84
  }
72
85
  export function newConceptsThisSession(db, sessionId) {
73
86
  return db
@@ -88,9 +101,20 @@ export function gateRow(db, sessionId) {
88
101
  }
89
102
  function countAnswered(db, sessionId) {
90
103
  const row = db
91
- .prepare(`SELECT
104
+ .prepare(
105
+ // `answered` counts everything -- quizzing on review debt is not free.
106
+ // `passedCount` counts only 'work', because `required` is derived from the
107
+ // concepts the work touched and the two ends have to measure the same
108
+ // thing. Without this, `concept` focus widening to domain siblings, or
109
+ // `learn` focus selecting from an unrelated topic, would let a gate whose
110
+ // bar was set by today's diff be cleared without a single question about
111
+ // today's diff. NULL is pre-migration and reads as 'work' (migration 005).
112
+ `SELECT
92
113
  count(DISTINCT a.concept_id) AS answered,
93
- count(DISTINCT CASE WHEN a.grade >= ? THEN a.concept_id END) AS passedCount
114
+ count(DISTINCT CASE
115
+ WHEN a.grade >= ? AND COALESCE(sc.origin, 'work') = 'work'
116
+ THEN a.concept_id
117
+ END) AS passedCount
94
118
  FROM attempts a
95
119
  JOIN session_concepts sc
96
120
  ON sc.concept_id = a.concept_id AND sc.session_id = a.session_id
@@ -121,11 +145,12 @@ export function syncGate(db, sessionId, config, opts = {}) {
121
145
  }
122
146
  /** The last few questions actually asked about a concept, newest first. */
123
147
  export function recentQuestions(db, conceptId, limit = 3) {
124
- return db
125
- .prepare(`SELECT question, difficulty AS tier, grade FROM attempts
148
+ const rows = db
149
+ .prepare(`SELECT question, difficulty AS tier, grade, outcome FROM attempts
126
150
  WHERE concept_id = ? AND question <> ''
127
151
  ORDER BY id DESC LIMIT ?`)
128
152
  .all(conceptId, limit);
153
+ return rows.map((r) => ({ ...r, taught: r.outcome === 'dont_know' }));
129
154
  }
130
155
  /** Loose match: whitespace and punctuation differences are still the same question. */
131
156
  export function questionFingerprint(question) {
@@ -140,6 +165,124 @@ export function hasAskedQuestion(db, conceptId, question) {
140
165
  .all(conceptId);
141
166
  return rows.some((r) => questionFingerprint(r.question) === target);
142
167
  }
168
+ /**
169
+ * Has this concept ever been blanked on and taught?
170
+ *
171
+ * Deliberately not derived from `recentQuestions`, which is windowed: once a few
172
+ * newer attempts pile up, the `dont_know` row falls out of that window and the
173
+ * flag flips back to false. The concepts that accumulate attempts fastest are
174
+ * the unmastered ones that keep resurfacing -- exactly the ones most likely to
175
+ * have been blanked on -- so deriving it there loses the flag precisely where it
176
+ * matters most, and the next question opens cold on a topic already explained.
177
+ */
178
+ export function wasEverTaught(db, conceptId) {
179
+ const row = db
180
+ .prepare(`SELECT 1 FROM attempts WHERE concept_id = ? AND outcome = 'dont_know' LIMIT 1`)
181
+ .get(conceptId);
182
+ return row !== undefined;
183
+ }
184
+ /**
185
+ * Concepts this session attempted but did not pass, minus the ones the learner
186
+ * declined.
187
+ *
188
+ * This is the way out of an otherwise unreachable gate. A blank grades 0, and a
189
+ * concept with any attempt is filtered from the session plan, so a session
190
+ * answered entirely with "I don't know" leaves `required` permanently unmet: the
191
+ * planner returns nothing, and the commit gate denies forever. Offering those
192
+ * concepts again -- once everything else is exhausted, a tier lower, and with
193
+ * `asked_before` forcing a different question -- turns the deadlock into a
194
+ * second lap over material that has now been taught.
195
+ *
196
+ * Declines are excluded on purpose, judged on the *latest* attempt so someone
197
+ * who declined and later engaged is not held to the earlier answer. "Leave me
198
+ * alone" is a choice, and enforced mode holding the gate against it is the
199
+ * enforcement working, not a deadlock.
200
+ */
201
+ export function gateRetryConcepts(db, sessionId) {
202
+ return db
203
+ .prepare(
204
+ // Carries `context` so a retry question stays grounded in the same code
205
+ // the first one was about -- the concept was taught, not the file.
206
+ `SELECT c.*, sc.context, sc.ts AS logged_at FROM concepts c
207
+ JOIN session_concepts sc ON sc.concept_id = c.id AND sc.session_id = ?
208
+ JOIN (
209
+ SELECT a.concept_id,
210
+ max(a.grade) AS best_grade,
211
+ (SELECT x.outcome FROM attempts x
212
+ WHERE x.session_id = a.session_id AND x.concept_id = a.concept_id
213
+ ORDER BY x.id DESC LIMIT 1) AS last_outcome
214
+ FROM attempts a
215
+ WHERE a.session_id = ?
216
+ GROUP BY a.concept_id
217
+ ) t ON t.concept_id = c.id
218
+ WHERE t.best_grade < ?
219
+ AND (t.last_outcome IS NULL OR t.last_outcome <> 'declined')
220
+ ORDER BY sc.ts ASC`)
221
+ .all(sessionId, sessionId, PASSING_GRADE);
222
+ }
223
+ /**
224
+ * Concepts in the same domains as the session's work, for `concept` focus.
225
+ *
226
+ * `project` focus asks about the diff; `concept` focus asks about the ideas the
227
+ * diff is an instance of, which means reaching past what the task happened to
228
+ * touch. Prerequisites of touched concepts come too: they are the part of "the
229
+ * general version" a learner is most likely to be missing.
230
+ */
231
+ export function domainSiblings(db, domains, exclude) {
232
+ if (domains.length === 0)
233
+ return [];
234
+ const rows = db
235
+ .prepare(`SELECT c.* FROM concepts c
236
+ WHERE c.domain IN (${domains.map(() => '?').join(',')})
237
+ ORDER BY c.tier ASC, c.slug ASC`)
238
+ .all(...domains);
239
+ return rows.filter((c) => !exclude.has(c.id));
240
+ }
241
+ /** Prerequisites of the given concepts, nearest first. Used to widen `concept` focus. */
242
+ export function prereqsOf(db, conceptIds) {
243
+ if (conceptIds.length === 0)
244
+ return [];
245
+ return db
246
+ .prepare(`SELECT DISTINCT c.* FROM concepts c
247
+ JOIN edges e ON e.from_concept = c.id
248
+ WHERE e.relation = 'prerequisite_of'
249
+ AND e.to_concept IN (${conceptIds.map(() => '?').join(',')})
250
+ ORDER BY c.tier ASC, c.slug ASC`)
251
+ .all(...conceptIds);
252
+ }
253
+ /**
254
+ * Resolve a free-text topic ("caching", "web auth") onto the graph, for `learn`
255
+ * focus.
256
+ *
257
+ * Tried in order of confidence: an exact domain, then a domain whose name the
258
+ * topic contains or is contained by, then concepts matching on slug or name.
259
+ * Returning both a domain and slugs lets the caller prefer the domain when the
260
+ * topic names one and fall back to loose concept matches when it does not.
261
+ *
262
+ * An empty result is meaningful and must not be papered over -- it means the
263
+ * graph has nothing on this topic yet, and the honest answer is to say so and
264
+ * offer to teach from first principles rather than to invent questions.
265
+ */
266
+ export function resolveTopic(db, topic) {
267
+ const needle = topic.trim().toLowerCase();
268
+ if (!needle)
269
+ return { domain: null, slugs: [] };
270
+ const slugged = needle.replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
271
+ const domains = db.prepare('SELECT DISTINCT domain FROM concepts').all().map((r) => r.domain);
272
+ const exact = domains.find((d) => d.toLowerCase() === needle || d.toLowerCase() === slugged);
273
+ const loose = exact ??
274
+ domains.find((d) => {
275
+ const l = d.toLowerCase();
276
+ return l.includes(slugged) || slugged.includes(l);
277
+ });
278
+ const rows = db
279
+ .prepare(`SELECT slug FROM concepts
280
+ WHERE slug LIKE ? OR lower(name) LIKE ?
281
+ ORDER BY tier ASC, slug ASC
282
+ LIMIT 40`)
283
+ .all(`%${slugged}%`, `%${needle}%`);
284
+ return { domain: loose ?? null, slugs: rows.map((r) => r.slug) };
285
+ }
143
286
  /** Concepts already asked about in this session — they have had their turn. */
144
287
  export function attemptedConceptIds(db, sessionId) {
145
288
  const rows = db
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,cAAc,EACd,OAAO,EAEP,YAAY,GACb,MAAM,UAAU,CAAC;AAkBlB,MAAM,UAAU,aAAa,CAAC,EAAM,EAAE,IAAY;IAChD,OAAO,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,CAAC,IAAI,CAA2B,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAM;IACpC,OAAO,EAAE,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC,GAAG,EAI3D,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,EAAM,EACN,CAA6G;IAE7G,EAAE,CAAC,OAAO,CACR;+BAC2B,CAC5B,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;IAClF,OAAO,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAM,EAAE,SAAiB;IAClD,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,SAAS,CAErE,CAAC;IACd,IAAI,CAAC,GAAG;QAAE,OAAO,cAAc,EAAE,CAAC;IAClC,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,WAAW,EAAE,GAAG,CAAC,WAAW;KAC7B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,SAAiB,EAAE,KAAmB;IACzE,EAAE,CAAC,OAAO,CACR;;;;gGAI4F,CAC7F,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,SAAS,CAAC,EAAM,EAAE,SAAiB,EAAE,KAAK,GAAG,YAAY;IACvE,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,0EAA0E,CAAC;SACnF,GAAG,CAAC,SAAS,EAAE,KAAK,CAAwB,CAAC;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,EAAM,EACN,SAAiB;IAEjB,OAAO,EAAE;SACN,OAAO,CAAC,0FAA0F,CAAC;SACnG,GAAG,CAAC,SAAS,CAAkE,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,EAAM,EACN,CASC;IAED,EAAE,CAAC,OAAO,CACR;qCACiC,CAClC,CAAC,GAAG,CACH,CAAC,CAAC,SAAS,EACX,CAAC,CAAC,SAAS,EACX,CAAC,CAAC,QAAQ,EACV,CAAC,CAAC,MAAM,EACR,CAAC,CAAC,KAAK,EACP,CAAC,CAAC,UAAU,EACZ,CAAC,CAAC,QAAQ,EACV,CAAC,CAAC,OAAO,CACV,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAC1B,EAAM,EACN,KAUC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACxF,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC;AASD,MAAM,UAAU,iBAAiB,CAC/B,EAAM,EACN,SAAiB,EACjB,SAAiB,EACjB,OAAsB,EACtB,SAAwB,MAAM;IAE9B,EAAE,CAAC,OAAO,CACR;;;;;;;;;oBASgB,CACjB,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,EAAM,EACN,SAAiB,EACjB,MAAsB;IAEtB,OAAO,EAAE;SACN,OAAO,CACN;;;WAGK,MAAM,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,EAAE;kDACZ,CAC7C;SACA,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAwB,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EAAM,EAAE,SAAiB;IAC9D,OACE,EAAE;SACC,OAAO,CACN;;sDAE8C,CAC/C;SACA,GAAG,CAAC,SAAS,CACjB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAM,EAAE,SAAiB;IACrD,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,uEAAuE,CAAC;SAChF,GAAG,CAAC,SAAS,CAA+B,CAAC;IAChD,OAAO,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC;AACzB,CAAC;AAqBD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,OAAO,CAAC,EAAM,EAAE,SAAiB;IAC/C,OAAO,EAAE,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,GAAG,CAAC,SAAS,CAAwB,CAAC;AACtG,CAAC;AAED,SAAS,aAAa,CAAC,EAAM,EAAE,SAAiB;IAC9C,MAAM,GAAG,GAAG,EAAE;SACX,OAAO;IACN,uEAAuE;IACvE,2EAA2E;IAC3E,sEAAsE;IACtE,uEAAuE;IACvE,0EAA0E;IAC1E,yEAAyE;IACzE,2EAA2E;IAC3E;;;;;;;;;8BASwB,CACzB;SACA,GAAG,CAAC,aAAa,EAAE,SAAS,CAA8C,CAAC;IAC9E,OAAO,GAAG,CAAC;AACb,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,EAAM,EACN,SAAiB,EACjB,MAAqB,EACrB,OAAwD,EAAE;IAE1D,MAAM,QAAQ,GAAG,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACxC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAE/D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;IAC3E,+EAA+E;IAC/E,gEAAgE;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,MAAM,CAAC;IAE7D,EAAE,CAAC,OAAO,CACR;;;;;8DAK0D,CAC3D,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAExE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;AACxG,CAAC;AA+BD,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,EAAM,EAAE,SAAiB,EAAE,KAAK,GAAG,CAAC;IAClE,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;;iCAE2B,CAC5B;SACA,GAAG,CAAC,SAAS,EAAE,KAAK,CAAoC,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAM,EAAE,SAAiB,EAAE,QAAgB;IAC1E,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,8EAA8E,CAAC;SACvF,GAAG,CAAC,SAAS,CAA2B,CAAC;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,EAAM,EAAE,SAAiB;IACrD,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,+EAA+E,CAAC;SACxF,GAAG,CAAC,SAAS,CAAC,CAAC;IAClB,OAAO,GAAG,KAAK,SAAS,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAM,EAAE,SAAiB;IACzD,OAAO,EAAE;SACN,OAAO;IACN,wEAAwE;IACxE,mEAAmE;IACnE;;;;;;;;;;;;;;2BAcqB,CACtB;SACA,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,CAAwB,CAAC;AACrE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAM,EAAE,OAAiB,EAAE,OAAoB;IAC5E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;6BACuB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;wCACrB,CACnC;SACA,GAAG,CAAC,GAAG,OAAO,CAAiB,CAAC;IACnC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,EAAM,EAAE,UAAoB;IACpD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,EAAE;SACN,OAAO,CACN;;;iCAG2B,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;wCAC5B,CACnC;SACA,GAAG,CAAC,GAAG,UAAU,CAAiB,CAAC;AACxC,CAAC;AASD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,KAAa;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE3E,MAAM,OAAO,GACX,EAAE,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,GAAG,EACvD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,CAAC;IAC7F,MAAM,KAAK,GACT,KAAK;QACL,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;YAC1B,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IAEL,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;;;iBAGW,CACZ;SACA,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,IAAI,MAAM,GAAG,CAAuB,CAAC;IAE5D,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,mBAAmB,CAAC,EAAM,EAAE,SAAiB;IAC3D,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,+DAA+D,CAAC;SACxE,GAAG,CAAC,SAAS,CAA6B,CAAC;IAC9C,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,SAAiB,EAAE,GAAS;IAC/D,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;;;;;wCAKkC,CACnC;SACA,GAAG,CAAC,SAAS,CAA8F,CAAC;IAE/G,OAAO,IAAI;SACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;SACrG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
@@ -14,6 +14,9 @@ export const getConfig = {
14
14
  global_path: resolved.globalPath,
15
15
  repo_path: resolved.repoPath,
16
16
  repo_root: resolved.repoRoot,
17
+ // Which of the learner's own settings this repo is overriding. Say it
18
+ // rather than let a personal focus silently stop applying.
19
+ overridden_by_repo: resolved.overrides,
17
20
  };
18
21
  },
19
22
  };
@@ -24,7 +27,19 @@ export const setConfig = {
24
27
  inputSchema: {
25
28
  scope: z.enum(['global', 'repo']).optional().describe('Defaults to global.'),
26
29
  cwd: z.string().optional().describe(CWD_HINT),
27
- mode: z.enum(['ambient', 'enforced', 'off']).optional(),
30
+ mode: z
31
+ .enum(['ambient', 'enforced', 'off'])
32
+ .optional()
33
+ .describe('How hard Eklavya pushes: ambient offers, enforced gates commits, off is dormant.'),
34
+ focus: z
35
+ .enum(['project', 'concept', 'learn'])
36
+ .optional()
37
+ .describe('What Eklavya teaches, independent of mode. "project" quizzes the code just written; "concept" asks the transferable version of the same ideas; "learn" follows focus_topic. Defaults to project.'),
38
+ focus_topic: z
39
+ .string()
40
+ .nullable()
41
+ .optional()
42
+ .describe('The topic "learn" focus teaches, e.g. "caching". Pass null to clear it.'),
28
43
  pass_threshold: z.number().min(0).max(1).optional(),
29
44
  max_questions_per_task: z.number().int().min(1).max(10).optional(),
30
45
  min_minutes_between_quizzes: z.number().int().min(0).optional(),
@@ -40,6 +55,8 @@ export const setConfig = {
40
55
  const patch = {};
41
56
  for (const key of [
42
57
  'mode',
58
+ 'focus',
59
+ 'focus_topic',
43
60
  'pass_threshold',
44
61
  'max_questions_per_task',
45
62
  'min_minutes_between_quizzes',
@@ -1 +1 @@
1
- {"version":3,"file":"config_tools.js","sourceRoot":"","sources":["../../src/tools/config_tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config_tools.js","sourceRoot":"","sources":["../../src/tools/config_tools.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,17 @@ import { z } from 'zod';
2
2
  import { loadConfig } from '../config.js';
3
3
  import { decayedScore, isDue, isKnown, nextTierToAsk } from '../srs.js';
4
4
  import { resolveSessionId } from '../session.js';
5
- import { attemptedConceptIds, lastAttempt, lastAttemptAt, masteryFor, recentQuestions, sessionConcepts, unmetPrereqs, } from '../store.js';
5
+ import { attemptedConceptIds, domainSiblings, gateRetryConcepts, gateRow, prereqsOf, resolveTopic, lastAttempt, lastAttemptAt, masteryFor, recentQuestions, sessionConcepts, unmetPrereqs, wasEverTaught, } from '../store.js';
6
6
  import { CWD_HINT, SESSION_HINT } from './types.js';
7
+ /**
8
+ * What the tutor must do with the questions, stated per focus so the pedagogy
9
+ * cannot drift from the setting. Returned with every plan.
10
+ */
11
+ const FRAMING = {
12
+ project: 'Ground every question in the diff just written: name the file, the line, the decision. The code is the subject.',
13
+ concept: 'Ask the transferable version. The diff is the motivation, not the subject: open from what was just written, then ask for the general rule, the class of problem, or where else it applies. A correct answer must be usable on a different codebase. Do not ask for a definition -- that is tier 1 recall, not generalisation.',
14
+ learn: 'Teach the declared topic, in prerequisite order. Where an item carries bridge_context, the session touched that concept: use that real code as the worked example instead of a hypothetical. Where it does not, teach it on its own terms -- do not force a link to unrelated work.',
15
+ };
7
16
  const ASKED_HISTORY = 3;
8
17
  function minutesSince(iso, now) {
9
18
  if (!iso)
@@ -17,7 +26,7 @@ function minutesSince(iso, now) {
17
26
  export const getSessionQuizPlan = {
18
27
  name: 'get_session_quiz_plan',
19
28
  title: 'Get session quiz plan',
20
- description: 'What to quiz on right now and at what difficulty tier, chosen from this session\'s concepts and whatever is due for review. Pass a domain to plan a topic quiz instead. Every item carries asked_before (questions this learner has already been asked — never repeat one) and prereqs_unmet. Returns questions_needed: 0 when there is nothing worth asking.',
29
+ description: 'What to quiz on right now and at what difficulty tier, chosen from this session\'s concepts and whatever is due for review. Pass a domain to plan a topic quiz instead. Every item carries asked_before (questions this learner has already been asked — never repeat one), already_taught (they blanked and you explained it, so the next question is a follow-up) and prereqs_unmet. In enforced mode, once everything else is exhausted and the gate is still unpassed, it re-offers concepts that were blanked on and taught, a tier lower, with reason "gate_retry". Honours the configured focus: "project" plans from the diff, "concept" widens to prerequisites and domain siblings, "learn" plans from focus_topic and marks overlaps with the session\'s work as bridge_context. Every plan carries focus and framing — follow framing, it is what the setting means. Returns questions_needed: 0 when there is nothing worth asking.',
21
30
  inputSchema: {
22
31
  session_id: z.string().optional().describe(SESSION_HINT),
23
32
  cwd: z.string().optional().describe(CWD_HINT),
@@ -34,13 +43,45 @@ export const getSessionQuizPlan = {
34
43
  .boolean()
35
44
  .optional()
36
45
  .describe('Set true when the developer asked to be quizzed. The cadence limit exists to stop nagging, not to refuse a request.'),
46
+ focus: z
47
+ .enum(['project', 'concept', 'learn'])
48
+ .optional()
49
+ .describe('Override the configured focus for this one plan. Omit to use the config.'),
37
50
  },
38
51
  handler: (args, { db }) => {
39
52
  const now = new Date();
40
53
  const { config } = loadConfig(args.cwd);
41
54
  const sessionId = resolveSessionId(db, args.session_id);
42
55
  const max = args.max ?? config.max_questions_per_task;
43
- const topicMode = Boolean(args.domain || (args.slugs && args.slugs.length > 0));
56
+ const focus = args.focus ?? config.focus;
57
+ // `learn` focus turns the configured topic into the same domain/slug
58
+ // selection an explicit topic quiz uses, so it goes through one engine with
59
+ // one set of tier and repeat rules. An explicit domain or slugs still win:
60
+ // the developer naming a topic outranks a standing setting.
61
+ let effDomain = args.domain;
62
+ let effSlugs = args.slugs;
63
+ let topicUnresolved = false;
64
+ const explicitTopic = Boolean(args.domain || (args.slugs && args.slugs.length > 0));
65
+ if (!explicitTopic && focus === 'learn') {
66
+ if (!config.focus_topic) {
67
+ return {
68
+ session_id: sessionId,
69
+ questions_needed: 0,
70
+ concepts: [],
71
+ focus,
72
+ reason: 'no_topic',
73
+ detail: 'Focus is "learn" but no focus_topic is set. Ask what they want to learn, then set_config focus_topic.',
74
+ };
75
+ }
76
+ const match = resolveTopic(db, config.focus_topic);
77
+ if (match.domain)
78
+ effDomain = match.domain;
79
+ else if (match.slugs.length > 0)
80
+ effSlugs = match.slugs;
81
+ else
82
+ topicUnresolved = true;
83
+ }
84
+ const topicMode = Boolean(effDomain || (effSlugs && effSlugs.length > 0));
44
85
  if (config.mode === 'off') {
45
86
  return { session_id: sessionId, questions_needed: 0, concepts: [], reason: 'mode_off' };
46
87
  }
@@ -59,17 +100,41 @@ export const getSessionQuizPlan = {
59
100
  };
60
101
  }
61
102
  }
103
+ // The graph has nothing on this topic yet. Saying so is the useful answer;
104
+ // inventing questions about concepts that do not exist is not.
105
+ if (topicUnresolved) {
106
+ return {
107
+ session_id: sessionId,
108
+ questions_needed: 0,
109
+ concepts: [],
110
+ focus,
111
+ topic: config.focus_topic,
112
+ reason: 'topic_unknown',
113
+ detail: `Nothing in the graph matches "${config.focus_topic}". Offer the closest domain from get_concept_graph, or teach from first principles and upsert_concepts as you go.`,
114
+ };
115
+ }
62
116
  const picked = [];
63
117
  const seen = new Set();
118
+ // Where the session's own work touched a concept. In `learn` focus this is
119
+ // what turns an abstract topic question into one about code they just saw.
120
+ const sessionContext = new Map();
121
+ for (const c of sessionConcepts(db, sessionId)) {
122
+ if (c.context)
123
+ sessionContext.set(c.slug, c.context);
124
+ }
64
125
  const domains = new Set();
65
126
  // Concepts already answered in this session have had their turn. Re-offering
66
127
  // them is how a learner gets asked the same thing twice in five minutes.
67
128
  const alreadyAsked = attemptedConceptIds(db, sessionId);
68
129
  let skippedAsked = 0;
69
- const add = (concept, context, reason) => {
130
+ const add = (concept, context, reason,
131
+ // The gate-retry pass is the one caller allowed past the already-asked
132
+ // filter, and it drops a tier because it is re-opening a concept the
133
+ // learner has just been taught rather than testing a fresh one.
134
+ opts = {}) => {
70
135
  if (seen.has(concept.slug) || picked.length >= max)
71
136
  return;
72
- if (alreadyAsked.has(concept.id)) {
137
+ if (!opts.retry && alreadyAsked.has(concept.id)) {
73
138
  seen.add(concept.slug);
74
139
  skippedAsked += 1;
75
140
  return;
@@ -77,21 +142,30 @@ export const getSessionQuizPlan = {
77
142
  const m = masteryFor(db, concept.id);
78
143
  const score = decayedScore(m.score, m.next_review, now);
79
144
  const last = lastAttempt(db, concept.id);
145
+ const asked = recentQuestions(db, concept.id, ASKED_HISTORY);
146
+ const tier = nextTierToAsk({
147
+ conceptTier: concept.tier,
148
+ lastDifficulty: last?.difficulty ?? null,
149
+ lastGrade: last?.grade ?? null,
150
+ score,
151
+ });
80
152
  seen.add(concept.slug);
81
153
  picked.push({
82
154
  slug: concept.slug,
83
155
  name: concept.name,
84
156
  domain: concept.domain,
85
157
  description: concept.description,
86
- tier_to_ask: nextTierToAsk({
87
- conceptTier: concept.tier,
88
- lastDifficulty: last?.difficulty ?? null,
89
- lastGrade: last?.grade ?? null,
90
- score,
91
- }),
158
+ tier_to_ask: opts.retry ? Math.max(1, tier - 1) : tier,
92
159
  context,
160
+ // Only in `learn` focus, and only when the topic concept really did turn
161
+ // up in this session's work. Absent means "teach it on its own terms",
162
+ // which is different from "no context available".
163
+ ...(focus === 'learn' && sessionContext.has(concept.slug)
164
+ ? { bridge_context: sessionContext.get(concept.slug) }
165
+ : {}),
93
166
  last_grade: last?.grade ?? null,
94
- asked_before: recentQuestions(db, concept.id, ASKED_HISTORY),
167
+ asked_before: asked,
168
+ already_taught: wasEverTaught(db, concept.id),
95
169
  prereqs_unmet: unmetPrereqs(db, concept.id, now),
96
170
  reason,
97
171
  });
@@ -101,13 +175,13 @@ export const getSessionQuizPlan = {
101
175
  // topic gets the same tier escalation and the same repeat protection.
102
176
  const clauses = [];
103
177
  const params = [];
104
- if (args.domain) {
178
+ if (effDomain) {
105
179
  clauses.push('c.domain = ?');
106
- params.push(args.domain);
180
+ params.push(effDomain);
107
181
  }
108
- if (args.slugs && args.slugs.length > 0) {
109
- clauses.push(`c.slug IN (${args.slugs.map(() => '?').join(',')})`);
110
- params.push(...args.slugs);
182
+ if (effSlugs && effSlugs.length > 0) {
183
+ clauses.push(`c.slug IN (${effSlugs.map(() => '?').join(',')})`);
184
+ params.push(...effSlugs);
111
185
  }
112
186
  const rows = db
113
187
  .prepare(`SELECT c.* FROM concepts c
@@ -131,8 +205,13 @@ export const getSessionQuizPlan = {
131
205
  return a.score - b.score;
132
206
  return a.c.tier - b.c.tier;
133
207
  });
134
- for (const cand of candidates)
135
- add(cand.c, null, 'topic');
208
+ // `learn_topic` and `topic` run the same selection; they differ only in
209
+ // why the topic was chosen, which is what the tutor needs in order to
210
+ // frame the question.
211
+ const topicReason = !explicitTopic && focus === 'learn' ? 'learn_topic' : 'topic';
212
+ for (const cand of candidates) {
213
+ add(cand.c, sessionContext.get(cand.c.slug) ?? null, topicReason);
214
+ }
136
215
  }
137
216
  else {
138
217
  const touched = sessionConcepts(db, sessionId);
@@ -151,6 +230,35 @@ export const getSessionQuizPlan = {
151
230
  if (isDue(m.next_review, now))
152
231
  add(c, c.context, 'due_review');
153
232
  }
233
+ // `concept` focus: reach past what the task happened to touch. The ideas
234
+ // the diff is an instance of live in its prerequisites and its domain
235
+ // siblings, and asking only about what the diff contains is how a quiz
236
+ // stays stuck at "what does this line do".
237
+ //
238
+ // Deliberately before (c): a prerequisite of today's work is more use than
239
+ // an unrelated concept that happens to be due.
240
+ if (focus === 'concept' && picked.length < max) {
241
+ const touchedIds = touched.map((c) => c.id);
242
+ const exclude = new Set(touchedIds);
243
+ for (const c of prereqsOf(db, touchedIds)) {
244
+ const m = masteryFor(db, c.id);
245
+ if (!isKnown({ score: decayedScore(m.score, m.next_review, now), reps: m.reps })) {
246
+ // Null context on purpose. In concept focus the diff is the
247
+ // motivation, not the subject, and handing over a line of code
248
+ // invites exactly the grounded-in-the-file question this focus
249
+ // exists to avoid.
250
+ add(c, null, 'concept_widening');
251
+ }
252
+ }
253
+ for (const c of domainSiblings(db, [...domains], exclude)) {
254
+ if (picked.length >= max)
255
+ break;
256
+ const m = masteryFor(db, c.id);
257
+ if (!isKnown({ score: decayedScore(m.score, m.next_review, now), reps: m.reps })) {
258
+ add(c, null, 'concept_widening');
259
+ }
260
+ }
261
+ }
154
262
  // (c) anything else due in the same domains, so review debt gets paid down
155
263
  if (picked.length < max && domains.size > 0) {
156
264
  const placeholders = [...domains].map(() => '?').join(',');
@@ -164,6 +272,29 @@ export const getSessionQuizPlan = {
164
272
  add(c, null, 'domain_review');
165
273
  }
166
274
  }
275
+ // Enforced mode only, and only once everything else is exhausted: a session
276
+ // answered entirely with "I don't know" grades every concept 0, and a graded
277
+ // concept is filtered from the plan above -- so `required` can never be met,
278
+ // the planner returns nothing, and pre-tool-gate.sh denies the commit while
279
+ // telling the developer to run a quiz that has nothing left to ask. That is
280
+ // a dead end with no route out inside the session.
281
+ //
282
+ // Ambient mode is deliberately left alone. It has no gate to deadlock, and
283
+ // re-offering a concept there would be the nagging the cooldown exists to
284
+ // prevent.
285
+ if (!topicMode && picked.length === 0 && config.mode === 'enforced') {
286
+ const gate = gateRow(db, sessionId);
287
+ if (gate && gate.required > 0 && !gate.passed) {
288
+ // `seen` carries two meanings: "already picked" and "considered and
289
+ // rejected". Only the second is in it here -- nothing was picked, or we
290
+ // would not be in this branch -- so clearing it drops exactly the
291
+ // rejections the retry pass exists to reconsider, and cannot lose a pick.
292
+ seen.clear();
293
+ for (const c of gateRetryConcepts(db, sessionId)) {
294
+ add(c, c.context, 'gate_retry', { retry: true });
295
+ }
296
+ }
297
+ }
167
298
  // Foundations before what rests on them, so a quiz never leads with a
168
299
  // question the learner has no way to answer.
169
300
  picked.sort((a, b) => {
@@ -180,6 +311,7 @@ export const getSessionQuizPlan = {
180
311
  session_id: sessionId,
181
312
  questions_needed: 0,
182
313
  concepts: [],
314
+ focus,
183
315
  reason: 'already_covered',
184
316
  detail: `${skippedAsked} concept(s) were already asked about in this session.`,
185
317
  }
@@ -187,12 +319,18 @@ export const getSessionQuizPlan = {
187
319
  session_id: sessionId,
188
320
  questions_needed: 0,
189
321
  concepts: [],
322
+ focus,
190
323
  reason: topicMode ? 'no_candidates' : 'nothing_logged',
191
324
  };
192
325
  }
193
326
  return {
194
327
  session_id: sessionId,
195
328
  questions_needed: picked.length,
329
+ focus,
330
+ // Stated with every plan so the pedagogy cannot drift from the setting:
331
+ // the tutor should not have to remember what `concept` implies.
332
+ framing: FRAMING[focus],
333
+ ...(focus === 'learn' && config.focus_topic ? { topic: config.focus_topic } : {}),
196
334
  concepts: picked,
197
335
  };
198
336
  },
@@ -1 +1 @@
1
- {"version":3,"file":"get_session_quiz_plan.js","sourceRoot":"","sources":["../../src/tools/get_session_quiz_plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get_session_quiz_plan.js","sourceRoot":"","sources":["../../src/tools/get_session_quiz_plan.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ const DEFAULT_TIER = 2;
10
10
  export const logSessionConcepts = {
11
11
  name: 'log_session_concepts',
12
12
  title: 'Log session concepts',
13
- description: 'Record the concepts the current task genuinely exercises, each with a one-line context pointing at the real code you wrote. Call this while implementing, batched, 3-8 concepts per task. Unknown slugs are created automatically.',
13
+ description: 'Record the concepts the current task genuinely exercises, each with a one-line context pointing at the real code you wrote. Call this while implementing, batched, 3-8 concepts per task. Unknown slugs are created automatically, but bare — when the response carries next_action, do what it says before quizzing.',
14
14
  inputSchema: {
15
15
  session_id: z.string().optional().describe(SESSION_HINT),
16
16
  cwd: z.string().optional().describe(CWD_HINT),
@@ -62,14 +62,16 @@ export const logSessionConcepts = {
62
62
  created.push(concept.slug);
63
63
  budget -= 1;
64
64
  }
65
- logSessionConcept(db, sessionId, concept.id, input.context ?? null);
65
+ logSessionConcept(db, sessionId, concept.id, input.context ?? null, 'work');
66
66
  logged.push(concept.slug);
67
67
  }
68
68
  });
69
69
  apply();
70
70
  // The gate's bar rises with the work: how many touched concepts are still
71
71
  // unmastered, capped at the configured questions per task.
72
- const unmastered = sessionConcepts(db, sessionId).filter((c) => {
72
+ // 'work' only: `passedCount` counts nothing else, so a review-debt concept
73
+ // raising the bar would raise it past anything the learner could clear.
74
+ const unmastered = sessionConcepts(db, sessionId, 'work').filter((c) => {
73
75
  const m = masteryFor(db, c.id);
74
76
  return !isKnown({ score: decayedScore(m.score, m.next_review, now), reps: m.reps });
75
77
  }).length;
@@ -77,7 +79,25 @@ export const logSessionConcepts = {
77
79
  requiredHint: Math.min(unmastered, config.max_questions_per_task),
78
80
  repo: repoRoot,
79
81
  });
80
- return { session_id: sessionId, logged, created, matched, capped, gate };
82
+ // Said in the response, not only in the tutor skill. A bare concept is
83
+ // tier 2, domain "general", no edges -- and `prereqs_unmet` is computed from
84
+ // edges, so a concept with none can never be reported as unfair to ask
85
+ // about, however far out of its depth the learner is. The skill says to fix
86
+ // that, but it is model-invoked and may never load; the same gap is why the
87
+ // concept-logging directive had to move into the SessionStart banner. This
88
+ // lands in context whatever loaded.
89
+ const nextAction = created.length > 0
90
+ ? `Call upsert_concepts for ${created.join(', ')} — each was created bare (tier 2, domain "general", no edges). Give each a real domain, an honest tier, and at least one prerequisite_of edge, or prereqs_unmet stays empty and the fairness check silently passes.`
91
+ : undefined;
92
+ return {
93
+ session_id: sessionId,
94
+ logged,
95
+ created,
96
+ matched,
97
+ capped,
98
+ gate,
99
+ ...(nextAction ? { next_action: nextAction } : {}),
100
+ };
81
101
  },
82
102
  };
83
103
  //# sourceMappingURL=log_session_concepts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"log_session_concepts.js","sourceRoot":"","sources":["../../src/tools/log_session_concepts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"log_session_concepts.js","sourceRoot":"","sources":["../../src/tools/log_session_concepts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,sBAAsB,EACtB,eAAe,EACf,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAElE,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,CAAC,MAAM,kBAAkB,GAAY;IACzC,IAAI,EAAE,sBAAsB;IAC5B,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EACT,uTAAuT;IACzT,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;QAC7C,QAAQ,EAAE,CAAC;aACR,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;YACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;YAChE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC7B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;YAC/C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wFAAwF,CAAC;SAClI,CAAC,CACH;aACA,GAAG,CAAC,CAAC,CAAC;KACV;IACD,OAAO,EAAE,CACP,IAIC,EACD,EAAE,EAAE,EAAE,EACN,EAAE;QACF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,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;QAExD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,OAAO,GAA8C,EAAE,CAAC;QAC9D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,4BAA4B,GAAG,sBAAsB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;YAEtG,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACvC,IAAI,CAAC,IAAI;oBAAE,SAAS;gBAEpB,IAAI,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;gBAEtC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;oBACvD,IAAI,IAAI,EAAE,CAAC;wBACT,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;wBACvC,IAAI,OAAO;4BAAE,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;oBACzE,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;wBAChB,MAAM,IAAI,CAAC,CAAC;wBACZ,SAAS;oBACX,CAAC;oBACD,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE;wBAC1B,IAAI;wBACJ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;wBAC3C,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,cAAc;wBACtC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,YAAY;wBAChC,MAAM,EAAE,KAAK;qBACd,CAAC,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC3B,MAAM,IAAI,CAAC,CAAC;gBACd,CAAC;gBAED,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC5E,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,EAAE,CAAC;QAER,0EAA0E;QAC1E,2DAA2D;QAC3D,2EAA2E;QAC3E,wEAAwE;QACxE,MAAM,UAAU,GAAG,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACrE,MAAM,CAAC,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC,MAAM,CAAC;QAEV,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC3C,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,sBAAsB,CAAC;YACjE,IAAI,EAAE,QAAQ;SACf,CAAC,CAAC;QAEH,uEAAuE;QACvE,6EAA6E;QAC7E,uEAAuE;QACvE,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,oCAAoC;QACpC,MAAM,UAAU,GACd,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,4BAA4B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,qNAAqN;YACrQ,CAAC,CAAC,SAAS,CAAC;QAEhB,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,MAAM;YACN,OAAO;YACP,OAAO;YACP,MAAM;YACN,IAAI;YACJ,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -3,12 +3,12 @@ import { loadConfig } from '../config.js';
3
3
  import { normalizeSlug } from '../slug.js';
4
4
  import { decayedScore, isKnown } from '../srs.js';
5
5
  import { resolveSessionId } from '../session.js';
6
- import { conceptBySlug, gradeConcept, hasAskedQuestion, logSessionConcept, syncGate } from '../store.js';
6
+ import { conceptBySlug, gradeConcept, hasAskedQuestion, logSessionConcept, syncGate, } from '../store.js';
7
7
  import { CWD_HINT, SESSION_HINT } from './types.js';
8
8
  export const recordAttempt = {
9
9
  name: 'record_attempt',
10
10
  title: 'Record a quiz attempt',
11
- description: 'Grade one answer on the 0-5 SM-2 scale and persist it. Updates mastery, the next review date and the session gate. Record skips too, as grade 0 with feedback "skipped".',
11
+ description: 'Grade one answer on the 0-5 SM-2 scale and persist it. Updates mastery, the next review date and the session gate. Record every response, including "I don\'t know" (grade 0, outcome dont_know, after you have taught it) and declines (grade 0, outcome declined).',
12
12
  inputSchema: {
13
13
  session_id: z.string().optional().describe(SESSION_HINT),
14
14
  cwd: z.string().optional().describe(CWD_HINT),
@@ -23,6 +23,10 @@ export const recordAttempt = {
23
23
  .describe('0 no answer/skip, 1-2 wrong, 3 correct but laboured, 4 correct, 5 correct and explained the why.'),
24
24
  difficulty: z.number().int().min(1).max(5).describe('The tier you actually asked at — use tier_to_ask from the plan.'),
25
25
  feedback: z.string().optional().describe('The short explanation you gave back.'),
26
+ outcome: z
27
+ .enum(['answered', 'dont_know', 'declined'])
28
+ .optional()
29
+ .describe('Why the grade is what it is. "answered" they attempted it; "dont_know" they said they did not know and you taught it; "declined" they chose to skip. Grade 0 covers the last two, so this is the only thing that tells them apart later.'),
26
30
  },
27
31
  handler: (args, { db }) => {
28
32
  const now = new Date();
@@ -40,9 +44,12 @@ export const recordAttempt = {
40
44
  // Checked before the write, or the question we are recording matches itself.
41
45
  const repeatQuestion = hasAskedQuestion(db, concept.id, args.question);
42
46
  const state = db.transaction(() => {
43
- // An attempt on a concept the session never logged still counts toward the
44
- // gate, so quizzing on review debt is not free.
45
- logSessionConcept(db, sessionId, concept.id, null);
47
+ // An attempt on a concept the session never logged still counts toward
48
+ // `answered`, so quizzing on review debt is not free -- but it lands as
49
+ // 'review', so it cannot satisfy a bar that the session's actual work set.
50
+ // If the task did touch this concept, log_session_concepts has already
51
+ // marked it 'work' and that wins.
52
+ logSessionConcept(db, sessionId, concept.id, null, 'review');
46
53
  return gradeConcept(db, {
47
54
  conceptId: concept.id,
48
55
  sessionId,
@@ -51,6 +58,11 @@ export const recordAttempt = {
51
58
  grade: args.grade,
52
59
  difficulty: args.difficulty,
53
60
  feedback: args.feedback ?? null,
61
+ // Left NULL rather than guessed when the tutor does not say. An absent
62
+ // answer is a fair hint that nothing was attempted, but it cannot tell
63
+ // "teach me" from "leave it" -- and inventing the difference here would
64
+ // put a value in the column that nobody observed.
65
+ outcome: args.outcome ?? null,
54
66
  now,
55
67
  });
56
68
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"record_attempt.js","sourceRoot":"","sources":["../../src/tools/record_attempt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACzG,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAElE,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,uBAAuB;IAC9B,WAAW,EACT,0KAA0K;IAC5K,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;QAC7C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;QAC9D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC/D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;QAC1F,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CAAC,kGAAkG,CAAC;QAC/G,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,iEAAiE,CAAC;QACtH,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;KACjF;IACD,OAAO,EAAE,CACP,IASC,EACD,EAAE,EAAE,EAAE,EACN,EAAE;QACF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,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;QAExD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,KAAK,EAAE,iBAAiB;gBACxB,IAAI;gBACJ,MAAM,EAAE,gFAAgF;aACzF,CAAC;QACJ,CAAC;QAED,6EAA6E;QAC7E,MAAM,cAAc,GAAG,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEvE,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YAChC,2EAA2E;YAC3E,gDAAgD;YAChD,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACnD,OAAO,YAAY,CAAC,EAAE,EAAE;gBACtB,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,SAAS;gBACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;gBAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;gBAC/B,GAAG;aACJ,CAAC,CAAC;QACL,CAAC,CAAC,EAAE,CAAC;QAEL,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAEhE,OAAO;YACL,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACzC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,aAAa,EAAE,KAAK,CAAC,UAAU;YAC/B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACnC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YAC3C,yEAAyE;YACzE,yEAAyE;YACzE,eAAe,EAAE,cAAc;YAC/B,IAAI;SACL,CAAC;IACJ,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"record_attempt.js","sourceRoot":"","sources":["../../src/tools/record_attempt.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Eklavya MCP server — local knowledge graph, spaced repetition, and quiz gating for agent-assisted learning.",
5
5
  "license": "MIT",
6
6
  "repository": {