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 +3 -1
- package/bin/eklavya-mcp.sh +1 -1
- package/dist/assets/tutor-skill.md +79 -9
- package/dist/cli.js +33 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/migrations/004_attempt_outcome.sql +15 -0
- package/dist/migrations/005_session_concept_origin.sql +24 -0
- package/dist/store.js +155 -12
- package/dist/store.js.map +1 -1
- package/dist/tools/config_tools.js +18 -1
- package/dist/tools/config_tools.js.map +1 -1
- package/dist/tools/get_session_quiz_plan.js +157 -19
- package/dist/tools/get_session_quiz_plan.js.map +1 -1
- package/dist/tools/log_session_concepts.js +24 -4
- package/dist/tools/log_session_concepts.js.map +1 -1
- package/dist/tools/record_attempt.js +17 -5
- package/dist/tools/record_attempt.js.map +1 -1
- package/package.json +1 -1
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
|
```
|
package/bin/eklavya-mcp.sh
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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 |
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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,
|
|
94
|
-
|
|
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
|
-
|
|
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
|
|
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. */
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -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)
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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":"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,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
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":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAY;IAChC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,+GAA+G;IACjH,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;IAC9D,OAAO,EAAE,CAAC,IAAsB,EAAE,EAAE;QAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,WAAW,EAAE,QAAQ,CAAC,UAAU;YAChC,SAAS,EAAE,QAAQ,CAAC,QAAQ;YAC5B,SAAS,EAAE,QAAQ,CAAC,QAAQ;
|
|
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
|
|
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:
|
|
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:
|
|
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 (
|
|
178
|
+
if (effDomain) {
|
|
105
179
|
clauses.push('c.domain = ?');
|
|
106
|
-
params.push(
|
|
180
|
+
params.push(effDomain);
|
|
107
181
|
}
|
|
108
|
-
if (
|
|
109
|
-
clauses.push(`c.slug IN (${
|
|
110
|
-
params.push(...
|
|
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
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"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,
|
|
1
|
+
{"version":3,"file":"log_session_concepts.js","sourceRoot":"","sources":["../../src/tools/log_session_concepts.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
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
|
|
44
|
-
//
|
|
45
|
-
|
|
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,
|
|
1
|
+
{"version":3,"file":"record_attempt.js","sourceRoot":"","sources":["../../src/tools/record_attempt.ts"],"names":[],"mappings":"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"}
|