@rookiestar/eng-lang-tutor 1.0.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.
Potentially problematic release.
This version of @rookiestar/eng-lang-tutor might be problematic. Click here for more details.
- package/CLAUDE.md +259 -0
- package/README.md +224 -0
- package/README_EN.md +224 -0
- package/SKILL.md +495 -0
- package/bin/eng-lang-tutor.js +177 -0
- package/docs/OPENCLAW_DEPLOYMENT.md +228 -0
- package/examples/sample_keypoint.json +130 -0
- package/examples/sample_quiz.json +92 -0
- package/npm-scripts/install.js +132 -0
- package/package.json +46 -0
- package/references/resources.md +292 -0
- package/requirements.txt +9 -0
- package/scripts/command_parser.py +336 -0
- package/scripts/cron_push.py +226 -0
- package/scripts/dedup.py +325 -0
- package/scripts/gamification.py +406 -0
- package/scripts/scorer.py +323 -0
- package/scripts/state_manager.py +1025 -0
- package/scripts/tts/__init__.py +30 -0
- package/scripts/tts/base.py +109 -0
- package/scripts/tts/manager.py +290 -0
- package/scripts/tts/providers/__init__.py +10 -0
- package/scripts/tts/providers/xunfei.py +192 -0
- package/templates/keypoint_schema.json +420 -0
- package/templates/prompt_templates.md +1261 -0
- package/templates/quiz_schema.json +201 -0
- package/templates/state_schema.json +241 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eng-lang-tutor
|
|
3
|
+
description: |
|
|
4
|
+
地道美式英语导师 - 提供每日知识点、Quiz测验等学习内容。
|
|
5
|
+
支持游戏化学习(XP/连胜/等级/徽章)。
|
|
6
|
+
触发场景:学习英语、英语知识点、Quiz、错题本、学习进度。
|
|
7
|
+
被 cron job 调用以实现定期内容推送。
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# American English Tutor
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Teaches authentic American English expressions, avoiding Chinglish patterns.
|
|
15
|
+
Delivers personalized content via daily knowledge points and quizzes.
|
|
16
|
+
Includes Duolingo-style gamification (XP, streaks, levels, badges).
|
|
17
|
+
|
|
18
|
+
## Core Workflow
|
|
19
|
+
|
|
20
|
+
### 1. Daily Knowledge Point Generation
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Input: state.json (user preferences, CEFR level, recent topics)
|
|
24
|
+
Process:
|
|
25
|
+
1. Load user preferences from state.json
|
|
26
|
+
2. Load recent topic fingerprints (14 days) for deduplication
|
|
27
|
+
3. Select topic based on user preference weights
|
|
28
|
+
4. Generate knowledge point via LLM (pure JSON output)
|
|
29
|
+
5. Validate JSON schema
|
|
30
|
+
6. Save to daily/YYYY-MM-DD/keypoint.json
|
|
31
|
+
7. Update state.json recent_topics
|
|
32
|
+
8. Append event to logs/events_YYYY-MM.jsonl
|
|
33
|
+
Output: keypoint.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Quiz Generation
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Input: keypoint.json
|
|
40
|
+
Process:
|
|
41
|
+
1. Read today's keypoint.json
|
|
42
|
+
2. Generate 3 questions (fixed pattern):
|
|
43
|
+
- 1 multiple_choice (10 XP)
|
|
44
|
+
- 1 chinglish_fix (15 XP)
|
|
45
|
+
- 1 fill_blank OR dialogue_completion (12 XP, random)
|
|
46
|
+
3. Save to daily/YYYY-MM-DD/quiz.json (with answers)
|
|
47
|
+
Output: quiz.json (~37 XP total)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Answer Evaluation
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Input: quiz.json, user_answers.json
|
|
54
|
+
Process:
|
|
55
|
+
1. Compare user answers with correct answers
|
|
56
|
+
2. Calculate XP (base + streak multiplier + perfect bonus)
|
|
57
|
+
3. Update state.json (XP, streak, level, badges)
|
|
58
|
+
4. Record wrong answers to error_notebook
|
|
59
|
+
Output: results.json, updated state.json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quiz Types
|
|
63
|
+
|
|
64
|
+
| Type | Description | XP Value | Daily Quiz |
|
|
65
|
+
|------|-------------|----------|------------|
|
|
66
|
+
| multiple_choice | Select correct expression from 4 options | 10 | 1 (required) |
|
|
67
|
+
| chinglish_fix | Identify and correct Chinglish expression | 15 | 1 (required) |
|
|
68
|
+
| fill_blank | Complete dialogue with missing expression | 12 | 0-1 (random) |
|
|
69
|
+
| dialogue_completion | Choose appropriate response in context | 15 | 0-1 (random) |
|
|
70
|
+
|
|
71
|
+
**Daily Quiz Pattern**: 3 questions, ~37 XP, pass with 2/3 correct
|
|
72
|
+
|
|
73
|
+
## Gamification System
|
|
74
|
+
|
|
75
|
+
### XP & Levels
|
|
76
|
+
|
|
77
|
+
This system has two independent level systems:
|
|
78
|
+
- **Ability Level (CEFR)**: A1-C2, determines content difficulty (language proficiency)
|
|
79
|
+
- **Activity Level (Level)**: 1-20, measures engagement depth (usage progression)
|
|
80
|
+
|
|
81
|
+
**Activity Level Stages (Journey):**
|
|
82
|
+
- Level 1-5 (Starter/启程者): 0-350 XP
|
|
83
|
+
- Level 6-10 (Traveler/行路人): 550-2000 XP
|
|
84
|
+
- Level 11-15 (Explorer/探索者): 2600-6000 XP
|
|
85
|
+
- Level 16-20 (Pioneer/开拓者): 7200-15000 XP
|
|
86
|
+
|
|
87
|
+
### Streak System
|
|
88
|
+
- Consecutive days of study builds streak
|
|
89
|
+
- Streak broken if miss a day (unless using streak freeze)
|
|
90
|
+
- Streak multiplier: 1.0 + (streak * 0.05), max 2.0x
|
|
91
|
+
- Streak freeze costs 50 gems
|
|
92
|
+
|
|
93
|
+
### Badges
|
|
94
|
+
- First Steps: Complete first quiz (+10 gems)
|
|
95
|
+
- Week Warrior: 7-day streak (+25 gems)
|
|
96
|
+
- Month Master: 30-day streak (+100 gems)
|
|
97
|
+
- Perfect 10: 10 perfect quizzes (+50 gems)
|
|
98
|
+
- Vocab Hunter: Learn 100 expressions (+75 gems)
|
|
99
|
+
- Error Slayer: Clear 30 errors (+30 gems)
|
|
100
|
+
|
|
101
|
+
## Key Scripts
|
|
102
|
+
|
|
103
|
+
| Script | Purpose |
|
|
104
|
+
|--------|---------|
|
|
105
|
+
| state_manager.py | State persistence, event logging, CLI for content saving |
|
|
106
|
+
| cron_push.py | Scheduled content push (keypoint/quiz placeholders) |
|
|
107
|
+
| scorer.py | Answer evaluation, XP calculation |
|
|
108
|
+
| gamification.py | Streak/level/badge logic |
|
|
109
|
+
| dedup.py | 14-day content deduplication |
|
|
110
|
+
| command_parser.py | Natural language command parsing |
|
|
111
|
+
|
|
112
|
+
## CLI Commands
|
|
113
|
+
|
|
114
|
+
> These bash commands are used by the Agent to execute operations. All commands use `--data-dir data` by default.
|
|
115
|
+
|
|
116
|
+
### Content Management
|
|
117
|
+
```bash
|
|
118
|
+
# Save daily content (keypoint/quiz)
|
|
119
|
+
python3 scripts/state_manager.py save_daily --content-type keypoint --content '<JSON>'
|
|
120
|
+
|
|
121
|
+
# Record keypoint view
|
|
122
|
+
python3 scripts/state_manager.py record_view [--date YYYY-MM-DD]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Stats & Config
|
|
126
|
+
```bash
|
|
127
|
+
# Display learning progress
|
|
128
|
+
python3 scripts/state_manager.py stats
|
|
129
|
+
|
|
130
|
+
# Display current configuration
|
|
131
|
+
python3 scripts/state_manager.py config
|
|
132
|
+
|
|
133
|
+
# Update configuration
|
|
134
|
+
python3 scripts/state_manager.py config --cefr B2
|
|
135
|
+
python3 scripts/state_manager.py config --style professional
|
|
136
|
+
python3 scripts/state_manager.py config --oral-ratio 80
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Error Notebook
|
|
140
|
+
```bash
|
|
141
|
+
# List errors (paginated)
|
|
142
|
+
python3 scripts/state_manager.py errors [--page 1] [--per-page 5] [--month YYYY-MM]
|
|
143
|
+
|
|
144
|
+
# Get random errors for review
|
|
145
|
+
python3 scripts/state_manager.py errors --random 5
|
|
146
|
+
|
|
147
|
+
# Get error statistics
|
|
148
|
+
python3 scripts/state_manager.py errors --stats
|
|
149
|
+
|
|
150
|
+
# Get errors for review session
|
|
151
|
+
python3 scripts/state_manager.py errors --review 5
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Schedule
|
|
155
|
+
```bash
|
|
156
|
+
# Display current schedule
|
|
157
|
+
python3 scripts/state_manager.py schedule
|
|
158
|
+
|
|
159
|
+
# Update schedule (quiz_time must be later than keypoint_time)
|
|
160
|
+
python3 scripts/state_manager.py schedule --keypoint-time 07:00 --quiz-time 21:00
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Core Principles
|
|
164
|
+
|
|
165
|
+
1. **Always output valid JSON** - No markdown, no extra text
|
|
166
|
+
2. **Focus on "How Americans say it"** - NOT translation
|
|
167
|
+
3. **Every knowledge point must include**:
|
|
168
|
+
- Scene context
|
|
169
|
+
- Alternative expressions
|
|
170
|
+
- Chinglish trap + correction
|
|
171
|
+
4. **14-day deduplication** - No repeated topics or expressions
|
|
172
|
+
5. **Topic fingerprints** - Use unique identifiers for deduplication
|
|
173
|
+
|
|
174
|
+
## File Structure
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
data/
|
|
178
|
+
state.json # Core state (streak/xp/preferences)
|
|
179
|
+
logs/
|
|
180
|
+
events_2026-02.jsonl # Monthly event log
|
|
181
|
+
daily/
|
|
182
|
+
2026-02-20/
|
|
183
|
+
keypoint.json # Today's knowledge point
|
|
184
|
+
quiz.json # Today's quiz
|
|
185
|
+
user_answers.json # User's answers
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## JSON Schemas
|
|
189
|
+
|
|
190
|
+
See templates/ directory:
|
|
191
|
+
- state_schema.json
|
|
192
|
+
- keypoint_schema.json
|
|
193
|
+
- quiz_schema.json
|
|
194
|
+
|
|
195
|
+
## Resource References
|
|
196
|
+
|
|
197
|
+
See references/ directory:
|
|
198
|
+
- resources.md - Themed English learning resources (TV shows, news, gaming, sports, workplace, daily life)
|
|
199
|
+
|
|
200
|
+
See templates/ directory:
|
|
201
|
+
- prompt_templates.md - LLM prompt templates for content generation
|
|
202
|
+
|
|
203
|
+
## Examples
|
|
204
|
+
|
|
205
|
+
See examples/ directory for sample outputs.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## User Commands
|
|
210
|
+
|
|
211
|
+
The bot recognizes these natural language commands:
|
|
212
|
+
|
|
213
|
+
### Initialization
|
|
214
|
+
| Command | Aliases | Description |
|
|
215
|
+
|---------|---------|-------------|
|
|
216
|
+
| `start` | `begin`, `开始`, `初始化`, `你好` | Start the onboarding process |
|
|
217
|
+
|
|
218
|
+
### Learning Content
|
|
219
|
+
| Command | Aliases | Description |
|
|
220
|
+
|---------|---------|-------------|
|
|
221
|
+
| `keypoint` | `知识点`, `今天`, `today` | View today's knowledge point |
|
|
222
|
+
| `keypoint history` | `知识点 历史`, `昨天`, `yesterday` | View historical keypoints |
|
|
223
|
+
| `quiz` | `测验`, `test`, `测试` | Take today's quiz (once per day) |
|
|
224
|
+
|
|
225
|
+
### Progress & Stats
|
|
226
|
+
| Command | Aliases | Description |
|
|
227
|
+
|---------|---------|-------------|
|
|
228
|
+
| `stats` | `进度`, `统计`, `level`, `XP` | View learning progress |
|
|
229
|
+
| `errors` | `错题本`, `mistakes` | View error notebook (recent 5) |
|
|
230
|
+
| `errors more` | `错题本 更多` | Next 5 errors |
|
|
231
|
+
| `errors 2026-02` | `错题本 2026-02` | Filter by month |
|
|
232
|
+
| `errors random 5` | `错题本 随机5` | Random 5 for review |
|
|
233
|
+
| `errors stats` | `错题本 统计` | Show error statistics |
|
|
234
|
+
| `errors review` | `错题本 复习`, `错题复习` | Start error review session (5 questions) |
|
|
235
|
+
|
|
236
|
+
### Settings
|
|
237
|
+
| Command | Aliases | Description |
|
|
238
|
+
|---------|---------|-------------|
|
|
239
|
+
| `config` | `设置`, `preferences` | View current settings |
|
|
240
|
+
| `set level B2` | `设置等级 B2` | Change CEFR level |
|
|
241
|
+
| `set style professional` | `设置风格 专业` | Change tutor style |
|
|
242
|
+
| `schedule` | `时间表`, `推送时间` | View/change schedule |
|
|
243
|
+
|
|
244
|
+
### Help
|
|
245
|
+
| Command | Aliases | Description |
|
|
246
|
+
|---------|---------|-------------|
|
|
247
|
+
| `help` | `帮助`, `怎么用` | Show available commands |
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Initialization Flow
|
|
252
|
+
|
|
253
|
+
New users go through a 6-step onboarding process:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
Step 0: Welcome → User replies "start"
|
|
257
|
+
Step 1: Select CEFR Level (A1-C2)
|
|
258
|
+
Step 2: Select Topic Interests (movies/news/gaming/sports/workplace/social/daily_life)
|
|
259
|
+
Step 3: Select Tutor Style (humorous/rigorous/casual/professional)
|
|
260
|
+
Step 4: Select Oral/Written Ratio (0-100% oral)
|
|
261
|
+
Step 5: Configure Schedule (keypoint time, quiz time) - Quiz must be later than keypoint
|
|
262
|
+
Step 6: Confirm Configuration → Set initialized=true + Create cron jobs
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Step 6 Completion Actions:**
|
|
266
|
+
When user confirms with "yes":
|
|
267
|
+
1. Set `initialized=true` via `state_manager.py`
|
|
268
|
+
2. Create cron jobs for keypoint and quiz push times:
|
|
269
|
+
```bash
|
|
270
|
+
# Parse schedule from state.json
|
|
271
|
+
# Keypoint job: {minute} {hour} * * * openclaw system event --text "Use eng-lang-tutor skill. Push today's keypoint." --mode now
|
|
272
|
+
# Quiz job: {minute} {hour} * * * openclaw system event --text "Use eng-lang-tutor skill. Push today's quiz invitation." --mode now
|
|
273
|
+
```
|
|
274
|
+
3. Log the completion event
|
|
275
|
+
|
|
276
|
+
**State Fields:**
|
|
277
|
+
- `initialized`: Boolean - Whether user completed onboarding
|
|
278
|
+
- `onboarding_step`: Integer (0-6) - Current step in onboarding
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Response Scenarios
|
|
283
|
+
|
|
284
|
+
> **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.
|
|
285
|
+
|
|
286
|
+
### Quiz Already Completed
|
|
287
|
+
```
|
|
288
|
+
User: "quiz"
|
|
289
|
+
Bot checks: completion_status.quiz_completed_date == today?
|
|
290
|
+
→ YES: "You've already completed today's quiz! 🎉 Score: X/Y"
|
|
291
|
+
→ NO: Check quiz.json exists (data/daily/YYYY-MM-DD/quiz.json) and quiz.generated == true?
|
|
292
|
+
→ YES: Load quiz and present questions
|
|
293
|
+
→ NO: Generate quiz via LLM, then:
|
|
294
|
+
1. Set generated=true in the JSON content
|
|
295
|
+
2. Save via bash: python3 scripts/state_manager.py save_daily --content-type quiz --content '<ESCAPED_JSON>'
|
|
296
|
+
3. Present questions
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**CRITICAL:**
|
|
300
|
+
1. After LLM generation, MUST update quiz JSON with `"generated": true` to prevent re-generation
|
|
301
|
+
2. MUST save via bash command:
|
|
302
|
+
```bash
|
|
303
|
+
python3 scripts/state_manager.py save_daily --content-type quiz --content '<ESCAPED_JSON>'
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Keypoint Query
|
|
307
|
+
```
|
|
308
|
+
User: "keypoint" or "知识点" or Cron Push
|
|
309
|
+
Bot checks: Does keypoint.json exist for today (data/daily/YYYY-MM-DD/keypoint.json)?
|
|
310
|
+
→ NO: Generate new keypoint via LLM, then:
|
|
311
|
+
1. Set generated=true in the JSON content
|
|
312
|
+
2. Save via bash: python3 scripts/state_manager.py save_daily --content-type keypoint --content '<ESCAPED_JSON>'
|
|
313
|
+
3. Record view via bash: python3 scripts/state_manager.py record_view
|
|
314
|
+
4. Display formatted content
|
|
315
|
+
→ YES: Check keypoint.generated
|
|
316
|
+
→ TRUE: Record view via bash, then read display object and output as Markdown
|
|
317
|
+
→ FALSE: Generate content via LLM, then follow steps 1-4 above
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**CRITICAL:**
|
|
321
|
+
1. After LLM generation, MUST update keypoint JSON with `"generated": true` to prevent re-generation
|
|
322
|
+
2. MUST save via bash command:
|
|
323
|
+
```bash
|
|
324
|
+
python3 scripts/state_manager.py save_daily --content-type keypoint --content '<ESCAPED_JSON>'
|
|
325
|
+
```
|
|
326
|
+
3. MUST record view after displaying:
|
|
327
|
+
```bash
|
|
328
|
+
python3 scripts/state_manager.py record_view
|
|
329
|
+
```
|
|
330
|
+
4. JSON content must be properly escaped:
|
|
331
|
+
- Wrap in single quotes
|
|
332
|
+
- Escape internal single quotes: `'` → `'\''`
|
|
333
|
+
|
|
334
|
+
**Display Fields (from keypoint.json `display` object):**
|
|
335
|
+
|
|
336
|
+
| Field | Description |
|
|
337
|
+
|-------|-------------|
|
|
338
|
+
| `title` | Main title with emoji |
|
|
339
|
+
| `topic_tag` | Topic label |
|
|
340
|
+
| `formality_tag` | Formality level |
|
|
341
|
+
| `scene_text` | Scene description |
|
|
342
|
+
| `expressions_formatted` | Array of formatted expressions |
|
|
343
|
+
| `alternatives_formatted` | Bullet list of alternatives |
|
|
344
|
+
| `chinglish_formatted` | Wrong/Correct comparison |
|
|
345
|
+
| `examples_formatted` | Array of dialogue examples |
|
|
346
|
+
| `extended_formatted` | Extended learning content |
|
|
347
|
+
| `references_formatted` | Reference links |
|
|
348
|
+
| `footer` | Date and footer info |
|
|
349
|
+
|
|
350
|
+
> **IMPORTANT:** Output assembled Markdown text directly, NOT JSON. See `templates/prompt_templates.md` Section 10.3 for full assembly template.
|
|
351
|
+
|
|
352
|
+
### Keypoint History
|
|
353
|
+
```
|
|
354
|
+
User: "keypoint history" or "知识点 历史"
|
|
355
|
+
Bot scans: data/daily/ directory for YYYY-MM-DD/keypoint.json files
|
|
356
|
+
→ NO files found: "📚 No history yet. Start learning with 'keypoint' today!"
|
|
357
|
+
→ YES: List keypoints (most recent first), max 10 entries:
|
|
358
|
+
- {date}: {title/topic} (e.g., "2026-02-20: Touch Base - 工作沟通")
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Display Format:**
|
|
362
|
+
```markdown
|
|
363
|
+
📚 **知识点历史记录**
|
|
364
|
+
|
|
365
|
+
| 日期 | 主题 | 查看 |
|
|
366
|
+
|------|------|------|
|
|
367
|
+
| 2026-02-20 | Touch Base - 工作沟通 | 输入 `keypoint 2026-02-20` |
|
|
368
|
+
| 2026-02-19 | Gonna/Wanna - 口语缩写 | 输入 `keypoint 2026-02-19` |
|
|
369
|
+
...
|
|
370
|
+
|
|
371
|
+
💡 输入 `keypoint 日期` 查看历史知识点详情
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Historical Keypoint
|
|
375
|
+
```
|
|
376
|
+
User: "keypoint 昨天" or "keypoint 2026-02-19"
|
|
377
|
+
Bot checks: Does keypoint.json exist for that date?
|
|
378
|
+
→ YES: Display
|
|
379
|
+
→ NO: "No keypoint found for that date. Try 'keypoint today'."
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Config Display
|
|
383
|
+
```
|
|
384
|
+
User: "config" or "设置"
|
|
385
|
+
Bot reads: state.json preferences
|
|
386
|
+
Output: Card format with formatted text (see Output Format below)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Stats Display
|
|
390
|
+
```
|
|
391
|
+
User: "stats" or "进度"
|
|
392
|
+
Bot reads: state.json user + progress
|
|
393
|
+
Output: Card format with emoji and formatted text
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Response Output Format
|
|
399
|
+
|
|
400
|
+
> IMPORTANT: All responses use platform-agnostic Markdown format. See `templates/prompt_templates.md` Section 10 for detailed formatting rules and `examples/` for sample outputs.
|
|
401
|
+
|
|
402
|
+
### Quick Reference
|
|
403
|
+
|
|
404
|
+
- **Format**: Standard Markdown (compatible with Feishu, Discord, Telegram, Slack)
|
|
405
|
+
- **Bold**: `**text**`
|
|
406
|
+
- **Links**: `[text](url)`
|
|
407
|
+
- **Emojis**: Use liberally for visual sections
|
|
408
|
+
- **Dividers**: `───────────────────`
|
|
409
|
+
|
|
410
|
+
### Response Templates
|
|
411
|
+
|
|
412
|
+
All response templates are documented in `templates/prompt_templates.md` Section 10:
|
|
413
|
+
|
|
414
|
+
| Template | Section | Description |
|
|
415
|
+
|----------|---------|-------------|
|
|
416
|
+
| Keypoint Display | 10.3 | Daily knowledge point output |
|
|
417
|
+
| Stats Display | 10.4 | Learning progress stats |
|
|
418
|
+
| Config Display | 10.5 | User settings display |
|
|
419
|
+
| Errors Display | 10.6 | Error notebook (paginated) |
|
|
420
|
+
| Error Review | 10.7 | Error review session flow |
|
|
421
|
+
| Quiz Result | 10.8 | Quiz completion results |
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Completion Tracking
|
|
426
|
+
|
|
427
|
+
### State Fields
|
|
428
|
+
|
|
429
|
+
```json
|
|
430
|
+
{
|
|
431
|
+
"completion_status": {
|
|
432
|
+
"quiz_completed_date": "2026-02-20",
|
|
433
|
+
"keypoint_view_history": [
|
|
434
|
+
{"date": "2026-02-20", "viewed_at": "2026-02-20T06:45:00"}
|
|
435
|
+
]
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Rules
|
|
441
|
+
|
|
442
|
+
1. **Quiz**: Can only take once per day (resets at midnight)
|
|
443
|
+
2. **Keypoint**: Can view multiple times (including historical pushed keypoints)
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Cron Configuration
|
|
448
|
+
|
|
449
|
+
### Default Schedule (UTC+8 / Asia/Shanghai)
|
|
450
|
+
|
|
451
|
+
| Task | Default Time | Description |
|
|
452
|
+
|------|--------------|-------------|
|
|
453
|
+
| Keypoint Push | 06:45 | Daily knowledge point |
|
|
454
|
+
| Quiz Push | 22:45 | Daily quiz |
|
|
455
|
+
|
|
456
|
+
### Crontab Template
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# /etc/cron.d/eng-lang-tutor
|
|
460
|
+
CRON_TZ=Asia/Shanghai
|
|
461
|
+
|
|
462
|
+
# 6:45 AM - Keypoint push
|
|
463
|
+
45 6 * * * openclaw agent --channel discord --message "☕ Good morning!" --agent eng-lang-tutor
|
|
464
|
+
|
|
465
|
+
# 10:45 PM - Quiz push
|
|
466
|
+
45 22 * * * openclaw agent --channel discord --message "🌙 Quiz time!" --agent eng-lang-tutor
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### User-Customizable Schedule
|
|
470
|
+
|
|
471
|
+
Users can customize their schedule via commands:
|
|
472
|
+
```
|
|
473
|
+
set schedule keypoint 7:00
|
|
474
|
+
set schedule quiz 21:00
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Stored in `state.json`:
|
|
478
|
+
```json
|
|
479
|
+
{
|
|
480
|
+
"schedule": {
|
|
481
|
+
"keypoint_time": "07:00",
|
|
482
|
+
"quiz_time": "21:00",
|
|
483
|
+
"timezone": "Asia/Shanghai"
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Additional Scripts
|
|
490
|
+
|
|
491
|
+
| Script | Purpose |
|
|
492
|
+
|--------|---------|
|
|
493
|
+
| command_parser.py | Parse user messages to determine intent |
|
|
494
|
+
| cron_push.py | Handle scheduled content generation |
|
|
495
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* eng-lang-tutor CLI
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* install - Install the skill to ~/.openclaw/skills/eng-lang-tutor/
|
|
8
|
+
* uninstall - Remove the skill from ~/.openclaw/skills/eng-lang-tutor/
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
|
|
15
|
+
const SKILL_NAME = 'eng-lang-tutor';
|
|
16
|
+
const SKILLS_DIR = path.join(os.homedir(), '.openclaw', 'skills');
|
|
17
|
+
const SKILL_TARGET = path.join(SKILLS_DIR, SKILL_NAME);
|
|
18
|
+
|
|
19
|
+
// Get the package root directory (where package.json is)
|
|
20
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
21
|
+
|
|
22
|
+
function getSkillSourceDir() {
|
|
23
|
+
return PACKAGE_ROOT;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function install() {
|
|
27
|
+
console.log(`Installing ${SKILL_NAME} skill...`);
|
|
28
|
+
|
|
29
|
+
const sourceDir = getSkillSourceDir();
|
|
30
|
+
|
|
31
|
+
// Check if source directory exists
|
|
32
|
+
if (!fs.existsSync(sourceDir)) {
|
|
33
|
+
console.error(`Error: Source directory not found: ${sourceDir}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create skills directory if it doesn't exist
|
|
38
|
+
if (!fs.existsSync(SKILLS_DIR)) {
|
|
39
|
+
fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
40
|
+
console.log(`Created skills directory: ${SKILLS_DIR}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove existing installation if present
|
|
44
|
+
if (fs.existsSync(SKILL_TARGET)) {
|
|
45
|
+
console.log(`Removing existing installation at ${SKILL_TARGET}...`);
|
|
46
|
+
fs.rmSync(SKILL_TARGET, { recursive: true, force: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create target directory
|
|
50
|
+
fs.mkdirSync(SKILL_TARGET, { recursive: true });
|
|
51
|
+
|
|
52
|
+
// Files and directories to copy
|
|
53
|
+
const itemsToCopy = [
|
|
54
|
+
'scripts',
|
|
55
|
+
'templates',
|
|
56
|
+
'references',
|
|
57
|
+
'examples',
|
|
58
|
+
'docs',
|
|
59
|
+
'SKILL.md',
|
|
60
|
+
'CLAUDE.md',
|
|
61
|
+
'README.md',
|
|
62
|
+
'README_EN.md',
|
|
63
|
+
'requirements.txt'
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// Copy each item
|
|
67
|
+
for (const item of itemsToCopy) {
|
|
68
|
+
const sourcePath = path.join(sourceDir, item);
|
|
69
|
+
const targetPath = path.join(SKILL_TARGET, item);
|
|
70
|
+
|
|
71
|
+
if (!fs.existsSync(sourcePath)) {
|
|
72
|
+
console.log(` Skipping ${item} (not found)`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
78
|
+
copyDir(sourcePath, targetPath);
|
|
79
|
+
} else {
|
|
80
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
81
|
+
}
|
|
82
|
+
console.log(` Copied ${item}`);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error(` Error copying ${item}: ${err.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(`\n✓ ${SKILL_NAME} installed to ${SKILL_TARGET}`);
|
|
89
|
+
console.log('\nNext steps:');
|
|
90
|
+
console.log(' 1. Install Python dependencies:');
|
|
91
|
+
console.log(` pip install -r ${SKILL_TARGET}/requirements.txt`);
|
|
92
|
+
console.log(' 2. Restart your OpenClaw agent');
|
|
93
|
+
console.log(' 3. Configure the skill through the onboarding flow');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function uninstall() {
|
|
97
|
+
console.log(`Uninstalling ${SKILL_NAME} skill...`);
|
|
98
|
+
|
|
99
|
+
if (!fs.existsSync(SKILL_TARGET)) {
|
|
100
|
+
console.log(`${SKILL_NAME} is not installed.`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
fs.rmSync(SKILL_TARGET, { recursive: true, force: true });
|
|
106
|
+
console.log(`✓ ${SKILL_NAME} removed from ${SKILL_TARGET}`);
|
|
107
|
+
console.log('\nNote: Your learning data is preserved at:');
|
|
108
|
+
console.log(' ~/.openclaw/state/eng-lang-tutor/');
|
|
109
|
+
console.log('\nTo completely remove all data, run:');
|
|
110
|
+
console.log(' rm -rf ~/.openclaw/state/eng-lang-tutor');
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error(`Error uninstalling: ${err.message}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function copyDir(src, dest) {
|
|
118
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
119
|
+
|
|
120
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
121
|
+
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
const srcPath = path.join(src, entry.name);
|
|
124
|
+
const destPath = path.join(dest, entry.name);
|
|
125
|
+
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
copyDir(srcPath, destPath);
|
|
128
|
+
} else {
|
|
129
|
+
fs.copyFileSync(srcPath, destPath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function showHelp() {
|
|
135
|
+
console.log(`
|
|
136
|
+
eng-lang-tutor - English Language Tutor Skill for OpenClaw
|
|
137
|
+
|
|
138
|
+
Usage:
|
|
139
|
+
eng-lang-tutor <command>
|
|
140
|
+
|
|
141
|
+
Commands:
|
|
142
|
+
install Install the skill to ~/.openclaw/skills/eng-lang-tutor/
|
|
143
|
+
uninstall Remove the skill (preserves learning data)
|
|
144
|
+
help Show this help message
|
|
145
|
+
|
|
146
|
+
Environment Variables:
|
|
147
|
+
OPENCLAW_STATE_DIR Custom directory for learning data
|
|
148
|
+
(default: ~/.openclaw/state/eng-lang-tutor)
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
eng-lang-tutor install
|
|
152
|
+
eng-lang-tutor uninstall
|
|
153
|
+
`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Main
|
|
157
|
+
const command = process.argv[2];
|
|
158
|
+
|
|
159
|
+
switch (command) {
|
|
160
|
+
case 'install':
|
|
161
|
+
install();
|
|
162
|
+
break;
|
|
163
|
+
case 'uninstall':
|
|
164
|
+
uninstall();
|
|
165
|
+
break;
|
|
166
|
+
case 'help':
|
|
167
|
+
case '--help':
|
|
168
|
+
case '-h':
|
|
169
|
+
showHelp();
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
if (command) {
|
|
173
|
+
console.error(`Unknown command: ${command}`);
|
|
174
|
+
}
|
|
175
|
+
showHelp();
|
|
176
|
+
process.exit(command ? 1 : 0);
|
|
177
|
+
}
|