@rookiestar/eng-lang-tutor 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.claude/settings.local.json +22 -0
  2. package/.gitignore +32 -0
  3. package/CHANGELOG.md +37 -0
  4. package/CLAUDE.md +275 -0
  5. package/README.md +369 -0
  6. package/SKILL.md +613 -0
  7. package/bin/eng-lang-tutor.js +177 -0
  8. package/docs/OPENCLAW_DEPLOYMENT.md +241 -0
  9. package/examples/sample_keypoint_a1.json +112 -0
  10. package/examples/sample_keypoint_a2.json +124 -0
  11. package/examples/sample_keypoint_b1.json +135 -0
  12. package/examples/sample_keypoint_b2.json +137 -0
  13. package/examples/sample_keypoint_c1.json +134 -0
  14. package/examples/sample_keypoint_c2.json +141 -0
  15. package/examples/sample_quiz_a1.json +94 -0
  16. package/examples/sample_quiz_a2.json +94 -0
  17. package/examples/sample_quiz_b1.json +92 -0
  18. package/examples/sample_quiz_b2.json +94 -0
  19. package/examples/sample_quiz_c1.json +94 -0
  20. package/examples/sample_quiz_c2.json +104 -0
  21. package/package.json +41 -0
  22. package/references/resources.md +292 -0
  23. package/requirements.txt +16 -0
  24. package/scripts/__init__.py +28 -0
  25. package/scripts/audio/__init__.py +23 -0
  26. package/scripts/audio/composer.py +367 -0
  27. package/scripts/audio/converter.py +331 -0
  28. package/scripts/audio/feishu_voice.py +404 -0
  29. package/scripts/audio/tts/__init__.py +30 -0
  30. package/scripts/audio/tts/base.py +166 -0
  31. package/scripts/audio/tts/manager.py +306 -0
  32. package/scripts/audio/tts/providers/__init__.py +12 -0
  33. package/scripts/audio/tts/providers/edge.py +111 -0
  34. package/scripts/audio/tts/providers/xunfei.py +205 -0
  35. package/scripts/audio/utils.py +63 -0
  36. package/scripts/cli/__init__.py +7 -0
  37. package/scripts/cli/cli.py +229 -0
  38. package/scripts/cli/command_parser.py +336 -0
  39. package/scripts/core/__init__.py +30 -0
  40. package/scripts/core/constants.py +125 -0
  41. package/scripts/core/error_notebook.py +308 -0
  42. package/scripts/core/gamification.py +405 -0
  43. package/scripts/core/scorer.py +295 -0
  44. package/scripts/core/state_manager.py +814 -0
  45. package/scripts/eng-lang-tutor +16 -0
  46. package/scripts/scheduling/__init__.py +6 -0
  47. package/scripts/scheduling/cron_push.py +229 -0
  48. package/scripts/utils/__init__.py +12 -0
  49. package/scripts/utils/dedup.py +331 -0
  50. package/scripts/utils/helpers.py +82 -0
  51. package/templates/keypoint_schema.json +420 -0
  52. package/templates/prompt_templates.md +73 -0
  53. package/templates/prompts/display_guide.md +106 -0
  54. package/templates/prompts/initialization.md +350 -0
  55. package/templates/prompts/keypoint_generation.md +272 -0
  56. package/templates/prompts/output_rules.md +106 -0
  57. package/templates/prompts/quiz_generation.md +190 -0
  58. package/templates/prompts/responses.md +339 -0
  59. package/templates/prompts/shared_enums.md +252 -0
  60. package/templates/quiz_schema.json +214 -0
  61. package/templates/state_schema.json +277 -0
package/SKILL.md ADDED
@@ -0,0 +1,613 @@
1
+ ---
2
+ name: eng-lang-tutor
3
+ description: "地道美式英语导师 - 提供每日知识点、Quiz测验等学习内容。支持游戏化学习(XP/连胜/等级/徽章)。触发场景:学习英语、英语知识点、Quiz、错题本、学习进度。被 cron job 调用以实现定期内容推送。"
4
+ ---
5
+
6
+ # American English Tutor
7
+
8
+ ## Overview
9
+
10
+ Teaches authentic American English expressions, avoiding Chinglish patterns.
11
+ Delivers personalized content via daily knowledge points and quizzes.
12
+ Includes Duolingo-style gamification (XP, streaks, levels, badges).
13
+
14
+ ## Core Workflow
15
+
16
+ ### 0. Initialization Guard Check
17
+
18
+ > **⛔ BEFORE generating any content (keypoint/quiz), check `initialized` status:**
19
+
20
+ ```
21
+ If state.initialized == false:
22
+ 1. Show friendly message: "看起来你还没有完成初始化配置,让我帮你快速设置一下吧!"
23
+ 2. Automatically start onboarding from Step 1/7
24
+ 3. DO NOT proceed with content generation until onboarding completes
25
+
26
+ If state.initialized == true:
27
+ Proceed with normal workflow below
28
+ ```
29
+
30
+ ### 1. Daily Knowledge Point Generation
31
+
32
+ ```
33
+ Input: state.json (user preferences, CEFR level, recent topics)
34
+ Process:
35
+ 1. Load user preferences from state.json
36
+ 2. Load recent topic fingerprints (14 days) for deduplication
37
+ 3. Select topic based on user preference weights
38
+ 4. Generate knowledge point via LLM (pure JSON output)
39
+ 5. Validate JSON schema
40
+ 6. Save to daily/YYYY-MM-DD/keypoint.json
41
+ 7. Update state.json recent_topics
42
+ 8. Append event to logs/events_YYYY-MM.jsonl
43
+ Output: keypoint.json
44
+ ```
45
+
46
+ ### 2. Quiz Generation
47
+
48
+ ```
49
+ Input: keypoint.json
50
+ Process:
51
+ 1. Read today's keypoint.json
52
+ 2. Generate 3 questions (fixed pattern):
53
+ - 1 multiple_choice (10 XP)
54
+ - 1 chinglish_fix (15 XP)
55
+ - 1 fill_blank OR dialogue_completion (12 XP, random)
56
+ 3. Save to daily/YYYY-MM-DD/quiz.json (with answers)
57
+ Output: quiz.json (~37 XP total)
58
+ ```
59
+
60
+ ### 3. Answer Evaluation
61
+
62
+ ```
63
+ Input: quiz.json, user_answers.json
64
+ Process:
65
+ 1. Compare user answers with correct answers
66
+ 2. Calculate XP (base + streak multiplier + perfect bonus)
67
+ 3. Update state.json (XP, streak, level, badges)
68
+ 4. Record wrong answers to error_notebook
69
+ Output: results.json, updated state.json
70
+ ```
71
+
72
+ ## Quiz Types
73
+
74
+ > See [templates/prompts/shared_enums.md](templates/prompts/shared_enums.md#quiz-question-types-题型) for full definitions.
75
+
76
+ | Type | XP | Daily |
77
+ |------|-----|-------|
78
+ | multiple_choice | 10 | 1 (required) |
79
+ | chinglish_fix | 15 | 1 (required) |
80
+ | fill_blank / dialogue_completion | 12 | 0-1 (random) |
81
+
82
+ **Daily Quiz:** 3 questions, ~37 XP, pass with 2/3 correct
83
+
84
+ ## Gamification System
85
+
86
+ ### XP & Levels
87
+
88
+ This system has two independent level systems:
89
+ - **Ability Level (CEFR)**: A1-C2, determines content difficulty
90
+ - **Activity Level (Level)**: 1-20, measures engagement depth
91
+
92
+ > See [templates/prompts/shared_enums.md](templates/prompts/shared_enums.md) for level stages and badge definitions.
93
+
94
+ ### Streak System
95
+ - Consecutive days of study builds streak
96
+ - Streak multiplier: `1.0 + (streak * 0.05)`, max 2.0x
97
+ - Streak freeze costs 50 gems
98
+
99
+ ## Key Scripts
100
+
101
+ | Script | Purpose |
102
+ |--------|---------|
103
+ | core/state_manager.py | State persistence, event logging, error notebook |
104
+ | core/error_notebook.py | Error notebook management |
105
+ | core/scorer.py | Answer evaluation, XP calculation |
106
+ | core/gamification.py | Streak/level/badge logic |
107
+ | core/constants.py | Shared constants (level thresholds, level names) |
108
+ | cli/cli.py | CLI entry point for state management |
109
+ | cli/command_parser.py | Natural language command parsing |
110
+ | scheduling/cron_push.py | Scheduled content push (keypoint/quiz placeholders) |
111
+ | utils/dedup.py | 14-day content deduplication |
112
+ | utils/helpers.py | Utility functions (safe divide, deep merge) |
113
+ | audio/ | Audio generation (TTS, composition, conversion, Feishu) |
114
+
115
+ ## CLI Commands
116
+
117
+ > These bash commands are used by the Agent to execute operations. Data is stored in `~/.openclaw/state/eng-lang-tutor/` by default. Do NOT specify `--data-dir` unless using a custom location.
118
+
119
+ ### Content Management
120
+ ```bash
121
+ # Save daily content (keypoint/quiz)
122
+ python3 -m scripts.cli.cli save_daily --content-type keypoint --content '<JSON>'
123
+
124
+ # Record keypoint view
125
+ python3 -m scripts.cli.cli record_view [--date YYYY-MM-DD]
126
+ ```
127
+
128
+ ### Stats & Config
129
+ ```bash
130
+ # Display learning progress
131
+ python3 -m scripts.cli.cli stats
132
+
133
+ # Display current configuration
134
+ python3 -m scripts.cli.cli config
135
+
136
+ # Update configuration
137
+ python3 -m scripts.cli.cli config --cefr B2
138
+ python3 -m scripts.cli.cli config --style professional
139
+ python3 -m scripts.cli.cli config --oral-ratio 80
140
+ ```
141
+
142
+ ### Error Notebook
143
+ ```bash
144
+ # List errors (paginated)
145
+ python3 -m scripts.cli.cli errors [--page 1] [--per-page 5] [--month YYYY-MM]
146
+
147
+ # Get random errors for review
148
+ python3 -m scripts.cli.cli errors --random 5
149
+
150
+ # Get error statistics
151
+ python3 -m scripts.cli.cli errors --stats
152
+
153
+ # Get errors for review session
154
+ python3 -m scripts.cli.cli errors --review 5
155
+ ```
156
+
157
+ ### Schedule
158
+ ```bash
159
+ # Display current schedule
160
+ python3 -m scripts.cli.cli schedule
161
+
162
+ # Update schedule (quiz_time must be later than keypoint_time)
163
+ python3 -m scripts.cli.cli schedule --keypoint-time 07:00 --quiz-time 21:00
164
+ ```
165
+
166
+ ## Core Principles
167
+
168
+ > See [templates/prompts/keypoint_generation.md](templates/prompts/keypoint_generation.md) for detailed generation rules.
169
+
170
+ 1. **Always output valid JSON** - No markdown, no extra text
171
+ 2. **Focus on "How Americans say it"** - NOT translation
172
+ 3. **Every knowledge point must include**: Scene context, alternatives, Chinglish trap
173
+ 4. **14-day deduplication** - No repeated topics or expressions
174
+
175
+ ## File Structure
176
+
177
+ ```
178
+ ~/.openclaw/state/eng-lang-tutor/ # Default data location
179
+ state.json # Core state (streak/xp/preferences)
180
+ logs/
181
+ events_2026-02.jsonl # Monthly event log
182
+ daily/
183
+ 2026-02-20/
184
+ keypoint.json # Today's knowledge point
185
+ quiz.json # Today's quiz
186
+ user_answers.json # User's answers
187
+ ```
188
+
189
+ **Note:** Data location can be customized via `OPENCLAW_STATE_DIR` environment variable.
190
+
191
+ ## JSON Schemas
192
+
193
+ See templates/ directory:
194
+ - state_schema.json
195
+ - keypoint_schema.json
196
+ - quiz_schema.json
197
+
198
+ ## Resource References
199
+
200
+ **Prompt Templates** (templates/prompts/):
201
+ - [shared_enums.md](templates/prompts/shared_enums.md) - Topics, CEFR levels, styles, badges
202
+ - [output_rules.md](templates/prompts/output_rules.md) - JSON/markdown formatting rules
203
+ - [keypoint_generation.md](templates/prompts/keypoint_generation.md) - Knowledge point generation
204
+ - [quiz_generation.md](templates/prompts/quiz_generation.md) - Quiz generation
205
+ - [display_guide.md](templates/prompts/display_guide.md) - Emoji and formatting guide
206
+ - [initialization.md](templates/prompts/initialization.md) - Onboarding flow
207
+ - [responses.md](templates/prompts/responses.md) - Response templates
208
+
209
+ **Other Resources:**
210
+ - [references/resources.md](references/resources.md) - Themed English learning resources
211
+ - [templates/prompt_templates.md](templates/prompt_templates.md) - Prompt template index
212
+
213
+ ## Examples
214
+
215
+ See `examples/` directory for sample outputs covering all CEFR levels:
216
+
217
+ | Level | Keypoint | Quiz |
218
+ |-------|----------|------|
219
+ | A1 (入门级) | sample_keypoint_a1.json | sample_quiz_a1.json |
220
+ | A2 (初级) | sample_keypoint_a2.json | sample_quiz_a2.json |
221
+ | B1 (中级) | sample_keypoint_b1.json | sample_quiz_b1.json |
222
+ | B2 (中高级) | sample_keypoint_b2.json | sample_quiz_b2.json |
223
+ | C1 (高级) | sample_keypoint_c1.json | sample_quiz_c1.json |
224
+ | C2 (精通级) | sample_keypoint_c2.json | sample_quiz_c2.json |
225
+
226
+ ---
227
+
228
+ ## User Commands
229
+
230
+ The bot recognizes these natural language commands:
231
+
232
+ ### Initialization
233
+ | Command | Aliases | Description |
234
+ |---------|---------|-------------|
235
+ | `start` | `begin`, `开始`, `初始化`, `你好` | Start the onboarding process |
236
+
237
+ ### Learning Content
238
+ | Command | Aliases | Description |
239
+ |---------|---------|-------------|
240
+ | `keypoint` | `知识点`, `今天`, `today` | View today's knowledge point |
241
+ | `keypoint history` | `知识点 历史`, `昨天`, `yesterday` | View historical keypoints |
242
+ | `quiz` | `测验`, `test`, `测试` | Take today's quiz (once per day) |
243
+
244
+ ### Progress & Stats
245
+ | Command | Aliases | Description |
246
+ |---------|---------|-------------|
247
+ | `stats` | `进度`, `统计`, `level`, `XP` | View learning progress |
248
+ | `errors` | `错题本`, `mistakes` | View error notebook (recent 5) |
249
+ | `errors more` | `错题本 更多` | Next 5 errors |
250
+ | `errors 2026-02` | `错题本 2026-02` | Filter by month |
251
+ | `errors random 5` | `错题本 随机5` | Random 5 for review |
252
+ | `errors stats` | `错题本 统计` | Show error statistics |
253
+ | `errors review` | `错题本 复习`, `错题复习` | Start error review session (5 questions) |
254
+
255
+ ### Settings
256
+ | Command | Aliases | Description |
257
+ |---------|---------|-------------|
258
+ | `config` | `设置`, `preferences` | View current settings |
259
+ | `set level B2` | `设置等级 B2` | Change CEFR level |
260
+ | `set style professional` | `设置风格 专业` | Change tutor style |
261
+ | `schedule` | `时间表`, `推送时间` | View/change schedule |
262
+
263
+ ### Help
264
+ | Command | Aliases | Description |
265
+ |---------|---------|-------------|
266
+ | `help` | `帮助`, `怎么用` | Show available commands |
267
+
268
+ ---
269
+
270
+ ## Initialization Flow
271
+
272
+ The 7-step onboarding process:
273
+ 1. CEFR Level
274
+ 2. Topic Interests
275
+ 3. Tutor Style
276
+ 4. Oral/Written Ratio
277
+ 5. Schedule
278
+ 6. Voice Teaching
279
+ 7. Confirm
280
+
281
+ Follow [templates/prompts/initialization.md](templates/prompts/initialization.md) exactly. Display ALL options with numbers (1, 2, 3...).
282
+
283
+ **Step 7 Completion:** Set `initialized=true` + Create cron jobs for keypoint/quiz push times.
284
+
285
+ **State Fields:**
286
+ - `initialized`: Boolean - Whether user completed onboarding
287
+ - `onboarding_step`: Integer (0-7) - Current step
288
+
289
+ ---
290
+
291
+ ## Response Scenarios
292
+
293
+ > **TIMEZONE AWARENESS:** All "today" date checks should use `state.json schedule.timezone` (default: Asia/Shanghai) to ensure consistent behavior across server locations. File paths like `daily/2026-02-21/` are timezone-specific.
294
+
295
+ ---
296
+
297
+ ## ⛔ MANDATORY BASH COMMANDS - READ THIS FIRST ⛔
298
+
299
+ **When generating content via LLM, you MUST execute bash commands to persist data to disk.**
300
+
301
+ ### After Keypoint Generation (MANDATORY)
302
+ ```
303
+ Step 1: LLM generates keypoint JSON
304
+ Step 2: ⛔ EXECUTE THIS BASH COMMAND (do NOT skip):
305
+ python3 -m scripts.cli.cli save_daily --content-type keypoint --content '<ESCAPED_JSON>'
306
+ (This auto-generates audio and saves to audio/YYYY-MM-DD/keypoint_full.mp3)
307
+ Step 3: ⛔ EXECUTE THIS BASH COMMAND (do NOT skip):
308
+ python3 -m scripts.cli.cli record_view
309
+ Step 4: Send audio file via message tool (if audio exists):
310
+ - Read keypoint.json and check if audio.composed field exists
311
+ - If exists, send audio file using message tool with media parameter
312
+ - Audio path: ~/.openclaw/state/eng-lang-tutor/{audio.composed}
313
+ Step 5: Display formatted content to user
314
+ ```
315
+
316
+ ### Audio File Sending (After Keypoint Save)
317
+
318
+ When keypoint is saved, audio is auto-generated. Send it to user with backward compatibility:
319
+
320
+ **Step 1: Check voice bubble capability**
321
+ ```bash
322
+ echo $FEISHU_VOICE_BUBBLE_ENABLED
323
+ ```
324
+
325
+ **Step 2a: If FEISHU_VOICE_BUBBLE_ENABLED=true (enhanced mode)**
326
+
327
+ Send BOTH voice bubble AND file attachment for best compatibility:
328
+
329
+ ```json
330
+ // Voice bubble (handled by enhanced OpenClaw gateway)
331
+ {
332
+ "action": "send",
333
+ "media": "~/.openclaw/media/{audio.composed}",
334
+ "asVoice": true
335
+ }
336
+
337
+ // File attachment (fallback for progress bar control)
338
+ {
339
+ "action": "send",
340
+ "media": "~/.openclaw/media/{audio.composed}",
341
+ "caption": "🔊 今日知识点语音版"
342
+ }
343
+ ```
344
+
345
+ **Step 2b: If FEISHU_VOICE_BUBBLE_ENABLED not set or false (standard mode)**
346
+
347
+ Only send file attachment:
348
+ ```json
349
+ {
350
+ "action": "send",
351
+ "media": "~/.openclaw/media/{audio.composed}",
352
+ "caption": "🔊 今日知识点语音版"
353
+ }
354
+ ```
355
+
356
+ **Audio file info is stored in keypoint.json:**
357
+ ```json
358
+ {
359
+ "audio": {
360
+ "composed": "eng-lang-tutor/2026-02-23/keypoint_full.mp3",
361
+ "duration_seconds": 37.7,
362
+ "generated_at": "2026-02-23T02:20:14"
363
+ }
364
+ }
365
+ ```
366
+
367
+ **IMPORTANT:**
368
+ - Audio is saved to `~/.openclaw/media/` (OpenClaw's allowed media directory)
369
+ - Always send audio file BEFORE displaying text content
370
+ - The `audio.composed` field contains the path relative to `~/.openclaw/media/`
371
+ - Enhanced mode (voice bubble) requires OpenClaw gateway patch for Feishu
372
+
373
+ ### After Quiz Generation (MANDATORY)
374
+ ```
375
+ Step 1: LLM generates quiz JSON
376
+ Step 2: ⛔ EXECUTE THIS BASH COMMAND (do NOT skip):
377
+ python3 -m scripts.cli.cli save_daily --content-type quiz --content '<ESCAPED_JSON>'
378
+ Step 3: Present quiz questions to user
379
+ ```
380
+
381
+ **JSON Escaping Rules:**
382
+ - Wrap content in single quotes: `'{"key": "value"}'`
383
+ - Escape internal single quotes: `'` → `'\''`
384
+ - Example: `'{"title": "It'\''s a test"}'`
385
+
386
+ **⛔ FAILURE TO EXECUTE BASH COMMANDS = DATA LOSS ⛔**
387
+
388
+ ---
389
+
390
+ ### Quiz Already Completed
391
+ ```
392
+ User: "quiz"
393
+ Bot checks: completion_status.quiz_completed_date == today?
394
+ → YES: "You've already completed today's quiz! 🎉 Score: X/Y"
395
+ → NO: Check quiz.json exists (~/.openclaw/state/eng-lang-tutor/daily/YYYY-MM-DD/quiz.json) and quiz.generated == true?
396
+ → YES: Load quiz and present questions
397
+ → NO:
398
+ 1. Check if keypoint.json exists for today
399
+ → If NO: Generate keypoint via LLM first
400
+ → ⛔ EXECUTE: python3 -m scripts.cli.cli save_daily --content-type keypoint --content '<ESCAPED_JSON>'
401
+ 2. Generate quiz via LLM
402
+ 3. Set generated=true in the JSON content
403
+ 4. ⛔ EXECUTE: python3 -m scripts.cli.cli save_daily --content-type quiz --content '<ESCAPED_JSON>'
404
+ 5. Present questions to user
405
+ ```
406
+
407
+ **⛔ CRITICAL: You MUST execute bash commands to save BEFORE presenting to user.**
408
+
409
+ ### Manual Quiz Before Keypoint Push
410
+ ```
411
+ User manually requests quiz before scheduled keypoint push time
412
+ Bot checks: Does keypoint.json exist for today?
413
+ → NO:
414
+ 1. IMMEDIATELY generate keypoint via LLM (do NOT say "will notify later")
415
+ 2. ⛔ EXECUTE: python3 -m scripts.cli.cli save_daily --content-type keypoint --content '<ESCAPED_JSON>'
416
+ 3. Generate quiz via LLM
417
+ 4. ⛔ EXECUTE: python3 -m scripts.cli.cli save_daily --content-type quiz --content '<ESCAPED_JSON>'
418
+ 5. Present quiz questions to user
419
+ → All in ONE response - user should receive quiz immediately
420
+ → YES: Proceed with quiz generation normally (see Quiz Already Completed section)
421
+
422
+ This ensures learning sequence is preserved even for early learners.
423
+ ```
424
+
425
+ **⛔ CRITICAL: NEVER tell user "will generate later and notify" - always generate immediately.**
426
+ **⛔ CRITICAL: You MUST execute the bash save commands BEFORE presenting content to user.**
427
+
428
+ ### Keypoint Query
429
+ ```
430
+ User: "keypoint" or "知识点" or Cron Push
431
+ Bot checks: Does keypoint.json exist for today (~/.openclaw/state/eng-lang-tutor/daily/YYYY-MM-DD/keypoint.json)?
432
+ → NO:
433
+ 1. Generate new keypoint via LLM
434
+ 2. Set generated=true in the JSON content
435
+ 3. ⛔ EXECUTE: python3 -m scripts.cli.cli save_daily --content-type keypoint --content '<ESCAPED_JSON>'
436
+ 4. ⛔ EXECUTE: python3 -m scripts.cli.cli record_view
437
+ 5. Send audio file via message tool (if audio.composed exists)
438
+ 6. Display formatted content to user (REMOVE any [AUDIO:...] tags from text)
439
+ → YES: Check keypoint.generated
440
+ → TRUE: ⛔ EXECUTE: python3 -m scripts.cli.cli record_view, then send audio and display
441
+ → FALSE: Follow steps 1-6 above
442
+ ```
443
+
444
+ **⛔ CRITICAL:**
445
+ 1. **You MUST execute bash commands to save BEFORE displaying to user**
446
+ 2. **You MUST remove `[AUDIO:...]` tags from display text** - these are NOT supported and will show as plain text
447
+ 3. **You SHOULD send audio via message tool** if `audio.composed` field exists
448
+
449
+ **Display Fields (from keypoint.json `display` object):**
450
+
451
+ | Field | Description |
452
+ |-------|-------------|
453
+ | `title` | Main title with emoji |
454
+ | `topic_tag` | Topic label |
455
+ | `formality_tag` | Formality level |
456
+ | `scene_text` | Scene description |
457
+ | `expressions_formatted` | Array of formatted expressions |
458
+ | `alternatives_formatted` | Bullet list of alternatives |
459
+ | `chinglish_formatted` | Wrong/Correct comparison |
460
+ | `examples_formatted` | Array of dialogue examples |
461
+ | `extended_formatted` | Extended learning content |
462
+ | `references_formatted` | Reference links |
463
+ | `footer` | Date and footer info |
464
+
465
+ > **IMPORTANT:**
466
+ > - Output assembled Markdown text directly, NOT JSON
467
+ > - **DO NOT display the `audio` field in text** - it's for message tool media sending only
468
+ > - See `templates/prompt_templates.md` Section 10.3 for full assembly template
469
+
470
+ ### Keypoint History
471
+ ```
472
+ User: "keypoint history" or "知识点 历史"
473
+ Bot scans: ~/.openclaw/state/eng-lang-tutor/daily/ directory for YYYY-MM-DD/keypoint.json files
474
+ → NO files found: "📚 No history yet. Start learning with 'keypoint' today!"
475
+ → YES: List keypoints (most recent first), max 10 entries:
476
+ - {date}: {title/topic} (e.g., "2026-02-20: Touch Base - 工作沟通")
477
+ ```
478
+
479
+ **Display Format:**
480
+ ```markdown
481
+ 📚 **知识点历史记录**
482
+
483
+ | 日期 | 主题 | 查看 |
484
+ |------|------|------|
485
+ | 2026-02-20 | Touch Base - 工作沟通 | 输入 `keypoint 2026-02-20` |
486
+ | 2026-02-19 | Gonna/Wanna - 口语缩写 | 输入 `keypoint 2026-02-19` |
487
+ ...
488
+
489
+ 💡 输入 `keypoint 日期` 查看历史知识点详情
490
+ ```
491
+
492
+ ### Historical Keypoint
493
+ ```
494
+ User: "keypoint 昨天" or "keypoint 2026-02-19"
495
+ Bot checks: Does keypoint.json exist for that date?
496
+ → YES: Display
497
+ → NO: "No keypoint found for that date. Try 'keypoint today'."
498
+ ```
499
+
500
+ ### Config Display
501
+ ```
502
+ User: "config" or "设置"
503
+ Bot reads: state.json preferences
504
+ Output: Card format with formatted text (see Output Format below)
505
+ ```
506
+
507
+ ### Stats Display
508
+ ```
509
+ User: "stats" or "进度"
510
+ Bot reads: state.json user + progress
511
+ Output: Card format with emoji and formatted text
512
+ ```
513
+
514
+ ---
515
+
516
+ ## Response Output Format
517
+
518
+ > IMPORTANT: All responses use platform-agnostic Markdown format. See `templates/prompt_templates.md` Section 10 for detailed formatting rules and `examples/` for sample outputs.
519
+
520
+ ### Quick Reference
521
+
522
+ - **Format**: Standard Markdown (compatible with Feishu, Discord, Telegram, Slack)
523
+ - **Bold**: `**text**`
524
+ - **Links**: `[text](url)`
525
+ - **Emojis**: Use liberally for visual sections
526
+ - **Dividers**: `───────────────────`
527
+
528
+ ### Response Templates
529
+
530
+ All response templates are documented in `templates/prompt_templates.md` Section 10:
531
+
532
+ | Template | Section | Description |
533
+ |----------|---------|-------------|
534
+ | Keypoint Display | 10.3 | Daily knowledge point output |
535
+ | Stats Display | 10.4 | Learning progress stats |
536
+ | Config Display | 10.5 | User settings display |
537
+ | Errors Display | 10.6 | Error notebook (paginated) |
538
+ | Error Review | 10.7 | Error review session flow |
539
+ | Quiz Result | 10.8 | Quiz completion results |
540
+
541
+ ---
542
+
543
+ ## Completion Tracking
544
+
545
+ ### State Fields
546
+
547
+ ```json
548
+ {
549
+ "completion_status": {
550
+ "quiz_completed_date": "2026-02-20",
551
+ "keypoint_view_history": [
552
+ {"date": "2026-02-20", "viewed_at": "2026-02-20T06:45:00"}
553
+ ]
554
+ }
555
+ }
556
+ ```
557
+
558
+ ### Rules
559
+
560
+ 1. **Quiz**: Can only take once per day (resets at midnight)
561
+ 2. **Keypoint**: Can view multiple times (including historical pushed keypoints)
562
+
563
+ ---
564
+
565
+ ## Cron Configuration
566
+
567
+ ### Default Schedule (UTC+8 / Asia/Shanghai)
568
+
569
+ | Task | Default Time | Description |
570
+ |------|--------------|-------------|
571
+ | Keypoint Push | 06:45 | Daily knowledge point |
572
+ | Quiz Push | 22:45 | Daily quiz |
573
+
574
+ ### Crontab Template
575
+
576
+ ```bash
577
+ # /etc/cron.d/eng-lang-tutor
578
+ CRON_TZ=Asia/Shanghai
579
+
580
+ # 6:45 AM - Keypoint push
581
+ 45 6 * * * openclaw agent --channel discord --message "☕ Good morning!" --agent eng-lang-tutor
582
+
583
+ # 10:45 PM - Quiz push
584
+ 45 22 * * * openclaw agent --channel discord --message "🌙 Quiz time!" --agent eng-lang-tutor
585
+ ```
586
+
587
+ ### User-Customizable Schedule
588
+
589
+ Users can customize their schedule via commands:
590
+ ```
591
+ set schedule keypoint 7:00
592
+ set schedule quiz 21:00
593
+ ```
594
+
595
+ Stored in `state.json`:
596
+ ```json
597
+ {
598
+ "schedule": {
599
+ "keypoint_time": "07:00",
600
+ "quiz_time": "21:00",
601
+ "timezone": "Asia/Shanghai"
602
+ }
603
+ }
604
+
605
+ ---
606
+
607
+ ## Additional Scripts
608
+
609
+ | Script | Purpose |
610
+ |--------|---------|
611
+ | cli/command_parser.py | Parse user messages to determine intent |
612
+ | scheduling/cron_push.py | Handle scheduled content generation |
613
+