dsh-nerd-mode 0.2.1 → 0.4.3
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 +218 -163
- package/README.zh.md +220 -163
- package/assets/calibration.svg +69 -0
- package/assets/choose-a-mode.svg +48 -0
- package/assets/nerd.svg +22 -0
- package/assets/section.md +15 -0
- package/assets/two-paths.svg +93 -0
- package/client.js +1391 -161
- package/cordis.patch.yml +24 -19
- package/lib/index.js +223 -93
- package/package.json +15 -3
- package/skills/study/SKILL.md +78 -86
- package/skills/study-plus/SKILL.md +246 -185
- package/skills/study-plus/scripts/book-client.js +286 -0
- package/skills/study-plus/scripts/book-data.mjs +289 -0
- package/skills/study-plus/scripts/build-book.mjs +149 -0
- package/skills/writing-beats/agents/openai.yaml +1 -1
- package/assets/icon-dark.svg +0 -5
- package/assets/icon-light.svg +0 -5
package/cordis.patch.yml
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
# Nerd Mode is a profile-level plugin, not a preset. See docs/adr/0008.
|
|
2
|
-
- insert:
|
|
3
|
-
- id: dsh-nerd-mode
|
|
4
|
-
name: dsh-nerd-mode
|
|
5
|
-
config:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
# Nerd Mode is a profile-level plugin, not a preset. See docs/adr/0008.
|
|
2
|
+
- insert:
|
|
3
|
+
- id: dsh-nerd-mode
|
|
4
|
+
name: dsh-nerd-mode
|
|
5
|
+
config:
|
|
6
|
+
recallSessionEnabled: false
|
|
7
|
+
unseenQuestionCount: 3
|
|
8
|
+
unseenQuestionType: short-answer
|
|
9
|
+
- id: study-plus-skills
|
|
10
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
11
|
+
config:
|
|
12
|
+
providerName: study-plus
|
|
13
|
+
includeDefaultRoots: false
|
|
14
|
+
bundledSkillDir: !!js process.getBuiltinModule('node:path').join(process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(baseUrl).resolve('dsh-nerd-mode/package.json')), 'skills')
|
|
15
|
+
|
|
16
|
+
# Goal mode stays off. The built-in create_goal description tells the model to
|
|
17
|
+
# arm a goal for any long-running request, which turns one study answer into an
|
|
18
|
+
# auto-continuing run. Without the three packages below, no goal can be created.
|
|
19
|
+
- id: tool-goal
|
|
20
|
+
disabled: true
|
|
21
|
+
- id: command-goal
|
|
22
|
+
disabled: true
|
|
23
|
+
- id: goal-round-driver
|
|
24
|
+
disabled: true
|
package/lib/index.js
CHANGED
|
@@ -1,93 +1,223 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nerd Mode: a per-session switch. While it is on, one prompt section joins
|
|
3
|
-
* every request and the `study` skill becomes the default answer shape. State
|
|
4
|
-
* folds from the session log, so it survives compaction, resume, and fork. The
|
|
5
|
-
* tool set never changes.
|
|
6
|
-
*
|
|
7
|
-
* The section registers at the profile level, not inside a preset, so it
|
|
8
|
-
* reaches every agent in every preset. See docs/adr/0008.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Nerd Mode: a per-session switch. While it is on, one prompt section joins
|
|
3
|
+
* every request and the `study` skill becomes the default answer shape. State
|
|
4
|
+
* folds from the session log, so it survives compaction, resume, and fork. The
|
|
5
|
+
* tool set never changes.
|
|
6
|
+
*
|
|
7
|
+
* The section registers at the profile level, not inside a preset, so it
|
|
8
|
+
* reaches every agent in every preset. See docs/adr/0008.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { COMMAND, KEY, fold, init } from './fold.js'
|
|
14
|
+
import z from '@deepseek-ai/schemastery'
|
|
15
|
+
|
|
16
|
+
export const name = 'dsh-nerd-mode'
|
|
17
|
+
export const PACKAGE_NAME = 'dsh-nerd-mode'
|
|
18
|
+
export const inject = ['systemPrompt', 'sessionProjections']
|
|
19
|
+
|
|
20
|
+
// The prompt text ships with the package. Keeping it out of the config stops
|
|
21
|
+
// every profile patch from carrying a stale copy of it.
|
|
22
|
+
const BUNDLED_SECTION = readFileSync(
|
|
23
|
+
fileURLToPath(new URL('../assets/section.md', import.meta.url)),
|
|
24
|
+
'utf8',
|
|
25
|
+
).trim()
|
|
26
|
+
|
|
27
|
+
export const Config = z.object({
|
|
28
|
+
section: z.string().pattern(/\S/).default(BUNDLED_SECTION),
|
|
29
|
+
recallSessionEnabled: z.boolean().default(false).volatile(),
|
|
30
|
+
unseenQuestionCount: z.number().step(1).min(0).max(10).default(3).volatile(),
|
|
31
|
+
unseenQuestionType: z.union([
|
|
32
|
+
z.const('short-answer'),
|
|
33
|
+
z.const('multiple-choice'),
|
|
34
|
+
z.const('mixed'),
|
|
35
|
+
]).default('short-answer').volatile(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const SECTION = 'study:policy'
|
|
39
|
+
// Between PLAN_POLICY (500) and TEAM_POLICY (600).
|
|
40
|
+
const SECTION_ORDER = 550
|
|
41
|
+
const studyPlusDefaultsBySession = new WeakMap()
|
|
42
|
+
const directStudyPlusBySession = new WeakMap()
|
|
43
|
+
// A PTC skill load leaves no message in the history, so keep its id for later
|
|
44
|
+
// turns the way an injected skill message would.
|
|
45
|
+
const ptcStudyPlusBySession = new WeakMap()
|
|
46
|
+
const SKILL_COMMAND = /(^|\s)\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\s|$)/g
|
|
47
|
+
|
|
48
|
+
// shortcut: parse-only schemas, no zod. The projection registry calls `parse`
|
|
49
|
+
// and nothing else. Upgrade to zod if the host starts introspecting schemas.
|
|
50
|
+
const stateSchema = {
|
|
51
|
+
parse: (value) => ({
|
|
52
|
+
active: value?.active === true,
|
|
53
|
+
running:
|
|
54
|
+
value?.running && typeof value.running.commandId === 'string'
|
|
55
|
+
? { commandId: value.running.commandId, wanted: value.running.wanted === true }
|
|
56
|
+
: null,
|
|
57
|
+
}),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const viewSchema = { parse: (value) => ({ active: value?.active === true }) }
|
|
61
|
+
|
|
62
|
+
function studyPlusSettings(config, session, invocationId) {
|
|
63
|
+
let defaults = studyPlusDefaultsBySession.get(session)
|
|
64
|
+
if (defaults?.invocationId !== invocationId) {
|
|
65
|
+
defaults = {
|
|
66
|
+
invocationId,
|
|
67
|
+
recallSessionEnabled: config.recallSessionEnabled?.get?.() ?? false,
|
|
68
|
+
unseenQuestionCount: config.unseenQuestionCount?.get?.() ?? 3,
|
|
69
|
+
unseenQuestionType: config.unseenQuestionType?.get?.() ?? 'short-answer',
|
|
70
|
+
}
|
|
71
|
+
studyPlusDefaultsBySession.set(session, defaults)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return [
|
|
75
|
+
'',
|
|
76
|
+
'',
|
|
77
|
+
'Study-plus profile defaults for this tracked session:',
|
|
78
|
+
`- Recall Session: ${defaults.recallSessionEnabled ? 'enabled' : 'disabled'}.`,
|
|
79
|
+
`- Post-lecture questions: ${defaults.unseenQuestionCount}; type: ${defaults.unseenQuestionType}.`,
|
|
80
|
+
'Apply these defaults only during tracked study-plus sessions. Ignore them for ordinary study and all other requests.',
|
|
81
|
+
'An explicit preference in the current /study-plus request overrides its profile default for this session and does not update the profile.',
|
|
82
|
+
].join('\n')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Return the message id when the message is a direct /study-plus command, else undefined. */
|
|
86
|
+
function directStudyPlusId(message) {
|
|
87
|
+
if (message?.source?.kind !== 'user') return undefined
|
|
88
|
+
const text = (message.content ?? [])
|
|
89
|
+
.filter((block) => block.type === 'text')
|
|
90
|
+
.map((block) => block.text)
|
|
91
|
+
.join('\n')
|
|
92
|
+
return [...text.matchAll(SKILL_COMMAND)].at(-1)?.[2] === 'study-plus' ? message.id : undefined
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function studyPlusInvocation(session) {
|
|
96
|
+
const directInvocation = directStudyPlusBySession.get(session)
|
|
97
|
+
if (directInvocation !== undefined) return directInvocation
|
|
98
|
+
const ptcInvocation = ptcStudyPlusBySession.get(session)
|
|
99
|
+
if (ptcInvocation !== undefined) return ptcInvocation
|
|
100
|
+
|
|
101
|
+
// The host returns the full surface history here, not just this turn. See docs/agents/dsh-session-api.md.
|
|
102
|
+
const invocation = session.deriveMessages().findLast((message) => message.source?.kind === 'skill-invocation')
|
|
103
|
+
if (invocation?.source.name === 'study-plus') return invocation.id
|
|
104
|
+
|
|
105
|
+
const completedCalls = new Set()
|
|
106
|
+
let loadedId
|
|
107
|
+
let directId
|
|
108
|
+
let skillCall
|
|
109
|
+
let ptcSkillCall
|
|
110
|
+
for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
|
|
111
|
+
const event = session.eventAt(seq)
|
|
112
|
+
if (event?.type === 'turn/start') break
|
|
113
|
+
// A resumed, forked, or seeded log may carry a partial event payload, so
|
|
114
|
+
// every nested read here is guarded. One bad record must not fail a turn.
|
|
115
|
+
if (event?.type === 'tool/result') {
|
|
116
|
+
const result = event.data?.message
|
|
117
|
+
if (result !== undefined && result.isError !== true && result.source?.callId !== undefined) {
|
|
118
|
+
completedCalls.add(result.source.callId)
|
|
119
|
+
}
|
|
120
|
+
} else if (event?.type === 'tool/call' && event.data?.name === 'skill') {
|
|
121
|
+
try {
|
|
122
|
+
if (JSON.parse(event.data.arguments).name === 'study-plus') skillCall = event.data.callId
|
|
123
|
+
} catch {}
|
|
124
|
+
} else if (event?.type === 'tool/ptc-dispatch' && event.data?.name === 'skill') {
|
|
125
|
+
// PTC records a skill call made inside run_code as a dispatch, not as a
|
|
126
|
+
// `tool/call`, and the dispatch event itself carries the outcome.
|
|
127
|
+
if (event.data.isError !== true && event.data.arguments?.name === 'study-plus') {
|
|
128
|
+
ptcSkillCall = event.data.subCallId
|
|
129
|
+
}
|
|
130
|
+
} else if (event?.type === 'user/message') {
|
|
131
|
+
// `user/message` stores the message fields directly in `data`; only
|
|
132
|
+
// `tool/result` and `system/message` wrap them under `data.message`.
|
|
133
|
+
const message = event.data
|
|
134
|
+
if (message?.source?.kind === 'skill-invocation') {
|
|
135
|
+
if (message.source.name === 'study-plus') loadedId = message.id
|
|
136
|
+
} else {
|
|
137
|
+
directId = directStudyPlusId(message) ?? directId
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// A skill load counts only when its tool call completed. A typed command counts at once.
|
|
143
|
+
if (skillCall !== undefined) {
|
|
144
|
+
return completedCalls.has(skillCall) ? loadedId ?? directId ?? skillCall : undefined
|
|
145
|
+
}
|
|
146
|
+
if (ptcSkillCall !== undefined) {
|
|
147
|
+
ptcStudyPlusBySession.set(session, ptcSkillCall)
|
|
148
|
+
return loadedId ?? directId ?? ptcSkillCall
|
|
149
|
+
}
|
|
150
|
+
return loadedId ?? directId
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const projection = {
|
|
154
|
+
key: KEY,
|
|
155
|
+
stateVersion: 1,
|
|
156
|
+
stateSchema,
|
|
157
|
+
init,
|
|
158
|
+
apply: fold,
|
|
159
|
+
wire: {
|
|
160
|
+
viewSchema,
|
|
161
|
+
view: (state) => ({ active: state.active }),
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param config - `section` is an optional overlay text; a missing value uses
|
|
167
|
+
* the bundled `assets/section.md`.
|
|
168
|
+
*/
|
|
169
|
+
export function apply(ctx, config) {
|
|
170
|
+
const section = config?.section
|
|
171
|
+
if (typeof section !== 'string' || section.trim() === '') {
|
|
172
|
+
throw new Error('dsh-nerd-mode needs a non-empty string `section`')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
ctx.sessionProjections.register(projection)
|
|
176
|
+
|
|
177
|
+
ctx.effect(
|
|
178
|
+
() => ctx.on('agent/inbox/claimed', ({ agent, message }) => {
|
|
179
|
+
directStudyPlusBySession.set(agent.session, directStudyPlusId(message))
|
|
180
|
+
}),
|
|
181
|
+
'dsh-nerd-mode: study-plus claim',
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
ctx.effect(
|
|
185
|
+
() =>
|
|
186
|
+
ctx.systemPrompt.section({
|
|
187
|
+
name: SECTION,
|
|
188
|
+
order: SECTION_ORDER,
|
|
189
|
+
text: (context) => {
|
|
190
|
+
if (context.agent === undefined) return ''
|
|
191
|
+
if (!ctx.sessionProjections.stateOf(context.agent.session, KEY).active) return ''
|
|
192
|
+
const session = context.agent.session
|
|
193
|
+
const invocationId = studyPlusInvocation(session)
|
|
194
|
+
if (invocationId === undefined) return section
|
|
195
|
+
return section + studyPlusSettings(config ?? {}, session, invocationId)
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
'dsh-nerd-mode: section',
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
ctx.inject(['commands'], (commandCtx) => {
|
|
202
|
+
commandCtx.commands.register({
|
|
203
|
+
name: COMMAND,
|
|
204
|
+
description: 'Turn Nerd Mode on or off for this conversation',
|
|
205
|
+
input: { hint: '[on|off]' },
|
|
206
|
+
handler: ({ agent, rawInput }) => {
|
|
207
|
+
const arg = rawInput.trim().toLowerCase()
|
|
208
|
+
const active = ctx.sessionProjections.stateOf(agent.session, KEY).active
|
|
209
|
+
|
|
210
|
+
if (arg === 'off') {
|
|
211
|
+
return { kind: 'success', text: active ? 'Nerd Mode off.' : 'Nerd Mode is already off.' }
|
|
212
|
+
}
|
|
213
|
+
if (arg !== '' && arg !== 'on') {
|
|
214
|
+
return { kind: 'error', text: `Unknown argument "${arg}". Use /nerd-mode on or /nerd-mode off.` }
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
kind: 'success',
|
|
218
|
+
text: active ? 'Nerd Mode is already on.' : 'Nerd Mode on. /study guides a concept or a task.',
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-nerd-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "A study switch and study skills for DeepSeek Harness.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,11 +36,18 @@
|
|
|
36
36
|
"client": {
|
|
37
37
|
"platform": "web",
|
|
38
38
|
"immediately": true,
|
|
39
|
-
"inject": [
|
|
39
|
+
"inject": [
|
|
40
|
+
"@deepseek-ai/dsh-client-ui-conversation"
|
|
41
|
+
]
|
|
40
42
|
}
|
|
41
43
|
},
|
|
42
44
|
"scripts": {
|
|
43
|
-
"test": "node --test"
|
|
45
|
+
"test": "node --test",
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"check": "npm run lint && npm test"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@deepseek-ai/schemastery": "^3.18.5"
|
|
44
51
|
},
|
|
45
52
|
"engines": {
|
|
46
53
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -54,5 +61,10 @@
|
|
|
54
61
|
],
|
|
55
62
|
"publishConfig": {
|
|
56
63
|
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@deepseek-ai/schemastery": "^3.18.5",
|
|
67
|
+
"eslint": "^10.13.0",
|
|
68
|
+
"globals": "^17.13.0"
|
|
57
69
|
}
|
|
58
70
|
}
|
package/skills/study/SKILL.md
CHANGED
|
@@ -1,86 +1,78 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: study
|
|
3
|
-
description:
|
|
4
|
-
argument-hint: "<concept or task>"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## 9. Do not
|
|
81
|
-
|
|
82
|
-
- Do not read or write `study/`. Not `graph.md`, not `state.md`, not a new file.
|
|
83
|
-
- Do not call the learner's understanding mastered, verified, or fluent.
|
|
84
|
-
- Do not open with flattery or close with "does that make sense".
|
|
85
|
-
- Do not stack questions. One at a time.
|
|
86
|
-
- Do not turn a code task into a project. Produce the asked artifact and stop.
|
|
1
|
+
---
|
|
2
|
+
name: study
|
|
3
|
+
description: Teach one concept or help complete one task, using the supplied material as scope and defaulting to chat output.
|
|
4
|
+
argument-hint: "<concept or task>"
|
|
5
|
+
---
|
|
6
|
+
# Study
|
|
7
|
+
|
|
8
|
+
Teach one concept or help complete one task. This skill owns the standards for teaching and lecture writing, including lecture drafts. `study-plus` owns tracked sessions, tracker updates, the choice of lecture paths, and book assembly. Never load or call `study-plus` from this skill.
|
|
9
|
+
|
|
10
|
+
The upper layer selects the path for multiple independent concepts. Follow that path and do not ask again.
|
|
11
|
+
|
|
12
|
+
## Teach accurately
|
|
13
|
+
|
|
14
|
+
- Use supplied material to set the scope, title, chapter boundaries, and order. Treat it as a reference, not as truth. Check important or uncertain claims when needed, prefer accuracy, and flag differences.
|
|
15
|
+
- Use an approved graph or supplied material by default. Do not research the web for every lecture.
|
|
16
|
+
- Cover the major ideas, relationships, and qualifications. Explain intuition first, then terminology, then formal detail. Choose a structure that fits the content. Do not force a fixed section template.
|
|
17
|
+
- Use examples and analogies when they help. State where an analogy stops matching. Include exam topics only when they matter to the request.
|
|
18
|
+
- Use tables or figures when they clarify the material. Define every mathematical symbol and its role. In derivations, justify each meaningful step, explain the result, and check algebra, arithmetic, assumptions, and units.
|
|
19
|
+
- Write math in LaTeX. Put one expression in each math span. Keep prose outside math spans.
|
|
20
|
+
- Give every symbol a noun and a role. Define each symbol once, where the reader first meets it: inside the sentence for an inline mention, or in one line directly above the first display formula that uses it. Keep the line in the formula's reading order, and switch to one symbol per line when a definition needs an explanatory clause. Do not restate a symbol that an earlier sentence or definition line already made clear.
|
|
21
|
+
- When four or more symbols each appear in two or more formulas, put a short table of just those symbols before the first formula. The table is a lookup aid, not a definition, so it does not remove any symbol from the line above a formula.
|
|
22
|
+
- Compare three or more numbers in a short table before the first formula.
|
|
23
|
+
- State bounds as verbal comparisons. Explain each formula once in plain words without symbols.
|
|
24
|
+
- Use “you” for the reader’s actions and “we” for shared work. Avoid generic “one” and passive wording when the reader acts. Correct misleading terminology at first use.
|
|
25
|
+
- Justify each meaningful derivation step before showing it. Put one meaningful move on each display line, and split long equality chains where the object changes. Mark approximations and assumptions.
|
|
26
|
+
- Prefer verbs to abstract nouns. Open each paragraph with its claim, then support it. State each fact once. Avoid rhetorical questions that ask the reader to infer a conclusion. Keep lecture questions sparse and purposeful.
|
|
27
|
+
- Name headings for their content. Number them only when useful.
|
|
28
|
+
- For a task, produce the requested usable artifact and explain the important steps. Do not expand it into a project or return an avoidable partial result.
|
|
29
|
+
|
|
30
|
+
## Teach at the given level
|
|
31
|
+
|
|
32
|
+
The upper layer may give a teaching brief of up to three lines: `Goal`, `Background`, and `Teaching level`. Treat it as context for writing, and never read `graph.md` or any other tracker file to obtain it.
|
|
33
|
+
|
|
34
|
+
`standard` uses every rule in this file unchanged. A missing `Teaching level` line means `standard`.
|
|
35
|
+
|
|
36
|
+
For `beginner`:
|
|
37
|
+
|
|
38
|
+
- Split the lecture into 3 to 7 numbered steps when the concept takes more than one move.
|
|
39
|
+
- Explain each term at first use, including terms that `standard` assumes, such as a prerequisite idea or a notation convention.
|
|
40
|
+
- Add one worked example per step when an example helps.
|
|
41
|
+
- Never add a prerequisite recap block. No level adds one.
|
|
42
|
+
|
|
43
|
+
For `advanced`:
|
|
44
|
+
|
|
45
|
+
- Use one pass without numbered steps, unless the derivation needs them.
|
|
46
|
+
- Skip the intuition for an idea only when the `Background` line names that idea as known. Start from the formal statement.
|
|
47
|
+
- Use at most one example per idea. Keep the qualifications and edge cases that matter for the stated goal.
|
|
48
|
+
|
|
49
|
+
Accuracy rules outrank the level. No level removes a symbol definition, a justification for a derivation step, a stated assumption or approximation, or a bound. No level adds filler, repeated explanation, or a section the concept does not need. The level affects the lecture only.
|
|
50
|
+
|
|
51
|
+
For a one-off request with no graph, derive the level from the brief the upper layer supplies and from the request and the supplied material. A brief from an upper layer is final: never ask again for a goal or a background it leaves out. A bare topic, or "I want to learn X", states no goal. When no brief arrives and neither the request nor the supplied material states a goal or a background, ask one combined question before teaching: what the learner wants to reach and whether they have met the topic before. State the derived level in one line, then teach. Write no file.
|
|
52
|
+
|
|
53
|
+
## Format the answer
|
|
54
|
+
|
|
55
|
+
- For one concept, use an H1 named for the concept.
|
|
56
|
+
- For a one-off multi-concept book, use ordinary Markdown, not a fenced block. Use an H1 for the book or topic, H2 headings for Chapters, and H3 headings for concept Sections. Name each Section after its concept. Follow the supplied book order when it exists.
|
|
57
|
+
- For a task, title the requested artifact or task. Explain the reason for non-obvious steps.
|
|
58
|
+
- Give the complete answer when it fits. If a platform limit forces continuation, state which chapters are covered and which remain, then wait.
|
|
59
|
+
- Do not quiz, grade, score, or log. You may offer similar practice, but do not impose it.
|
|
60
|
+
|
|
61
|
+
## Write lecture drafts
|
|
62
|
+
|
|
63
|
+
The upper layer may hand you an approved concept graph, the relevant supplied material, the teaching brief, and one destination file per concept. Then write one complete draft per concept at the given level, under the same writing standard. A draft is prepared teaching material, not teaching.
|
|
64
|
+
|
|
65
|
+
- Cover the concept completely at the given level: the teaching explanation, its relationships, and useful examples. Write no assessment questions, no answers, and no grading criteria. Actual questions stay unseen.
|
|
66
|
+
- Write only the destinations you are given, and one Chapter per batch when the upper layer splits the work. Never invent a path, and never read or write `graph.md`, `state.md`, `log.md`, `plan.md`, or any other tracker file.
|
|
67
|
+
- Frontmatter is exactly `status: draft` and the concept number. Add no date. The body opens with an H1 named for the concept.
|
|
68
|
+
- Preparation is not teaching. Change no learner state and append nothing to the log.
|
|
69
|
+
- Keep a valid existing draft or dated lecture at a destination. Report it instead of overwriting. Leave an occupied or unrecognized target alone and report it.
|
|
70
|
+
- Report the paths you wrote and every destination you refused. Print no draft in chat.
|
|
71
|
+
|
|
72
|
+
## Files and figures
|
|
73
|
+
|
|
74
|
+
Default to chat output. If the learner asks to save a lecture or other artifact, save it to the requested path and show the same content in chat when practical.
|
|
75
|
+
|
|
76
|
+
When an SVG would materially clarify a relationship, create it by default. When the upper layer scopes a Course, write `study/<course>/diagrams/<name>.svg`. When the guide runs alone, write `diagrams/<name>.svg` and create no folder under `study/`. Load `diagram` for its visual authoring rules, but use this study-specific path instead of its generic default. Sanitize `course` and `name` as single lowercase kebab-case path segments with no slashes or dot segments. If either is unclear, choose a safe fallback. Before writing, confirm the resolved parent stays inside the intended `diagrams/` root and the target is not `study/<course>/graph.md`, `state.md`, `log.md`, or `plan.md`. Compute plotted points rather than placing them by eye. Before replacing an SVG with the same name, copy the old file to `<name>.svg.bak`, replacing any older backup.
|
|
77
|
+
|
|
78
|
+
Never read or write `study/<course>/graph.md`, `state.md`, `log.md`, or `plan.md`. Do not read or change tracker state. Study-plus owns the choice of lecture paths and the automatic lecture save during tracked sessions. Study may read or write ordinary artifacts, including lectures, drafts, and figures, at paths the user or the upper layer gives it.
|