@qnroa/qtype 0.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.
- package/CHANGELOG.md +97 -0
- package/CHANGELOG.zh.md +82 -0
- package/LICENSE +201 -0
- package/README.md +113 -0
- package/README.zh.md +114 -0
- package/dist/cli/bootstrap.js +190 -0
- package/dist/cli/commands/check/action.js +56 -0
- package/dist/cli/commands/check/i18n.js +26 -0
- package/dist/cli/commands/check/index.js +20 -0
- package/dist/cli/commands/config/action.js +236 -0
- package/dist/cli/commands/config/i18n.js +64 -0
- package/dist/cli/commands/config/index.js +72 -0
- package/dist/cli/commands/content/index.js +22 -0
- package/dist/cli/commands/content/lint/action.js +102 -0
- package/dist/cli/commands/content/lint/i18n.js +32 -0
- package/dist/cli/commands/content/lint/index.js +19 -0
- package/dist/cli/commands/content/search/action.js +64 -0
- package/dist/cli/commands/content/search/i18n.js +20 -0
- package/dist/cli/commands/content/search/index.js +24 -0
- package/dist/cli/commands/content/validate/action.js +145 -0
- package/dist/cli/commands/content/validate/i18n.js +32 -0
- package/dist/cli/commands/content/validate/index.js +19 -0
- package/dist/cli/commands/index.js +19 -0
- package/dist/cli/commands/init/index.js +53 -0
- package/dist/cli/commands/pack/i18n.js +20 -0
- package/dist/cli/commands/pack/index.js +48 -0
- package/dist/cli/commands/plugin/i18n.js +28 -0
- package/dist/cli/commands/plugin/index.js +41 -0
- package/dist/cli/commands/publish/build/action.js +208 -0
- package/dist/cli/commands/publish/build/i18n.js +50 -0
- package/dist/cli/commands/publish/build/index.js +24 -0
- package/dist/cli/commands/publish/build/mermaidPass.js +93 -0
- package/dist/cli/commands/publish/build/mermaidPrerender.js +199 -0
- package/dist/cli/commands/publish/index.js +21 -0
- package/dist/cli/commands/publish/init/action.js +74 -0
- package/dist/cli/commands/publish/init/i18n.js +22 -0
- package/dist/cli/commands/publish/init/index.js +26 -0
- package/dist/cli/commands/publish/password/action.js +158 -0
- package/dist/cli/commands/publish/password/i18n.js +44 -0
- package/dist/cli/commands/publish/password/index.js +35 -0
- package/dist/cli/commands/publish/serve/action.js +117 -0
- package/dist/cli/commands/publish/serve/i18n.js +32 -0
- package/dist/cli/commands/publish/serve/index.js +37 -0
- package/dist/cli/commands/repo/index.js +19 -0
- package/dist/cli/commands/repo/new/action.js +98 -0
- package/dist/cli/commands/repo/new/i18n.js +46 -0
- package/dist/cli/commands/repo/new/index.js +57 -0
- package/dist/cli/commands/repo/sync/action.js +202 -0
- package/dist/cli/commands/repo/sync/i18n.js +44 -0
- package/dist/cli/commands/repo/sync/index.js +47 -0
- package/dist/cli/commands/repo/templates.js +87 -0
- package/dist/cli/i18n/index.js +141 -0
- package/dist/cli/i18n/shared.js +142 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/registry/command.js +1 -0
- package/dist/cli/registry/commander-i18n.js +76 -0
- package/dist/cli/registry/help.js +142 -0
- package/dist/cli/registry/mount.js +71 -0
- package/dist/cli/utils/output.js +92 -0
- package/dist/cli/utils/paths.js +13 -0
- package/dist/cli/utils/prompt.js +72 -0
- package/dist/cli/utils/repoGuard.js +41 -0
- package/dist/core/encrypt.js +113 -0
- package/dist/core/index.js +8 -0
- package/dist/core/keystroke/asciiMap.js +39 -0
- package/dist/core/keystroke/chineseMap.js +32 -0
- package/dist/core/keystroke/fullwidthMap.js +41 -0
- package/dist/core/keystroke/index.js +22 -0
- package/dist/core/keystroke/textNormalize.js +68 -0
- package/dist/core/keystroke/types.js +1 -0
- package/dist/core/parser/frontmatter.js +58 -0
- package/dist/core/parser/index.js +2 -0
- package/dist/core/parser/parseMaterial.js +42 -0
- package/dist/core/types.js +1 -0
- package/dist/core/typing/engine.js +112 -0
- package/dist/core/typing/index.js +2 -0
- package/dist/core/typing/metrics.js +20 -0
- package/dist/store/index.js +44 -0
- package/dist/store/kv.js +276 -0
- package/dist/store/storage/browser.js +61 -0
- package/dist/store/storage/json.js +113 -0
- package/dist/store/storage/memory.js +24 -0
- package/dist/store/types.js +47 -0
- package/dist/store/validators.js +71 -0
- package/dist/view/assets/index-DBABH3dG.js +111 -0
- package/dist/view/assets/index-RTLZ-zOC.css +1 -0
- package/dist/view/index.html +14 -0
- package/dist/view/qtype-logo.svg +49 -0
- package/dist/view/qtype-mark.svg +21 -0
- package/docs/README.md +23 -0
- package/docs/README.zh.md +20 -0
- package/docs/en/authoring.md +138 -0
- package/docs/en/cli-reference.md +171 -0
- package/docs/zh/authoring.md +131 -0
- package/docs/zh/cli-reference.md +162 -0
- package/package.json +85 -0
- package/templates/en/QTYPE.md +618 -0
- package/templates/en/README.md +64 -0
- package/templates/en/TODO.md +7 -0
- package/templates/en/index.md +7 -0
- package/templates/en/log.md +3 -0
- package/templates/en/material/.gitkeep +0 -0
- package/templates/en/material/example.md +47 -0
- package/templates/zh/QTYPE.md +618 -0
- package/templates/zh/README.md +64 -0
- package/templates/zh/TODO.md +7 -0
- package/templates/zh/index.md +7 -0
- package/templates/zh/log.md +3 -0
- package/templates/zh/material/.gitkeep +0 -0
- package/templates/zh/material/example.md +47 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
# QTYPE — LLM Typing-Practice Card Spec
|
|
2
|
+
|
|
3
|
+
You maintain a typing-practice card repository. This file defines the **rules** and **operations**. On entering the repo, read this file end-to-end once, then execute by the rules.
|
|
4
|
+
|
|
5
|
+
qtype is a typing-practice tool driven by Markdown source and typed via the OS keyboard (Chinese IME supported). The user picks topics or drops raw material; the Agent reads, distills, splits into cards, files, and maintains.
|
|
6
|
+
|
|
7
|
+
The whole document is organized into four modules: **Concepts / Format / Operations / Rules**.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Concepts
|
|
12
|
+
|
|
13
|
+
### Philosophy
|
|
14
|
+
|
|
15
|
+
- The user picks **topics** or **drops materials**. You **read, distill, cut into cards, file, and maintain**.
|
|
16
|
+
- You are not a Q&A bot — you are the **author of a typing-practice card library**. Every card must be something a real person can actually type.
|
|
17
|
+
- `raw/` is where the user drops source material (optional dir). **Never modify its contents**.
|
|
18
|
+
- Each `.md` file under `material/` is one deck, **fully maintained by you** (except cards marked as hand-edited).
|
|
19
|
+
- Cards are not arbitrary Q&A pairs — they are **strings a human can type on an OS keyboard** (or via Chinese IME).
|
|
20
|
+
|
|
21
|
+
### Terminology
|
|
22
|
+
|
|
23
|
+
| Term | Definition |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Repo | A standalone qtype directory (e.g. `my-typing-deck/`) |
|
|
26
|
+
| Deck | A single `.md` file under `material/`, containing many cards |
|
|
27
|
+
| Card | One `## Q:` + `## A:` unit, preceded by a frontmatter block |
|
|
28
|
+
| Tag | Category marker on a card or deck; used for filtering and index grouping |
|
|
29
|
+
| Word card | `type: word`; drills spelling and word form; requires `phonetic` and `gloss` |
|
|
30
|
+
| Sentence card | `type: sentence`; drills continuous typing of a sentence; requires `translation` |
|
|
31
|
+
| Normal card | `type: normal` (default); Q is a prompt, A is the answer; most free-form |
|
|
32
|
+
| Raw material | Source file under `raw/` (optional); used by ingest to derive cards |
|
|
33
|
+
| Log | `log.md`, append-only timeline |
|
|
34
|
+
| Review progress | Local practice progress produced by the Web UI (not the Agent's concern) |
|
|
35
|
+
|
|
36
|
+
### Directory Layout
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<repo-root>/
|
|
40
|
+
├── QTYPE.md # this file (rules and operations; language chosen at repo creation)
|
|
41
|
+
├── AGENTS.md # symlink → QTYPE.md
|
|
42
|
+
├── CLAUDE.md # symlink → QTYPE.md
|
|
43
|
+
├── GEMINI.md # symlink → QTYPE.md
|
|
44
|
+
├── README.md # repo intro (maintained by human)
|
|
45
|
+
├── TODO.md # backlog (maintained by todo command)
|
|
46
|
+
├── index.md # table of contents of all decks, grouped by tag
|
|
47
|
+
├── log.md # append-only timeline
|
|
48
|
+
├── qtype.config.json # user-tunable settings (theme, target WPM, hint mode, ...)
|
|
49
|
+
├── material/ # decks you write
|
|
50
|
+
│ ├── .gitkeep
|
|
51
|
+
│ ├── <slug>.md # one deck per file
|
|
52
|
+
│ └── assets/ # images / audio referenced by decks
|
|
53
|
+
│ └── <deck-slug>/
|
|
54
|
+
│ └── xxx.png
|
|
55
|
+
└── raw/ # (optional) source materials dropped by the user
|
|
56
|
+
├── .gitkeep
|
|
57
|
+
├── YYYY-MM-DD-<slug>.md
|
|
58
|
+
└── assets/
|
|
59
|
+
└── <raw-slug>/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Rules:
|
|
63
|
+
|
|
64
|
+
- Under `material/`, all deck files are **flat**. Only one subdirectory is allowed: `assets/` (for images and other binaries referenced by decks).
|
|
65
|
+
- Under `raw/`, all source files are **flat**. Only one subdirectory is allowed: `assets/`.
|
|
66
|
+
- `material/assets/<deck-slug>/` and `raw/assets/<raw-slug>/`: grouped by the owning md file's slug, to avoid global name collisions.
|
|
67
|
+
- **On first operation, sanity-check the skeleton**: the user normally initializes. If the Agent enters a repo and finds `material/` / `index.md` / `log.md` missing, create them by spec. When creating `material/` or `raw/`, drop a `.gitkeep`.
|
|
68
|
+
- `raw/` is optional — if the user never drops materials, don't create it.
|
|
69
|
+
- Only create the directories and files specified here. **Never** create anything outside the rules.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Format
|
|
74
|
+
|
|
75
|
+
### Naming
|
|
76
|
+
|
|
77
|
+
**Deck filename**
|
|
78
|
+
|
|
79
|
+
- Path: `material/<slug>.md`.
|
|
80
|
+
- `<slug>`: kebab-case of the topic, lowercase, ASCII letters and hyphens, ≤ 60 chars. For Chinese topics use an English translation or pinyin.
|
|
81
|
+
- On collision, append `-2` / `-3`.
|
|
82
|
+
- Examples: `gre-vocab-list-3.md`, `react-hooks-quiz.md`, `toefl-idioms.md`.
|
|
83
|
+
|
|
84
|
+
**Raw filename**
|
|
85
|
+
|
|
86
|
+
- Path: `raw/YYYY-MM-DD-<slug>.md`.
|
|
87
|
+
- `YYYY-MM-DD`: the date the material was collected into this repo.
|
|
88
|
+
- `<slug>`: kebab-case of the source title.
|
|
89
|
+
- Untitled paste: `YYYY-MM-DD-note-<N>.md`.
|
|
90
|
+
|
|
91
|
+
**Assets naming**
|
|
92
|
+
|
|
93
|
+
- `material/assets/<deck-slug>/` and `raw/assets/<raw-slug>/`: grouped by the owning file's slug; consistent slug means one directory.
|
|
94
|
+
|
|
95
|
+
### Links
|
|
96
|
+
|
|
97
|
+
**Use Markdown links only, never Wikilinks.**
|
|
98
|
+
|
|
99
|
+
| Context | Path form | Example |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| Deck references another deck | `./<slug>.md` | `[GRE Frequent](./gre-vocab-list-3.md)` |
|
|
102
|
+
| Deck references a raw file | `../raw/<file>.md` | `[Source](../raw/2026-08-14-mdn-closures.md)` |
|
|
103
|
+
| Deck references image in material/assets | `./assets/<deck-slug>/<file>` | `` |
|
|
104
|
+
| Deck references image in raw/assets | `../raw/assets/<raw-slug>/<file>` | `` |
|
|
105
|
+
| index.md / log.md reference a deck | From repo root | `material/gre-vocab-list-3.md` |
|
|
106
|
+
|
|
107
|
+
Rules:
|
|
108
|
+
|
|
109
|
+
- **Never** use `[[wikilink]]` syntax.
|
|
110
|
+
- **Never** use absolute paths. Every link must start with `./` or `../`.
|
|
111
|
+
- Link targets must actually exist.
|
|
112
|
+
|
|
113
|
+
### Deck-level frontmatter
|
|
114
|
+
|
|
115
|
+
Each deck file begins with one YAML frontmatter block describing the whole deck:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
---
|
|
119
|
+
title: <deck title>
|
|
120
|
+
language: <target language ISO code, e.g. en / zh / ja>
|
|
121
|
+
tags: [<tag-1>, <tag-2>]
|
|
122
|
+
created: YYYY-MM-DD
|
|
123
|
+
updated: YYYY-MM-DD
|
|
124
|
+
---
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Fields:
|
|
128
|
+
|
|
129
|
+
- `title`: deck title. Human-readable; can contain non-ASCII.
|
|
130
|
+
- `language`: the primary language of the answer text (drives IME hint and input expectations). Common values: `en`, `zh`, `ja`, `mixed`.
|
|
131
|
+
- `tags`: array or space-separated string. At least one; used for grouping in `index.md`.
|
|
132
|
+
- `created` / `updated`: dates. Bump `updated` on any content change.
|
|
133
|
+
|
|
134
|
+
Directly after the deck frontmatter comes the first card's own frontmatter (separated by a `---` line). No deck-level body prose — put commentary in README or the index.
|
|
135
|
+
|
|
136
|
+
### Card frontmatter
|
|
137
|
+
|
|
138
|
+
Each card starts with its own frontmatter block. Fields:
|
|
139
|
+
|
|
140
|
+
| Field | Values | Applies to | Notes |
|
|
141
|
+
|---|---|---|---|
|
|
142
|
+
| `type` | `normal` \| `word` \| `sentence` | All | Default `normal`. |
|
|
143
|
+
| `tags` | Array or space-separated string | Optional | Card-level tags; can overlap with deck tags. |
|
|
144
|
+
| `difficulty` | Integer 1..5 | Optional | 1=trivial, 5=hardest. Default 3. |
|
|
145
|
+
| `hint` | `none` \| `ghost` | Optional | Overrides the global hint mode. Omit = follow global. |
|
|
146
|
+
| `phonetic` | IPA string, `/…/` or `[…]` | Word cards | e.g. `/əˈbæn.dən/`. |
|
|
147
|
+
| `gloss` | Native-language gloss (typically simplified Chinese) | Word cards | e.g. `抛弃, 放弃`. |
|
|
148
|
+
| `translation` | Translation of the sentence | Sentence cards | Meaning-preserving; not word-for-word. |
|
|
149
|
+
|
|
150
|
+
Rules:
|
|
151
|
+
|
|
152
|
+
- Word cards **must** have `phonetic` and `gloss`.
|
|
153
|
+
- Sentence cards **must** have `translation`.
|
|
154
|
+
- Normal cards may omit all optional fields.
|
|
155
|
+
- Fields not listed above are not allowed — this keeps the format stable.
|
|
156
|
+
|
|
157
|
+
### Card structure
|
|
158
|
+
|
|
159
|
+
A card consists of frontmatter, a `## Q:` line, and a `## A:` line:
|
|
160
|
+
|
|
161
|
+
```md
|
|
162
|
+
---
|
|
163
|
+
type: word
|
|
164
|
+
phonetic: /əˈbæn.dən/
|
|
165
|
+
gloss: 抛弃, 放弃
|
|
166
|
+
tags: [gre]
|
|
167
|
+
difficulty: 2
|
|
168
|
+
---
|
|
169
|
+
## Q: abandon
|
|
170
|
+
## A: abandon
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Rules:
|
|
174
|
+
|
|
175
|
+
- `## Q:` — the prompt. Shown to the user during practice.
|
|
176
|
+
- `## A:` — **the exact text the user is expected to type**.
|
|
177
|
+
- Cards are separated by a blank line + `---` + a blank line (i.e. the start of the next card's frontmatter).
|
|
178
|
+
- The deck frontmatter's closing `---` is followed immediately by the first card's opening frontmatter — no prose between them.
|
|
179
|
+
|
|
180
|
+
**Answer-text constraints (contents of `## A:`)**:
|
|
181
|
+
|
|
182
|
+
- Only characters typable via the OS keyboard (including Chinese IME) are allowed. Emojis, zero-width chars, and most exotic Unicode are rejected.
|
|
183
|
+
- Full-width Chinese punctuation is auto-normalized by the tool (smart quotes → straight quotes, em-dash → double hyphen, etc.). Write punctuation naturally.
|
|
184
|
+
- Length **should** stay under 200 characters — longer answers hurt the practice UX. Split into multiple cards.
|
|
185
|
+
- Spaces, newlines (Enter), and Tab characters are allowed. Newlines inside an answer must be real newlines (a blank line would break Markdown parsing — use a soft break or no break).
|
|
186
|
+
|
|
187
|
+
### Card examples
|
|
188
|
+
|
|
189
|
+
**Word card**
|
|
190
|
+
|
|
191
|
+
```md
|
|
192
|
+
---
|
|
193
|
+
type: word
|
|
194
|
+
phonetic: /əˈbæn.dən/
|
|
195
|
+
gloss: 抛弃, 放弃
|
|
196
|
+
tags: [gre]
|
|
197
|
+
difficulty: 2
|
|
198
|
+
---
|
|
199
|
+
## Q: abandon
|
|
200
|
+
## A: abandon
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Sentence card**
|
|
204
|
+
|
|
205
|
+
```md
|
|
206
|
+
---
|
|
207
|
+
type: sentence
|
|
208
|
+
translation: 熟能生巧。
|
|
209
|
+
tags: [idiom]
|
|
210
|
+
difficulty: 3
|
|
211
|
+
---
|
|
212
|
+
## Q: Practice makes perfect.
|
|
213
|
+
## A: Practice makes perfect.
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Normal card**
|
|
217
|
+
|
|
218
|
+
```md
|
|
219
|
+
---
|
|
220
|
+
type: normal
|
|
221
|
+
tags: [js, closures]
|
|
222
|
+
difficulty: 4
|
|
223
|
+
---
|
|
224
|
+
## Q: What is a closure in JavaScript?
|
|
225
|
+
## A: A function bundled with its lexical scope, letting it access variables from where it was defined.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
For normal cards, the A section is **what the user types**; Q is the prompt. A can be a code snippet, a phrase, a full sentence — subject to the answer-text constraints.
|
|
229
|
+
|
|
230
|
+
### `index.md` format
|
|
231
|
+
|
|
232
|
+
`index.md` lives at repo root, grouped by tag. It **must** be updated after every ingest / update / quiz-gen.
|
|
233
|
+
|
|
234
|
+
```md
|
|
235
|
+
---
|
|
236
|
+
updated: YYYY-MM-DD
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
# Index
|
|
240
|
+
|
|
241
|
+
## <tag-1>
|
|
242
|
+
|
|
243
|
+
| Deck | Cards | Language | Summary | Updated |
|
|
244
|
+
|---|---|---|---|---|
|
|
245
|
+
| [Title 1](material/<file-1>.md) | 32 | en | one-line summary | YYYY-MM-DD |
|
|
246
|
+
| [Title 2](material/<file-2>.md) | 25 | zh | one-line summary | YYYY-MM-DD |
|
|
247
|
+
|
|
248
|
+
## <tag-2>
|
|
249
|
+
|
|
250
|
+
| Deck | Cards | Language | Summary | Updated |
|
|
251
|
+
|---|---|---|---|---|
|
|
252
|
+
| [Title 3](material/<file-3>.md) | 40 | mixed | one-line summary | YYYY-MM-DD |
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Rules:
|
|
256
|
+
|
|
257
|
+
- One `##` heading per tag. **A deck can appear under multiple tags** if it has multiple `tags` values — list it in every relevant section.
|
|
258
|
+
- Under each tag, **only one markdown table** — nothing else.
|
|
259
|
+
- Link paths start from the repo root (`index.md` is at the root).
|
|
260
|
+
- Summary ≤ 60 characters; one line describing the deck's topic.
|
|
261
|
+
- `Cards` reflects the actual card count in the deck file.
|
|
262
|
+
|
|
263
|
+
### `log.md` format
|
|
264
|
+
|
|
265
|
+
`log.md` lives at repo root, chronologically appended (new entries at the end). **Never delete entries.** Every operation **must** append one entry.
|
|
266
|
+
|
|
267
|
+
```md
|
|
268
|
+
---
|
|
269
|
+
updated: YYYY-MM-DD
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
# Log
|
|
273
|
+
|
|
274
|
+
## [YYYY-MM-DD] ingest | <source title or new deck name>
|
|
275
|
+
- created: [Deck A](material/<A>.md) — N cards
|
|
276
|
+
- updated: [Deck B](material/<B>.md) — appended M cards
|
|
277
|
+
|
|
278
|
+
## [YYYY-MM-DD] quiz-gen | <topic>
|
|
279
|
+
- created: [Deck C](material/<C>.md) — N cards
|
|
280
|
+
|
|
281
|
+
## [YYYY-MM-DD] update | <deck name>
|
|
282
|
+
- appended: N
|
|
283
|
+
- fixed: M
|
|
284
|
+
|
|
285
|
+
## [YYYY-MM-DD] cascade | <triggering deck>
|
|
286
|
+
- affected: [Deck X](material/<X>.md), [Deck Y](material/<Y>.md)
|
|
287
|
+
|
|
288
|
+
## [YYYY-MM-DD] check | N issues, M auto-fixed
|
|
289
|
+
|
|
290
|
+
## [YYYY-MM-DD] level-up | <merge/split description>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Rules:
|
|
294
|
+
|
|
295
|
+
- Every ingest / update / quiz-gen / cascade / check / level-up appends one entry.
|
|
296
|
+
- Heading format: `## [YYYY-MM-DD] <op> | <note>`.
|
|
297
|
+
- Entries are **append-only**; do not delete or modify existing ones.
|
|
298
|
+
- `grep "^## \[" log.md` scans all entries.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Operations
|
|
303
|
+
|
|
304
|
+
### Ingest
|
|
305
|
+
|
|
306
|
+
**Turn source material into a stack of typable cards.**
|
|
307
|
+
|
|
308
|
+
Trigger: the user says "ingest <path-or-url>", "make cards from this article", "process this word list".
|
|
309
|
+
|
|
310
|
+
**Step 1: fetch (drop source into raw/)**
|
|
311
|
+
|
|
312
|
+
Four cases:
|
|
313
|
+
|
|
314
|
+
Case A: input is a URL
|
|
315
|
+
- Use whatever web-fetching tool is available; convert to markdown. If none is available, ask the user to paste the content.
|
|
316
|
+
- Save to `raw/YYYY-MM-DD-<slug>.md`.
|
|
317
|
+
- Write frontmatter: `source: <URL>` + `collected: YYYY-MM-DD`.
|
|
318
|
+
|
|
319
|
+
Case B: input is a local file (`ingest /path/to/wordlist.pdf`)
|
|
320
|
+
- Convert to markdown, copy to `raw/YYYY-MM-DD-<slug>.md`.
|
|
321
|
+
- Write frontmatter: `source: <original path>` + `collected: YYYY-MM-DD`.
|
|
322
|
+
|
|
323
|
+
Case C: input is pasted text
|
|
324
|
+
- Save to `raw/YYYY-MM-DD-note-<N>.md`.
|
|
325
|
+
- Write frontmatter: `source: pasted text` + `collected: YYYY-MM-DD`.
|
|
326
|
+
|
|
327
|
+
Case D: input is a topic description ("give me 20 high-frequency GRE words from memory")
|
|
328
|
+
- **Skip fetch**; go straight to Step 2 (this is the quiz-gen path — see Quiz-gen).
|
|
329
|
+
|
|
330
|
+
**Step 2: cut into cards**
|
|
331
|
+
|
|
332
|
+
Read the source and split by material type:
|
|
333
|
+
|
|
334
|
+
- **Word list** → one word card per entry. Grab IPA from the material; if absent, look up an authoritative source; if unknown, leave blank and report in log. Gloss goes in simplified Chinese (or the configured native language).
|
|
335
|
+
- **Sentence / example collection** → one sentence card per line. Translation from the material or a natural translation.
|
|
336
|
+
- **Article / tutorial / code snippet** → extract key points as normal cards. Q is the question, A is a concise answer. Avoid answers > 200 chars — split if longer.
|
|
337
|
+
|
|
338
|
+
**Step 3: decide which deck it belongs to**
|
|
339
|
+
|
|
340
|
+
- Scan `material/` for existing decks on the closest topic:
|
|
341
|
+
- Same topic, existing deck has < 50 cards → append to it.
|
|
342
|
+
- Same topic, existing deck has ≥ 50 cards → create a follow-on deck (e.g. `gre-vocab-list-4.md`).
|
|
343
|
+
- No matching deck → create `material/<slug>.md` with a fresh deck frontmatter.
|
|
344
|
+
- **One deck, one topic.** Never mix TypeScript with English vocabulary in one file.
|
|
345
|
+
|
|
346
|
+
**Step 4: verify typability**
|
|
347
|
+
|
|
348
|
+
For each new card's `## A:`:
|
|
349
|
+
|
|
350
|
+
- Scan characters; reject emojis, zero-widths, control chars (except Tab / Enter).
|
|
351
|
+
- Full-width punctuation is auto-normalized by the tool — write naturally.
|
|
352
|
+
- Length > 200 chars → split into multiple cards, giving each Q a different angle.
|
|
353
|
+
- On word cards, A must equal the target word from Q (this drills spelling, not meaning).
|
|
354
|
+
|
|
355
|
+
**Step 5: update index.md and log.md**
|
|
356
|
+
|
|
357
|
+
- index.md: add rows for new decks; on appended decks, bump `Cards` and `updated`. See Index format.
|
|
358
|
+
- log.md: append one ingest entry. See Log format.
|
|
359
|
+
|
|
360
|
+
**Step 6: tell the user**
|
|
361
|
+
|
|
362
|
+
Briefly report in the conversation what was created / appended / total card count. The user reviews via `git diff` and commits.
|
|
363
|
+
|
|
364
|
+
**The Agent does not run `git add` / `git commit` / `git push`.**
|
|
365
|
+
|
|
366
|
+
**Don't**:
|
|
367
|
+
|
|
368
|
+
- **Don't** "preview and wait for confirmation" — write by the rules directly.
|
|
369
|
+
- **Don't** ingest multiple sources at once — one at a time, so topic judgments don't interfere.
|
|
370
|
+
- **Don't** mix topics in one deck — create more decks instead.
|
|
371
|
+
|
|
372
|
+
### Update
|
|
373
|
+
|
|
374
|
+
**Fix or extend an existing deck.**
|
|
375
|
+
|
|
376
|
+
Trigger: the user says "add a few cards on X to `<deck>`", "fix the IPA on the abandon card in `<deck>`", "merge these cards into `<deck>`".
|
|
377
|
+
|
|
378
|
+
**Steps**
|
|
379
|
+
|
|
380
|
+
1. **Read the target deck**: locate cards to change or the position to append.
|
|
381
|
+
2. **Apply changes**:
|
|
382
|
+
- **Append**: add new cards to the end of the file, following the format.
|
|
383
|
+
- **Fix**: only touch the specified field or the specified card's Q/A/frontmatter. **Don't touch** anything not asked for.
|
|
384
|
+
- **Delete**: only when explicitly requested; do not proactively tidy.
|
|
385
|
+
3. **Verify typability**: same as Ingest Step 4.
|
|
386
|
+
4. **Bump the deck frontmatter `updated`**.
|
|
387
|
+
5. **Update index.md** if card count changed.
|
|
388
|
+
6. **Append log.md**: `## [YYYY-MM-DD] update | <deck name>` with counts.
|
|
389
|
+
|
|
390
|
+
**Don't**:
|
|
391
|
+
|
|
392
|
+
- **Don't** modify hand-edited cards — see the rules section on `<!-- hand-edited -->`.
|
|
393
|
+
- **Don't** re-tidy sections that weren't asked to be touched.
|
|
394
|
+
- **Don't** rewrite the whole deck unless the user explicitly asks.
|
|
395
|
+
|
|
396
|
+
### Cascade
|
|
397
|
+
|
|
398
|
+
**After a big change, check related decks.**
|
|
399
|
+
|
|
400
|
+
Trigger: automatic when an Ingest or Update involves **many cards** (≥ 10), a **topic-scope change** (e.g. a deck goes from "GRE list" to "GRE + TOEFL mixed"), or **terminology renames**.
|
|
401
|
+
|
|
402
|
+
**Steps**
|
|
403
|
+
|
|
404
|
+
1. **Scan index.md**: find decks sharing tags or with related topics.
|
|
405
|
+
2. **Check each one** for:
|
|
406
|
+
- Terminology consistency — did the new deck adopt a new term the old decks should follow?
|
|
407
|
+
- Duplicate cards — remove duplicates from whichever deck has the lower quality version.
|
|
408
|
+
- IPA / gloss / translation conflicts — the same word/sentence should have consistent fields across decks.
|
|
409
|
+
3. **Fix conflicts**: prefer "newer wins" or "higher quality wins". Touch **only** conflicting items — do not retidy.
|
|
410
|
+
4. **Bump `updated` on every deck you touched**.
|
|
411
|
+
5. **Append log.md**: `## [YYYY-MM-DD] cascade | <triggering deck>` with the affected list.
|
|
412
|
+
|
|
413
|
+
Cascade completes **before the triggering operation returns**, not later.
|
|
414
|
+
|
|
415
|
+
### Quiz-gen
|
|
416
|
+
|
|
417
|
+
**Fabricate a stack of cards from a topic description.**
|
|
418
|
+
|
|
419
|
+
Trigger: the user says "quiz-gen JavaScript closures", "give me 30 GRE high-frequency words", "make a React hooks quiz deck".
|
|
420
|
+
|
|
421
|
+
**Steps**
|
|
422
|
+
|
|
423
|
+
1. **Confirm topic and count**: default 25 cards; use whatever count the user specifies.
|
|
424
|
+
2. **Choose card type**:
|
|
425
|
+
- Vocabulary → mostly word cards.
|
|
426
|
+
- Sentences for language learning → mostly sentence cards.
|
|
427
|
+
- Programming / concept knowledge → normal cards (Q is question, A is a short answer).
|
|
428
|
+
3. **Follow Ingest Steps 2–6**: cut cards → decide deck (usually new) → verify typability → update index / log.
|
|
429
|
+
4. **In log, use `quiz-gen` (not `ingest`)** and mark source as "from training memory".
|
|
430
|
+
|
|
431
|
+
**Rules**
|
|
432
|
+
|
|
433
|
+
- Content must be accurate. If unsure (e.g. IPA of a rare word), leave blank with a note — do not fabricate.
|
|
434
|
+
- One topic, one deck per invocation.
|
|
435
|
+
|
|
436
|
+
### Check
|
|
437
|
+
|
|
438
|
+
**When the user's `qtype check` fails, the Agent fixes.**
|
|
439
|
+
|
|
440
|
+
Trigger: the user runs `qtype check` (see CLI section) and hands the error list to the Agent.
|
|
441
|
+
|
|
442
|
+
**Agent handling**:
|
|
443
|
+
|
|
444
|
+
**Deterministic (auto-fix)**:
|
|
445
|
+
|
|
446
|
+
- **Illegal characters** in answer text (emoji / zero-width / control) → strip or drop the card.
|
|
447
|
+
- **Frontmatter errors**: field name misspelled, wrong type, missing required field → fix it.
|
|
448
|
+
- **Word card missing phonetic/gloss, sentence card missing translation** → fill it in (if unknown, place `TODO` and report).
|
|
449
|
+
- **Answer > 200 chars** → split into multiple cards, giving each Q a different angle.
|
|
450
|
+
- **index.md inconsistency**: deck file exists but not in index → add row; card count changed → update.
|
|
451
|
+
- **Broken internal link**: target missing → search `material/` for a same-named file; unique match → repair; otherwise report.
|
|
452
|
+
|
|
453
|
+
**Heuristic (report only)**:
|
|
454
|
+
|
|
455
|
+
- **Duplicate cards**: same Q + A across decks. Report; the user decides whether to merge.
|
|
456
|
+
- **Deck too large**: > 100 cards → recommend split.
|
|
457
|
+
- **Deck too small**: < 5 cards → recommend merge.
|
|
458
|
+
- **Orphan deck**: the deck's tag appears only on this one deck in index.md. Might be a new topic, might be miscategorized. Report.
|
|
459
|
+
|
|
460
|
+
**Report & log**: give the user a fix report, then append `## [YYYY-MM-DD] check | N issues, M auto-fixed`.
|
|
461
|
+
|
|
462
|
+
### Level-up
|
|
463
|
+
|
|
464
|
+
**Restructure a scatter of small decks into a coherent series.**
|
|
465
|
+
|
|
466
|
+
Trigger: the user says "merge these decks", "split this deck into beginner/advanced", "organize the GRE lists into list 1 / 2 / 3".
|
|
467
|
+
|
|
468
|
+
**Steps**
|
|
469
|
+
|
|
470
|
+
1. **Read all involved decks**: full frontmatter + all cards.
|
|
471
|
+
2. **Decide split strategy**:
|
|
472
|
+
- **Merge**: many small decks → one large deck (by topic, difficulty, or tag).
|
|
473
|
+
- **Split**: one large deck → several smaller ones (by difficulty tier, by subtopic).
|
|
474
|
+
- **Reorder**: rearrange cards within a deck — e.g. by ascending `difficulty`, alphabetical, or learning order.
|
|
475
|
+
3. **Write new deck files** using the deck naming rules (e.g. `gre-vocab-list-1.md`).
|
|
476
|
+
4. **Delete or empty old deck files**: post-merge originals get removed; post-split originals get removed. Use `rm` — do not proactively `git rm`.
|
|
477
|
+
5. **Update index.md and log.md**: adjust rows and links; append `## [YYYY-MM-DD] level-up | <description>`.
|
|
478
|
+
6. **Preserve card content**: don't change Q / A / IPA / gloss / translation — level-up is a reorganization, not a content edit. Hand-edited cards (see rules) migrate as-is.
|
|
479
|
+
|
|
480
|
+
**Don't**:
|
|
481
|
+
|
|
482
|
+
- **Don't** modify card content during level-up. Use Update for content changes.
|
|
483
|
+
- **Don't** delete user data on a hunch — if unsure whether an old file should be removed, ask.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Rules
|
|
488
|
+
|
|
489
|
+
### Prohibitions
|
|
490
|
+
|
|
491
|
+
The following are **strictly forbidden**:
|
|
492
|
+
|
|
493
|
+
1. **Never modify raw/ contents.** Files under `raw/` are read-only by default. Moving, renaming, or deleting requires explicit user instruction.
|
|
494
|
+
2. **Never write untypable characters.** Emojis, zero-widths, control characters (except Tab and Enter) in an answer are forbidden. If unsure, prefer ASCII or omit.
|
|
495
|
+
3. **Never write over-long answers.** `## A:` should be ≤ 200 characters. Longer → split. Don't make the user type for ten minutes.
|
|
496
|
+
4. **Never mix topics in one deck.** One deck, one topic. TypeScript and English vocabulary do not co-exist in a single file. Create more decks instead.
|
|
497
|
+
5. **Never modify hand-edited cards.** A card whose body contains the HTML comment `<!-- hand-edited -->` is read-only to the Agent. To change it, ask the user first. Cascade skips these.
|
|
498
|
+
6. **Never use Wikilinks.** `[[foo]]` is banned. Use `[text](./path.md)`.
|
|
499
|
+
7. **Never use absolute paths.** All links must be relative, starting with `./` or `../`.
|
|
500
|
+
8. **Never run `git commit` / `push`.** The user decides commits.
|
|
501
|
+
9. **Never create directories outside the rules.** Under `material/`, only flat `.md` files and an `assets/` subdirectory are allowed. Under `raw/`, likewise. Structures like `material/words/` or `material/2026/` are forbidden.
|
|
502
|
+
10. **Never fabricate IPA / gloss / translation.** For uncertain fields, leave them blank and report in log — do not invent.
|
|
503
|
+
|
|
504
|
+
### Content language
|
|
505
|
+
|
|
506
|
+
- Deck `title`, `index.md` summaries, `log.md` entries: **primarily the repo language (e.g. Chinese), with technical terms in their original form**.
|
|
507
|
+
- Word card `gloss`: **the native language** (usually simplified Chinese; the repo may specify otherwise).
|
|
508
|
+
- Sentence card `translation`: translation into the user's native language (default simplified Chinese). Meaning-preserving, not word-for-word.
|
|
509
|
+
- The card `## A:` answer text itself: **in the deck's target language**, preserved verbatim from the source (English words stay English, Japanese sentences stay Japanese).
|
|
510
|
+
|
|
511
|
+
### Card count guidance
|
|
512
|
+
|
|
513
|
+
- **20–50 cards per deck** is the sweet spot.
|
|
514
|
+
- < 5: too thin — consider merging (Level-up).
|
|
515
|
+
- \> 100: too thick — consider splitting (Level-up).
|
|
516
|
+
- A single Ingest / Quiz-gen usually produces ~25 cards. Beyond that, quality degrades.
|
|
517
|
+
|
|
518
|
+
### Difficulty scale
|
|
519
|
+
|
|
520
|
+
`difficulty` semantics (default 3):
|
|
521
|
+
|
|
522
|
+
| Value | Meaning |
|
|
523
|
+
|---|---|
|
|
524
|
+
| 1 | Trivial recognition, no thought (common words, common-sense facts) |
|
|
525
|
+
| 2 | Read at a glance, no recall needed (common phrases, basic grammar) |
|
|
526
|
+
| 3 | Brief recall required (default, most cards) |
|
|
527
|
+
| 4 | Real thought or a longer answer (concept explanations, sentence composition) |
|
|
528
|
+
| 5 | Hard — long answers or complex concepts (small code snippets, long-sentence dictation) |
|
|
529
|
+
|
|
530
|
+
### Quick reference
|
|
531
|
+
|
|
532
|
+
**Ingest a source**:
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
raw/YYYY-MM-DD-<slug>.md ← write source (flat, optional)
|
|
536
|
+
material/<slug>.md ← write or append to deck
|
|
537
|
+
index.md ← update row (cards / updated)
|
|
538
|
+
log.md ← append ## [date] ingest | title
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**Update a deck**:
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
material/<slug>.md ← append/fix cards, bump updated
|
|
545
|
+
index.md ← update cards & updated
|
|
546
|
+
log.md ← append ## [date] update | deck name
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Quiz-gen from a topic**:
|
|
550
|
+
|
|
551
|
+
```
|
|
552
|
+
material/<slug>.md ← create new deck
|
|
553
|
+
index.md ← add row
|
|
554
|
+
log.md ← append ## [date] quiz-gen | topic
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Check errors reported by user**:
|
|
558
|
+
|
|
559
|
+
```
|
|
560
|
+
classify errors, auto-fix or report
|
|
561
|
+
auto-fix: chars / frontmatter / length / index consistency
|
|
562
|
+
report only: duplicates / oversized / undersized / orphans
|
|
563
|
+
log.md ← append ## [date] check | N issues
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Level-up restructure**:
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
read old decks → decide merge/split/reorder
|
|
570
|
+
write new material/*.md
|
|
571
|
+
rm old files
|
|
572
|
+
index.md ← rows added/removed
|
|
573
|
+
log.md ← append ## [date] level-up | description
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## CLI Commands (for the user, not for you)
|
|
579
|
+
|
|
580
|
+
The following commands are run **by the user** in a terminal. Don't try to invoke them; but recognize them when the user reports errors.
|
|
581
|
+
|
|
582
|
+
| Command | Purpose |
|
|
583
|
+
|---|---|
|
|
584
|
+
| `qtype check` | Validate the repo: scans for **untypable characters**, **missing/malformed frontmatter**, **over-long answers**, **index inconsistencies** (fatal) + duplicate cards / oversized / undersized decks (warnings). **Writes no files.** |
|
|
585
|
+
| `qtype publish build` | Build the repo into a static site in `dist/`. |
|
|
586
|
+
| `qtype publish serve` | Local server for the practice UI in the browser. Supports Chinese IME, target-WPM cursor, blind / ghost hint modes. |
|
|
587
|
+
| `qtype publish init --ci github` | Generate a GitHub Actions workflow. |
|
|
588
|
+
|
|
589
|
+
**Notes**:
|
|
590
|
+
|
|
591
|
+
- After ingest / update / quiz-gen, you don't need to run these; CI runs them on commit.
|
|
592
|
+
- User reports "check says card XXX has illegal characters" — locate the card and fix per the Check section.
|
|
593
|
+
- User reports "publish serve won't let me practice this card" — usually A contains untypable chars or exceeds the length cap — fix via Check.
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Publish output (background only)
|
|
598
|
+
|
|
599
|
+
`qtype publish build` produces roughly:
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
dist/
|
|
603
|
+
├── index.html ← SPA shell
|
|
604
|
+
├── material/<slug>.html ← one shell per deck (<noscript> plain-HTML fallback embedded)
|
|
605
|
+
├── _app/ ← view bundle
|
|
606
|
+
│ └── app.js + chunks/...
|
|
607
|
+
└── _data/
|
|
608
|
+
└── repo.json ← single data snapshot (decks / cards / metadata)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
The Web UI supports: Chinese IME input (diffs against the target after commit), three card types (`type: normal | word | sentence`), target-speed cursor, blind / ghost hints, full-width punctuation auto-normalization, space / Enter / Tab characters.
|
|
612
|
+
|
|
613
|
+
**Fatal errors**: the build aborts (no files emitted) if:
|
|
614
|
+
|
|
615
|
+
- Any card's answer contains untypable characters.
|
|
616
|
+
- Any card's frontmatter is missing a required field or has a wrong type.
|
|
617
|
+
- Any deck's frontmatter is missing `title` / `language` / `tags`.
|
|
618
|
+
- A word card is missing `phonetic` / `gloss`, or a sentence card is missing `translation`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
An LLM-maintained **{{description}}** typing deck, following the [QTYPE spec](./QTYPE.md).
|
|
4
|
+
|
|
5
|
+
> You pick topics and throw in material. An LLM Agent produces structured Markdown cards. You practice typing; progress stays local.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
<!-- Use a nested list to define the first- and second-level topics this deck covers, e.g.:
|
|
10
|
+
|
|
11
|
+
- **English**
|
|
12
|
+
- GRE vocab: list 1 / list 2 / list 3
|
|
13
|
+
- High-frequency patterns: conditionals / subjunctive
|
|
14
|
+
- **Frontend**
|
|
15
|
+
- JavaScript fundamentals
|
|
16
|
+
- React hooks
|
|
17
|
+
|
|
18
|
+
`TODO.md`'s "missing / thin coverage / covered" buckets are driven by this section.
|
|
19
|
+
-->
|
|
20
|
+
|
|
21
|
+
## Layout
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
{{name}}/
|
|
25
|
+
├── QTYPE.md # authoring spec (source of truth)
|
|
26
|
+
├── AGENTS.md # symlink → QTYPE.md (created by qtype init)
|
|
27
|
+
├── CLAUDE.md # symlink → QTYPE.md (created by qtype init)
|
|
28
|
+
├── GEMINI.md # symlink → QTYPE.md (created by qtype init)
|
|
29
|
+
├── README.md # this file (scope maintained by humans)
|
|
30
|
+
├── TODO.md # gap list (Agent-maintained)
|
|
31
|
+
├── index.md # global deck index
|
|
32
|
+
├── log.md # activity log (append-only)
|
|
33
|
+
├── material/ # deck files (one .md per deck)
|
|
34
|
+
│ └── assets/<slug>/
|
|
35
|
+
├── raw/ # (optional) raw material for the Agent to process
|
|
36
|
+
│ └── assets/<slug>/
|
|
37
|
+
└── qtype.config.json # typing / theme / target-speed config
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Common operations (ask the LLM to run these)
|
|
41
|
+
|
|
42
|
+
| Operation | Trigger | Effect |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| ingest | `ingest <path-or-topic>` | read material or generate → create/update deck → append log |
|
|
45
|
+
| quiz-gen | `generate <N> cards on <topic>` | build a new deck from a topic |
|
|
46
|
+
| update | `edit <field> in <deck-slug>` | modify an existing deck |
|
|
47
|
+
| cascade | `cascade check` | after editing one deck, sync related ones |
|
|
48
|
+
| check | `check` (or run the CLI) | validate card formatting |
|
|
49
|
+
| level-up | `merge/split <slug>` | reorganize too-small / too-large decks |
|
|
50
|
+
|
|
51
|
+
Full rules live in [QTYPE.md](./QTYPE.md).
|
|
52
|
+
|
|
53
|
+
## CLI commands you run yourself
|
|
54
|
+
|
|
55
|
+
| Command | Effect |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `qtype check` | Validate deck files. **Read-only**, CI-friendly. |
|
|
58
|
+
| `qtype publish build` | Build a static site under `dist/`. |
|
|
59
|
+
| `qtype publish serve` | Preview typing locally. |
|
|
60
|
+
| `qtype pack` | Bundle the entire deck repo into a shareable archive. |
|
|
61
|
+
|
|
62
|
+
## Typing settings
|
|
63
|
+
|
|
64
|
+
`qtype.config.json` controls theme, hint mode, target WPM, Tab-key behavior, etc. Defaults are generated on first run.
|