cotutor 0.1.0 → 0.3.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.
Files changed (845) hide show
  1. package/LICENSE +247 -21
  2. package/NOTICE +42 -0
  3. package/README.md +107 -24
  4. package/bin/cotutor.js +4 -4
  5. package/cards/canvas/card.css +8 -0
  6. package/cards/canvas/card.md +68 -0
  7. package/cards/choice/card.css +18 -0
  8. package/cards/choice/card.md +55 -0
  9. package/cards/code/card.css +5 -0
  10. package/cards/code/card.md +46 -0
  11. package/cards/dictation/card.css +10 -0
  12. package/cards/dictation/card.md +97 -0
  13. package/cards/image/card.css +10 -0
  14. package/cards/image/card.md +52 -0
  15. package/cards/lecture/card.css +9 -0
  16. package/cards/lecture/card.md +69 -0
  17. package/cards/paint/card.css +20 -0
  18. package/cards/paint/card.md +77 -0
  19. package/cards/photo/card.css +15 -0
  20. package/cards/photo/card.md +61 -0
  21. package/cards/read/card.css +6 -0
  22. package/cards/read/card.md +51 -0
  23. package/cards/text/card.css +3 -0
  24. package/cards/text/card.md +69 -0
  25. package/cards/tianzige/card.css +17 -0
  26. package/cards/tianzige/card.md +78 -0
  27. package/cards/tutor/card.css +66 -0
  28. package/cards/tutor/card.md +148 -0
  29. package/cards/word/card.css +24 -0
  30. package/cards/word/card.md +91 -0
  31. package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +159 -0
  32. package/dist/app/activity-eval-rules.d.ts +62 -0
  33. package/dist/app/activity-eval-rules.js +276 -0
  34. package/dist/app/activity-eval.d.ts +65 -0
  35. package/dist/app/activity-eval.js +158 -0
  36. package/dist/app/build.d.ts +11 -0
  37. package/dist/app/build.js +45 -0
  38. package/dist/app/cli/activities.d.ts +63 -0
  39. package/dist/app/cli/activities.js +158 -0
  40. package/dist/app/cli/cert.d.ts +7 -0
  41. package/dist/app/cli/cert.js +36 -0
  42. package/dist/app/cli/chalk/bake.d.ts +10 -0
  43. package/dist/app/cli/chalk/bake.js +58 -0
  44. package/dist/app/cli/chalk/browser.d.ts +16 -0
  45. package/dist/app/cli/chalk/browser.js +142 -0
  46. package/dist/app/cli/chalk/build.d.ts +14 -0
  47. package/dist/app/cli/chalk/build.js +100 -0
  48. package/dist/app/cli/chalk/dub.d.ts +51 -0
  49. package/dist/app/cli/chalk/dub.js +106 -0
  50. package/dist/app/cli/chalk/load-scene.d.ts +34 -0
  51. package/dist/app/cli/chalk/load-scene.js +117 -0
  52. package/dist/app/cli/chalk/main.d.ts +7 -0
  53. package/dist/app/cli/chalk/main.js +198 -0
  54. package/dist/app/cli/chalk/serve.d.ts +4 -0
  55. package/dist/app/cli/chalk/serve.js +28 -0
  56. package/dist/app/cli/chalk/snap.d.ts +65 -0
  57. package/dist/app/cli/chalk/snap.js +103 -0
  58. package/dist/app/cli/chalk/typecheck.d.ts +7 -0
  59. package/dist/app/cli/chalk/typecheck.js +51 -0
  60. package/dist/app/cli/doctor.d.ts +23 -0
  61. package/dist/app/cli/doctor.js +617 -0
  62. package/dist/app/cli/grades.d.ts +14 -0
  63. package/dist/app/cli/grades.js +44 -0
  64. package/dist/{cli → app/cli}/init.d.ts +3 -1
  65. package/dist/app/cli/init.js +155 -0
  66. package/dist/app/cli/main.js +715 -0
  67. package/dist/app/cli/migrate.d.ts +58 -0
  68. package/dist/app/cli/migrate.js +253 -0
  69. package/dist/app/cli/serve.d.ts +37 -0
  70. package/dist/app/cli/serve.js +98 -0
  71. package/dist/app/cli/shipped.d.ts +30 -0
  72. package/dist/app/cli/shipped.js +47 -0
  73. package/dist/app/cli/skeleton.d.ts +38 -0
  74. package/dist/app/cli/skeleton.js +161 -0
  75. package/dist/app/cli/skills.d.ts +55 -0
  76. package/dist/app/cli/skills.js +189 -0
  77. package/dist/app/cli/themes.d.ts +52 -0
  78. package/dist/app/cli/themes.js +148 -0
  79. package/dist/app/cli/tutors.d.ts +73 -0
  80. package/dist/app/cli/tutors.js +250 -0
  81. package/dist/app/cli/usage.d.ts +2 -0
  82. package/dist/app/cli/usage.js +30 -0
  83. package/dist/app/dictation/recognize.d.ts +18 -0
  84. package/dist/app/dictation/recognize.js +6 -0
  85. package/dist/app/dictation/route.d.ts +52 -0
  86. package/dist/app/dictation/route.js +510 -0
  87. package/dist/app/diff.d.ts +11 -0
  88. package/dist/app/diff.js +71 -0
  89. package/dist/app/http/access.d.ts +62 -0
  90. package/dist/app/http/access.js +180 -0
  91. package/dist/app/http/app.d.ts +246 -0
  92. package/dist/app/http/app.js +1212 -0
  93. package/dist/app/http/dev-activities.d.ts +115 -0
  94. package/dist/app/http/dev-activities.js +193 -0
  95. package/dist/app/http/dev-overview.d.ts +108 -0
  96. package/dist/app/http/dev-overview.js +209 -0
  97. package/dist/app/http/dev-tutor-files.d.ts +80 -0
  98. package/dist/app/http/dev-tutor-files.js +151 -0
  99. package/dist/app/http/parent-home.d.ts +37 -0
  100. package/dist/app/http/parent-home.js +203 -0
  101. package/dist/app/http/range.d.ts +9 -0
  102. package/dist/app/http/range.js +19 -0
  103. package/dist/app/http/raw-view.d.ts +117 -0
  104. package/dist/app/http/raw-view.js +149 -0
  105. package/dist/app/http/send-file.d.ts +7 -0
  106. package/dist/app/http/send-file.js +29 -0
  107. package/dist/app/http/stage.d.ts +24 -0
  108. package/dist/app/http/stage.js +97 -0
  109. package/dist/app/http/theme.d.ts +13 -0
  110. package/dist/app/http/theme.js +41 -0
  111. package/dist/app/machine/bailian.d.ts +28 -0
  112. package/dist/app/machine/bailian.js +88 -0
  113. package/dist/app/mock/mock-live.d.ts +16 -0
  114. package/dist/app/mock/mock-live.js +140 -0
  115. package/dist/app/mock/mock.d.ts +89 -0
  116. package/dist/app/mock/mock.js +1117 -0
  117. package/dist/app/replay.d.ts +94 -0
  118. package/dist/app/replay.js +170 -0
  119. package/dist/app/skills/analyze-doc.d.ts +3 -0
  120. package/dist/app/skills/analyze-doc.js +47 -0
  121. package/dist/app/skills/home-doc.d.ts +5 -0
  122. package/dist/app/skills/home-doc.js +112 -0
  123. package/dist/app/skills/tune-doc.d.ts +3 -0
  124. package/dist/app/skills/tune-doc.js +80 -0
  125. package/dist/app/talk/list.d.ts +11 -0
  126. package/dist/app/talk/list.js +9 -0
  127. package/dist/app/talk/route.d.ts +16 -0
  128. package/dist/app/talk/route.js +142 -0
  129. package/dist/app/workspace.d.ts +58 -0
  130. package/dist/{cli → app}/workspace.js +82 -36
  131. package/dist/app/ws/route.d.ts +25 -0
  132. package/dist/app/ws/route.js +29 -0
  133. package/dist/app/ws/ws.d.ts +31 -0
  134. package/dist/app/ws/ws.js +169 -0
  135. package/dist/board/canvas-chat.d.ts +97 -0
  136. package/dist/board/canvas-chat.js +175 -0
  137. package/dist/board/card-guards.d.ts +15 -0
  138. package/dist/board/card-guards.js +38 -0
  139. package/dist/board/cards/canvas.d.ts +41 -0
  140. package/dist/board/cards/canvas.js +83 -0
  141. package/dist/board/cards/choice.d.ts +20 -0
  142. package/dist/board/cards/choice.js +64 -0
  143. package/dist/board/cards/code.d.ts +9 -0
  144. package/dist/board/cards/code.js +10 -0
  145. package/dist/board/cards/dictation.d.ts +42 -0
  146. package/dist/board/cards/dictation.js +105 -0
  147. package/dist/board/cards/docs.d.ts +47 -0
  148. package/dist/board/cards/docs.js +152 -0
  149. package/dist/board/cards/image.d.ts +13 -0
  150. package/dist/board/cards/image.js +26 -0
  151. package/dist/board/cards/index.d.ts +62 -0
  152. package/dist/board/cards/index.js +144 -0
  153. package/dist/board/cards/kind.d.ts +33 -0
  154. package/dist/board/cards/kind.js +9 -0
  155. package/dist/board/cards/lecture.d.ts +26 -0
  156. package/dist/board/cards/lecture.js +62 -0
  157. package/dist/board/cards/paint.d.ts +38 -0
  158. package/dist/board/cards/paint.js +85 -0
  159. package/dist/board/cards/photo.d.ts +15 -0
  160. package/dist/board/cards/photo.js +29 -0
  161. package/dist/board/cards/read.d.ts +8 -0
  162. package/dist/board/cards/read.js +18 -0
  163. package/dist/board/cards/text.d.ts +20 -0
  164. package/dist/board/cards/text.js +59 -0
  165. package/dist/board/cards/tianzige.d.ts +16 -0
  166. package/dist/board/cards/tianzige.js +64 -0
  167. package/dist/board/cards/tutor.d.ts +72 -0
  168. package/dist/board/cards/tutor.js +134 -0
  169. package/dist/board/cards/word.d.ts +20 -0
  170. package/dist/board/cards/word.js +76 -0
  171. package/dist/board/lecture.d.ts +100 -0
  172. package/dist/board/lecture.js +204 -0
  173. package/dist/board/paper.d.ts +20 -0
  174. package/dist/board/paper.js +76 -0
  175. package/dist/board/parse.d.ts +120 -0
  176. package/dist/board/parse.js +330 -0
  177. package/dist/board/presentation.d.ts +295 -0
  178. package/dist/board/presentation.js +615 -0
  179. package/dist/board/sections.d.ts +16 -0
  180. package/dist/board/sections.js +130 -0
  181. package/dist/brain/api-tools.d.ts +8 -0
  182. package/dist/brain/api-tools.js +190 -0
  183. package/dist/brain/api.d.ts +62 -0
  184. package/dist/brain/api.js +411 -0
  185. package/dist/brain/cli/adapters.d.ts +60 -0
  186. package/dist/brain/cli/adapters.js +91 -0
  187. package/dist/brain/cli/qwen.d.ts +11 -0
  188. package/dist/brain/cli/qwen.js +141 -0
  189. package/dist/brain/cli/run-plan.d.ts +59 -0
  190. package/dist/brain/cli/run-plan.js +71 -0
  191. package/dist/brain/cli/run.d.ts +35 -0
  192. package/dist/brain/cli/run.js +256 -0
  193. package/dist/brain/cli/stream-json.d.ts +18 -0
  194. package/dist/brain/cli/stream-json.js +86 -0
  195. package/dist/brain/cli/types.d.ts +144 -0
  196. package/dist/brain/cli/types.js +3 -0
  197. package/dist/brain/cli/warm.d.ts +53 -0
  198. package/dist/brain/cli/warm.js +103 -0
  199. package/dist/brain/index.d.ts +145 -0
  200. package/dist/brain/index.js +74 -0
  201. package/dist/brain/stream.d.ts +20 -0
  202. package/dist/brain/stream.js +70 -0
  203. package/dist/brain/transcript.d.ts +45 -0
  204. package/dist/brain/transcript.js +132 -0
  205. package/dist/chalk/actions.d.ts +37 -0
  206. package/dist/chalk/actions.js +69 -0
  207. package/dist/chalk/bundle.d.ts +26 -0
  208. package/dist/chalk/bundle.js +69 -0
  209. package/dist/chalk/check.d.ts +28 -0
  210. package/dist/chalk/check.js +291 -0
  211. package/dist/chalk/circle.d.ts +64 -0
  212. package/dist/chalk/circle.js +193 -0
  213. package/dist/chalk/clock.d.ts +84 -0
  214. package/dist/chalk/clock.js +84 -0
  215. package/dist/chalk/geometry.d.ts +44 -0
  216. package/dist/chalk/geometry.js +139 -0
  217. package/dist/chalk/glyphs/capitals.d.ts +12 -0
  218. package/dist/chalk/glyphs/capitals.js +264 -0
  219. package/dist/chalk/glyphs/index.d.ts +21 -0
  220. package/dist/chalk/glyphs/index.js +18 -0
  221. package/dist/chalk/glyphs/letters.d.ts +13 -0
  222. package/dist/chalk/glyphs/letters.js +280 -0
  223. package/dist/chalk/glyphs/metrics.d.ts +33 -0
  224. package/dist/chalk/glyphs/metrics.js +28 -0
  225. package/dist/chalk/glyphs/registry.d.ts +7 -0
  226. package/dist/chalk/glyphs/registry.js +8 -0
  227. package/dist/chalk/glyphs/sample.d.ts +32 -0
  228. package/dist/chalk/glyphs/sample.js +102 -0
  229. package/dist/chalk/glyphs/skeleton.d.ts +64 -0
  230. package/dist/chalk/glyphs/skeleton.js +126 -0
  231. package/dist/chalk/glyphs/types.d.ts +58 -0
  232. package/dist/chalk/glyphs/types.js +17 -0
  233. package/dist/chalk/index.d.ts +23 -0
  234. package/dist/chalk/index.js +15 -0
  235. package/dist/chalk/manifest.d.ts +41 -0
  236. package/dist/chalk/manifest.js +6 -0
  237. package/dist/chalk/paint.d.ts +180 -0
  238. package/dist/chalk/paint.js +210 -0
  239. package/dist/chalk/pointer.d.ts +8 -0
  240. package/dist/chalk/pointer.js +21 -0
  241. package/dist/chalk/prepare.d.ts +11 -0
  242. package/dist/chalk/prepare.js +15 -0
  243. package/dist/chalk/scene-schema.d.ts +199 -0
  244. package/dist/chalk/scene-schema.js +91 -0
  245. package/dist/chalk/seed.d.ts +3 -0
  246. package/dist/chalk/seed.js +10 -0
  247. package/dist/chalk/text-metrics.d.ts +8 -0
  248. package/dist/chalk/text-metrics.js +156 -0
  249. package/dist/chalk/timeline.d.ts +47 -0
  250. package/dist/chalk/timeline.js +84 -0
  251. package/dist/chalk/types.d.ts +150 -0
  252. package/dist/chalk/types.js +8 -0
  253. package/dist/lib/index.d.ts +14 -8
  254. package/dist/lib/index.js +14 -8
  255. package/dist/media/draw.d.ts +23 -0
  256. package/dist/media/draw.js +44 -0
  257. package/dist/media/mp3.d.ts +6 -0
  258. package/dist/media/mp3.js +58 -0
  259. package/dist/media/mp4.d.ts +1 -0
  260. package/dist/media/mp4.js +58 -0
  261. package/dist/media/tts-voices.d.ts +18 -0
  262. package/dist/media/tts-voices.js +606 -0
  263. package/dist/media/tts.d.ts +56 -0
  264. package/dist/media/tts.js +174 -0
  265. package/dist/media/vision.d.ts +25 -0
  266. package/dist/media/vision.js +71 -0
  267. package/dist/memory/commit.d.ts +67 -0
  268. package/dist/memory/commit.js +130 -0
  269. package/dist/memory/context.d.ts +86 -0
  270. package/dist/memory/context.js +97 -0
  271. package/dist/memory/diary.d.ts +84 -0
  272. package/dist/memory/diary.js +183 -0
  273. package/dist/memory/grade.d.ts +2 -0
  274. package/dist/memory/grade.js +7 -0
  275. package/dist/{lib → memory}/plan.d.ts +3 -1
  276. package/dist/{lib → memory}/plan.js +10 -1
  277. package/dist/memory/proposals.d.ts +11 -0
  278. package/dist/memory/proposals.js +43 -0
  279. package/dist/memory/retrieval.d.ts +44 -0
  280. package/dist/memory/retrieval.js +157 -0
  281. package/dist/memory/timetable.d.ts +19 -0
  282. package/dist/memory/timetable.js +95 -0
  283. package/dist/memory/vault-notes.d.ts +165 -0
  284. package/dist/memory/vault-notes.js +359 -0
  285. package/dist/memory/vault.d.ts +70 -0
  286. package/dist/memory/vault.js +169 -0
  287. package/dist/realtime/bailian.d.ts +120 -0
  288. package/dist/realtime/bailian.js +288 -0
  289. package/dist/realtime/cues.d.ts +51 -0
  290. package/dist/realtime/cues.js +94 -0
  291. package/dist/realtime/listen.d.ts +32 -0
  292. package/dist/realtime/listen.js +53 -0
  293. package/dist/realtime/preview.d.ts +23 -0
  294. package/dist/realtime/preview.js +38 -0
  295. package/dist/realtime/prompt.d.ts +8 -0
  296. package/dist/realtime/prompt.js +13 -0
  297. package/dist/realtime/voices.d.ts +19 -0
  298. package/dist/realtime/voices.js +71 -0
  299. package/dist/runtime/activities.d.ts +17 -0
  300. package/dist/runtime/activities.js +46 -0
  301. package/dist/runtime/activity-state.d.ts +22 -0
  302. package/dist/runtime/activity-state.js +83 -0
  303. package/dist/runtime/activity.d.ts +63 -0
  304. package/dist/runtime/activity.js +428 -0
  305. package/dist/runtime/agent-file.d.ts +24 -0
  306. package/dist/runtime/agent-file.js +59 -0
  307. package/dist/runtime/auto-bookkeep.d.ts +30 -0
  308. package/dist/runtime/auto-bookkeep.js +84 -0
  309. package/dist/{lib → runtime/context}/context-pack.d.ts +3 -3
  310. package/dist/runtime/context/context-pack.js +182 -0
  311. package/dist/runtime/context/thread-digest.d.ts +4 -0
  312. package/dist/runtime/context/thread-digest.js +35 -0
  313. package/dist/runtime/context/tutor-rules.d.ts +25 -0
  314. package/dist/runtime/context/tutor-rules.js +47 -0
  315. package/dist/runtime/delivery/delivery.d.ts +94 -0
  316. package/dist/runtime/delivery/delivery.js +145 -0
  317. package/dist/runtime/delivery/playback.d.ts +25 -0
  318. package/dist/runtime/delivery/playback.js +29 -0
  319. package/dist/runtime/delivery/player.d.ts +145 -0
  320. package/dist/runtime/delivery/player.js +221 -0
  321. package/dist/runtime/delivery/queue.d.ts +89 -0
  322. package/dist/runtime/delivery/queue.js +59 -0
  323. package/dist/runtime/home/edit.d.ts +39 -0
  324. package/dist/runtime/home/edit.js +132 -0
  325. package/dist/runtime/home/home.d.ts +180 -0
  326. package/dist/runtime/home/home.js +311 -0
  327. package/dist/runtime/home/store.d.ts +142 -0
  328. package/dist/runtime/home/store.js +440 -0
  329. package/dist/runtime/journal/conversation.d.ts +46 -0
  330. package/dist/runtime/journal/conversation.js +89 -0
  331. package/dist/runtime/journal/events.d.ts +27 -0
  332. package/dist/runtime/journal/events.js +136 -0
  333. package/dist/runtime/journal/kid-log.d.ts +105 -0
  334. package/dist/runtime/journal/kid-log.js +133 -0
  335. package/dist/runtime/journal/ledger.d.ts +17 -0
  336. package/dist/{lib → runtime/journal}/ledger.js +7 -32
  337. package/dist/runtime/journal/mix.d.ts +33 -0
  338. package/dist/runtime/journal/mix.js +88 -0
  339. package/dist/runtime/journal/mouth-index.d.ts +26 -0
  340. package/dist/runtime/journal/mouth-index.js +34 -0
  341. package/dist/runtime/journal/mouth.d.ts +54 -0
  342. package/dist/runtime/journal/mouth.js +102 -0
  343. package/dist/runtime/journal/recorder.d.ts +46 -0
  344. package/dist/runtime/journal/recorder.js +58 -0
  345. package/dist/runtime/journal/store.d.ts +123 -0
  346. package/dist/runtime/journal/store.js +316 -0
  347. package/dist/runtime/kid-input.d.ts +16 -0
  348. package/dist/runtime/kid-input.js +195 -0
  349. package/dist/runtime/kid-view.d.ts +124 -0
  350. package/dist/runtime/kid-view.js +157 -0
  351. package/dist/runtime/lecture.d.ts +86 -0
  352. package/dist/runtime/lecture.js +237 -0
  353. package/dist/runtime/live/call.d.ts +219 -0
  354. package/dist/runtime/live/call.js +1211 -0
  355. package/dist/runtime/live/turns.d.ts +48 -0
  356. package/dist/runtime/live/turns.js +102 -0
  357. package/dist/runtime/runner.d.ts +289 -0
  358. package/dist/runtime/runner.js +898 -0
  359. package/dist/runtime/talk-run.d.ts +21 -0
  360. package/dist/runtime/talk-run.js +34 -0
  361. package/dist/scenes/dictation/match.d.ts +57 -0
  362. package/dist/scenes/dictation/match.js +255 -0
  363. package/dist/scenes/dictation/recognize.d.ts +86 -0
  364. package/dist/scenes/dictation/recognize.js +163 -0
  365. package/dist/scenes/dictation/store.d.ts +112 -0
  366. package/dist/scenes/dictation/store.js +99 -0
  367. package/dist/scenes/look.d.ts +26 -0
  368. package/dist/scenes/look.js +1 -0
  369. package/dist/scenes/paint/painter.d.ts +84 -0
  370. package/dist/scenes/paint/painter.js +170 -0
  371. package/dist/scenes/paint/prompt.d.ts +23 -0
  372. package/dist/scenes/paint/prompt.js +36 -0
  373. package/dist/scenes/talk/en-words.d.ts +8 -0
  374. package/dist/scenes/talk/en-words.js +44 -0
  375. package/dist/scenes/talk/list.d.ts +44 -0
  376. package/dist/scenes/talk/list.js +88 -0
  377. package/dist/scenes/talk/store.d.ts +27 -0
  378. package/dist/scenes/talk/store.js +61 -0
  379. package/dist/scenes/talk/warmup.d.ts +16 -0
  380. package/dist/scenes/talk/warmup.js +39 -0
  381. package/dist/schema/activity.d.ts +149 -0
  382. package/dist/schema/activity.js +113 -0
  383. package/dist/schema/board.d.ts +200 -0
  384. package/dist/schema/board.js +42 -0
  385. package/dist/schema/config.d.ts +353 -80
  386. package/dist/schema/config.js +293 -76
  387. package/dist/schema/context-pack.d.ts +82 -13
  388. package/dist/schema/context-pack.js +97 -10
  389. package/dist/schema/conversation-files.d.ts +43 -0
  390. package/dist/schema/conversation-files.js +26 -0
  391. package/dist/schema/conversation.d.ts +459 -13
  392. package/dist/schema/conversation.js +148 -8
  393. package/dist/schema/events.d.ts +138 -0
  394. package/dist/schema/events.js +1 -0
  395. package/dist/schema/home.d.ts +46 -0
  396. package/dist/schema/home.js +30 -0
  397. package/dist/schema/index.d.ts +19 -4
  398. package/dist/schema/index.js +19 -4
  399. package/dist/schema/json-schema.d.ts +2 -0
  400. package/dist/schema/json-schema.js +23 -0
  401. package/dist/schema/kid-device.d.ts +20 -0
  402. package/dist/schema/kid-device.js +32 -0
  403. package/dist/schema/ledger.d.ts +9 -64
  404. package/dist/schema/ledger.js +7 -31
  405. package/dist/schema/memory.d.ts +73 -0
  406. package/dist/schema/memory.js +47 -0
  407. package/dist/schema/plan.d.ts +2 -2
  408. package/dist/schema/plan.js +2 -2
  409. package/dist/schema/sections.d.ts +32 -25
  410. package/dist/schema/sections.js +21 -22
  411. package/dist/schema/theme.d.ts +25 -0
  412. package/dist/schema/theme.js +26 -0
  413. package/dist/schema/timetable.d.ts +26 -0
  414. package/dist/schema/timetable.js +18 -0
  415. package/dist/schema/workspace.d.ts +41 -0
  416. package/dist/schema/workspace.js +9 -0
  417. package/dist/shared/errors.d.ts +8 -0
  418. package/dist/shared/errors.js +13 -0
  419. package/dist/shared/home.d.ts +2 -0
  420. package/dist/shared/home.js +16 -0
  421. package/dist/shared/human-notes.d.ts +5 -0
  422. package/dist/shared/human-notes.js +7 -0
  423. package/dist/shared/qianwen.d.ts +50 -0
  424. package/dist/shared/qianwen.js +109 -0
  425. package/dist/shared/time.d.ts +9 -0
  426. package/dist/shared/time.js +22 -0
  427. package/dist/shared/tokens.d.ts +18 -0
  428. package/dist/shared/tokens.js +11 -0
  429. package/dist/shared/wav.d.ts +10 -0
  430. package/dist/shared/wav.js +50 -0
  431. package/dist/stage/Assistant-Bold-ZDZZ6JHA.woff2 +0 -0
  432. package/dist/stage/Assistant-Medium-DZ25RZU3.woff2 +0 -0
  433. package/dist/stage/Assistant-Regular-PLF2XOGW.woff2 +0 -0
  434. package/dist/stage/Assistant-SemiBold-CZ5MX6FK.woff2 +0 -0
  435. package/dist/stage/chunk-226TNLQA.js +129 -0
  436. package/dist/stage/chunk-24LGQEKE.js +1 -0
  437. package/dist/stage/chunk-25XV6UY3.js +10 -0
  438. package/dist/stage/chunk-2AZI3NHJ.js +1 -0
  439. package/dist/stage/chunk-2BQZTTBE.js +1 -0
  440. package/dist/stage/chunk-2M7CGY3Y.js +11 -0
  441. package/dist/stage/chunk-2VR4WFQ7.js +10 -0
  442. package/dist/stage/chunk-2ZYXBUWG.js +9 -0
  443. package/dist/stage/chunk-32LAWMGY.js +1 -0
  444. package/dist/stage/chunk-3LEMTSOO.js +11 -0
  445. package/dist/stage/chunk-3QCKGZYE.js +32 -0
  446. package/dist/stage/chunk-44P66WFA.js +1 -0
  447. package/dist/stage/chunk-4AM6FRR6.js +7 -0
  448. package/dist/stage/chunk-4QDE4SUK.js +1 -0
  449. package/dist/stage/chunk-4QESHV3U.js +1 -0
  450. package/dist/stage/chunk-4SG5QPHB.js +2 -0
  451. package/dist/stage/chunk-4T4UVCOC.js +1 -0
  452. package/dist/stage/chunk-4WUEPPG3.js +34 -0
  453. package/dist/stage/chunk-4YCN6UZ5.js +99 -0
  454. package/dist/stage/chunk-5V7ZY2AP.js +1 -0
  455. package/dist/stage/chunk-5VS5Y2VJ.js +6 -0
  456. package/dist/stage/chunk-5W4NO2ZD.js +62 -0
  457. package/dist/stage/chunk-65CRNICM.js +1 -0
  458. package/dist/stage/chunk-66MULO7F.js +5 -0
  459. package/dist/stage/chunk-6COFQTBS.js +162 -0
  460. package/dist/stage/chunk-6VZG3M3I.js +8 -0
  461. package/dist/stage/chunk-7HVDH43T.js +1 -0
  462. package/dist/stage/chunk-7JLJN4EZ.js +8 -0
  463. package/dist/stage/chunk-7QDSP4LN.js +1 -0
  464. package/dist/stage/chunk-7R6OQAO3.js +1 -0
  465. package/dist/stage/chunk-7VTTHZKW.js +7 -0
  466. package/dist/stage/chunk-ATHR5KEN.js +10 -0
  467. package/dist/stage/chunk-B6R5LTP2.js +36 -0
  468. package/dist/stage/chunk-BD4WE264.js +1 -0
  469. package/dist/stage/chunk-BONANU47.js +206 -0
  470. package/dist/stage/chunk-BTVW7OFK.js +89 -0
  471. package/dist/stage/chunk-BUTJIDHG.js +8 -0
  472. package/dist/stage/chunk-C2IPYMML.js +84 -0
  473. package/dist/stage/chunk-C54FTPPV.js +9 -0
  474. package/dist/stage/chunk-C6LOUF2Z.js +90 -0
  475. package/dist/stage/chunk-CFF3PILO.js +339 -0
  476. package/dist/stage/chunk-CI2FQ2JY.js +9 -0
  477. package/dist/stage/chunk-CW5O4RV7.js +12 -0
  478. package/dist/stage/chunk-CYABNQYD.js +1 -0
  479. package/dist/stage/chunk-D7FFA4NS.js +15 -0
  480. package/dist/stage/chunk-DUNNXU6Z.js +8 -0
  481. package/dist/stage/chunk-E3E4Q6JF.js +6 -0
  482. package/dist/stage/chunk-EB3U3LH7.js +9 -0
  483. package/dist/stage/chunk-EGSZ736B.js +8 -0
  484. package/dist/stage/chunk-EOBXW5VQ.js +2 -0
  485. package/dist/stage/chunk-ERZC3B7Y.js +11 -0
  486. package/dist/stage/chunk-ESDOOBIH.js +11 -0
  487. package/dist/stage/chunk-EVU4QAL4.js +178 -0
  488. package/dist/stage/chunk-EVVZI2NY.js +39 -0
  489. package/dist/stage/chunk-FBKAZUKD.js +39 -0
  490. package/dist/stage/chunk-FDVKYQJN.js +1 -0
  491. package/dist/stage/chunk-FGPVYHVR.js +5 -0
  492. package/dist/stage/chunk-FHUD4AZC.js +1 -0
  493. package/dist/stage/chunk-FKUJ2DMY.js +96 -0
  494. package/dist/stage/chunk-FNZGVIJV.js +1 -0
  495. package/dist/stage/chunk-GBYSEGYM.js +78 -0
  496. package/dist/stage/chunk-GCO4TDHX.js +1 -0
  497. package/dist/stage/chunk-GKTML2LG.js +10 -0
  498. package/dist/stage/chunk-GMDIHHXQ.js +11 -0
  499. package/dist/stage/chunk-GOUNACI3.js +10 -0
  500. package/dist/stage/chunk-GXAYATPF.js +3 -0
  501. package/dist/stage/chunk-H2K2DDXV.js +8 -0
  502. package/dist/stage/chunk-H3UJI6ZY.js +13 -0
  503. package/dist/stage/chunk-H6PUGCME.js +7 -0
  504. package/dist/stage/chunk-HNBUIMIG.js +1 -0
  505. package/dist/stage/chunk-HRZY2BJQ.js +10 -0
  506. package/dist/stage/chunk-HSPJGALW.js +30 -0
  507. package/dist/stage/chunk-IBYORHIP.js +25 -0
  508. package/dist/stage/chunk-IHF4EWTY.js +1 -0
  509. package/dist/stage/chunk-ISBRPNKI.js +1 -0
  510. package/dist/stage/chunk-IZ3OI3C2.js +2 -0
  511. package/dist/stage/chunk-JEFZAC2R.js +1 -0
  512. package/dist/stage/chunk-JFIGRDPO.js +1 -0
  513. package/dist/stage/chunk-JFVYBOOC.js +9 -0
  514. package/dist/stage/chunk-JGD4TUTQ.js +1 -0
  515. package/dist/stage/chunk-JO4BEHDH.js +4 -0
  516. package/dist/stage/chunk-JOWBS42C.js +1 -0
  517. package/dist/stage/chunk-JP3TM2TG.js +1 -0
  518. package/dist/stage/chunk-JVK2C52O.js +1 -0
  519. package/dist/stage/chunk-K4I76XBX.js +14 -0
  520. package/dist/stage/chunk-KA6CZZ4M.js +1 -0
  521. package/dist/stage/chunk-KESSJ36M.js +1 -0
  522. package/dist/stage/chunk-LKATEVRU.js +1 -0
  523. package/dist/stage/chunk-LS4EJ2EI.js +1 -0
  524. package/dist/stage/chunk-LUKJQC2X.js +70 -0
  525. package/dist/stage/chunk-LWULR3SM.js +1 -0
  526. package/dist/stage/chunk-LWVRFMKX.js +2 -0
  527. package/dist/stage/chunk-LXC4ZBPZ.js +1 -0
  528. package/dist/stage/chunk-M2UP255H.js +1 -0
  529. package/dist/stage/chunk-MAPPXUZQ.js +231 -0
  530. package/dist/stage/chunk-MBMPW7Z5.js +1 -0
  531. package/dist/stage/chunk-MD3HMMTK.js +1 -0
  532. package/dist/stage/chunk-MLPCUIKR.js +17 -0
  533. package/dist/stage/chunk-MNQIRPDQ.js +8 -0
  534. package/dist/stage/chunk-MRG7MJVS.js +1 -0
  535. package/dist/stage/chunk-MU6EXW72.js +10 -0
  536. package/dist/stage/chunk-MVU2X6LU.js +4 -0
  537. package/dist/stage/chunk-N6UA5CZV.js +36 -0
  538. package/dist/stage/chunk-NLZIT2TJ.js +7 -0
  539. package/dist/stage/chunk-NOJR3J7M.js +1 -0
  540. package/dist/stage/chunk-NT7ARDOL.js +1 -0
  541. package/dist/stage/chunk-NWOGMPZH.js +1 -0
  542. package/dist/stage/chunk-NXE4KMG3.js +40 -0
  543. package/dist/stage/chunk-O4YCZXD2.js +257 -0
  544. package/dist/stage/chunk-OAJZ7HLK.js +1 -0
  545. package/dist/stage/chunk-OCFCFASO.js +1 -0
  546. package/dist/stage/chunk-ODGPG5RY.js +7 -0
  547. package/dist/stage/chunk-ODUEA52Q.js +3 -0
  548. package/dist/stage/chunk-OGDJ3FCI.js +1 -0
  549. package/dist/stage/chunk-OLDJ55TT.js +1 -0
  550. package/dist/stage/chunk-OOCL4ATI.js +2 -0
  551. package/dist/stage/chunk-OQEFDUDV.js +1 -0
  552. package/dist/stage/chunk-OQWVEWG7.js +4 -0
  553. package/dist/stage/chunk-OUIX6BGK.js +1 -0
  554. package/dist/stage/chunk-OV7OZYUO.js +7 -0
  555. package/dist/stage/chunk-PFGDY33O.js +1 -0
  556. package/dist/stage/chunk-PFJDUJB3.js +139 -0
  557. package/dist/stage/chunk-PGEW76ER.js +1 -0
  558. package/dist/stage/chunk-PP76TAQX.js +32 -0
  559. package/dist/stage/chunk-PTBOP4OT.js +2 -0
  560. package/dist/stage/chunk-PX2RVHCK.js +1 -0
  561. package/dist/stage/chunk-Q7K3PNAD.js +1 -0
  562. package/dist/stage/chunk-QDDPLZDB.js +1 -0
  563. package/dist/stage/chunk-QLGLZ77F.js +1 -0
  564. package/dist/stage/chunk-QOPFDFHI.js +1 -0
  565. package/dist/stage/chunk-QXT4KAEJ.js +1 -0
  566. package/dist/stage/chunk-R4YVZCIT.js +1 -0
  567. package/dist/stage/chunk-R6P6EHWA.js +62 -0
  568. package/dist/stage/chunk-RCH3OXBI.js +9 -0
  569. package/dist/stage/chunk-RHPE44I2.js +1 -0
  570. package/dist/stage/chunk-RJ2R4FVC.js +1 -0
  571. package/dist/stage/chunk-RJH2YVTD.js +1 -0
  572. package/dist/stage/chunk-RLWWW5OB.js +6 -0
  573. package/dist/stage/chunk-S3WO35Z7.js +120 -0
  574. package/dist/stage/chunk-S7ZGX6MW.js +1 -0
  575. package/dist/stage/chunk-SIAWX27B.js +1 -0
  576. package/dist/stage/chunk-SLG4X622.js +1 -0
  577. package/dist/stage/chunk-SLJRBC77.js +168 -0
  578. package/dist/stage/chunk-SRORWQJZ.js +1 -0
  579. package/dist/stage/chunk-STZTUIIF.js +2 -0
  580. package/dist/stage/chunk-SVQ6MWNV.js +9 -0
  581. package/dist/stage/chunk-TL3HLL2S.js +132 -0
  582. package/dist/stage/chunk-U3DOE6SF.js +24 -0
  583. package/dist/stage/chunk-UABQIZKW.js +6 -0
  584. package/dist/stage/chunk-UDK3R764.js +1 -0
  585. package/dist/stage/chunk-UFF4E54B.js +41 -0
  586. package/dist/stage/chunk-UFP5Q4AY.js +1 -0
  587. package/dist/stage/chunk-UMH2TSW2.js +72 -0
  588. package/dist/stage/chunk-UTASVNF5.js +10 -0
  589. package/dist/stage/chunk-UW7ML5ML.js +88 -0
  590. package/dist/stage/chunk-V3J7F6OP.js +1 -0
  591. package/dist/stage/chunk-VEHP5B5Q.js +5 -0
  592. package/dist/stage/chunk-VRXWB733.js +9 -0
  593. package/dist/stage/chunk-VVDHBBS5.js +5 -0
  594. package/dist/stage/chunk-VYFOH6FU.js +106 -0
  595. package/dist/stage/chunk-W3VZTLQS.js +3 -0
  596. package/dist/stage/chunk-W7WNRWTZ.js +8 -0
  597. package/dist/stage/chunk-WHUCV53C.js +1 -0
  598. package/dist/stage/chunk-WULMYHIF.js +1 -0
  599. package/dist/stage/chunk-X54EX7AW.js +9 -0
  600. package/dist/stage/chunk-X565ISZM.js +1 -0
  601. package/dist/stage/chunk-XGZMDECB.js +1 -0
  602. package/dist/stage/chunk-XHONC4RF.js +172 -0
  603. package/dist/stage/chunk-XIK4XQ6E.js +24 -0
  604. package/dist/stage/chunk-XJQQWFTE.js +1 -0
  605. package/dist/stage/chunk-XM4WOU3I.js +13 -0
  606. package/dist/stage/chunk-XM7Y5HP5.js +38 -0
  607. package/dist/stage/chunk-XNYYRGD5.js +292 -0
  608. package/dist/stage/chunk-XOYVR3SV.js +1 -0
  609. package/dist/stage/chunk-XQIW5HCE.js +1 -0
  610. package/dist/stage/chunk-YEWL7FUW.js +8 -0
  611. package/dist/stage/chunk-YI55PY3V.js +1 -0
  612. package/dist/stage/chunk-YM63HI6W.js +1 -0
  613. package/dist/stage/chunk-YNLGLU4P.js +9 -0
  614. package/dist/stage/chunk-YTNMNOWP.js +1 -0
  615. package/dist/stage/chunk-YV62XKTC.js +1 -0
  616. package/dist/stage/chunk-YVBA3QW6.js +1 -0
  617. package/dist/stage/chunk-Z2SY2HOC.js +1 -0
  618. package/dist/stage/chunk-ZBTCQEBK.js +10 -0
  619. package/dist/stage/chunk-ZCUFN74D.js +1 -0
  620. package/dist/stage/chunk-ZHOULXEJ.js +11 -0
  621. package/dist/stage/chunk-ZTRZIET2.js +1 -0
  622. package/dist/stage/index.html +11 -0
  623. package/dist/stage/stage.css +1 -0
  624. package/dist/stage/stage.js +1 -0
  625. package/dist/ui/dev/dev-page.d.ts +1 -0
  626. package/dist/ui/dev/dev-page.js +2248 -0
  627. package/dist/ui/dev/dev-theme.d.ts +6 -0
  628. package/dist/ui/dev/dev-theme.js +67 -0
  629. package/dist/ui/dev/qr-page.d.ts +8 -0
  630. package/dist/ui/dev/qr-page.js +61 -0
  631. package/dist/ui/dev/voice-gallery-page.d.ts +1 -0
  632. package/dist/ui/dev/voice-gallery-page.js +121 -0
  633. package/dist/ui/dev/voice-scenes.d.ts +21 -0
  634. package/dist/ui/dev/voice-scenes.js +30 -0
  635. package/dist/ui/dictation/en-box.d.ts +2 -0
  636. package/dist/ui/dictation/en-box.js +86 -0
  637. package/dist/ui/dictation/en-page.d.ts +1 -0
  638. package/dist/ui/dictation/en-page.js +484 -0
  639. package/dist/ui/dictation/page.d.ts +1 -0
  640. package/dist/ui/dictation/page.js +1084 -0
  641. package/dist/ui/dictation/parent-page.d.ts +1 -0
  642. package/dist/ui/dictation/parent-page.js +234 -0
  643. package/dist/ui/dictation/reel-mode.d.ts +1 -0
  644. package/dist/ui/dictation/reel-mode.js +87 -0
  645. package/dist/ui/dictation/reel-page.d.ts +1 -0
  646. package/dist/ui/dictation/reel-page.js +62 -0
  647. package/dist/ui/dictation/reel.d.ts +163 -0
  648. package/dist/ui/dictation/reel.js +436 -0
  649. package/dist/ui/dictation/ui.d.ts +44 -0
  650. package/dist/ui/dictation/ui.js +80 -0
  651. package/dist/ui/dictation/unlock.d.ts +7 -0
  652. package/dist/ui/dictation/unlock.js +16 -0
  653. package/dist/ui/kid/audio-client.d.ts +11 -0
  654. package/dist/ui/kid/audio-client.js +74 -0
  655. package/dist/ui/kid/board-mark-art.d.ts +37 -0
  656. package/dist/ui/kid/board-mark-art.js +5 -0
  657. package/dist/ui/kid/board-mark.d.ts +24 -0
  658. package/dist/ui/kid/board-mark.js +58 -0
  659. package/dist/ui/kid/kid-log.d.ts +15 -0
  660. package/dist/ui/kid/kid-log.js +87 -0
  661. package/dist/ui/kid/kid-nav.d.ts +11 -0
  662. package/dist/ui/kid/kid-nav.js +22 -0
  663. package/dist/ui/kid/kid-page.d.ts +9 -0
  664. package/dist/ui/kid/kid-page.js +3719 -0
  665. package/dist/ui/kid/letters.d.ts +26 -0
  666. package/dist/ui/kid/letters.js +51 -0
  667. package/dist/ui/kid/photo-edit.d.ts +83 -0
  668. package/dist/ui/kid/photo-edit.js +106 -0
  669. package/dist/ui/kid/photo-ui.d.ts +23 -0
  670. package/dist/ui/kid/photo-ui.js +60 -0
  671. package/dist/ui/kid/reel.d.ts +419 -0
  672. package/dist/ui/kid/reel.js +648 -0
  673. package/dist/ui/kid/tianzige.d.ts +8 -0
  674. package/dist/ui/kid/tianzige.js +45 -0
  675. package/dist/ui/kid/tutor-face.d.ts +35 -0
  676. package/dist/ui/kid/tutor-face.js +84 -0
  677. package/dist/ui/kid/voice-ui.d.ts +70 -0
  678. package/dist/ui/kid/voice-ui.js +316 -0
  679. package/dist/ui/kid/voice-view.d.ts +71 -0
  680. package/dist/ui/kid/voice-view.js +75 -0
  681. package/dist/ui/kid/word-draw.d.ts +2 -0
  682. package/dist/ui/kid/word-draw.js +107 -0
  683. package/dist/ui/kid/word-emoji.d.ts +10 -0
  684. package/dist/ui/kid/word-emoji.js +66 -0
  685. package/dist/ui/kid/word-layout.d.ts +25 -0
  686. package/dist/ui/kid/word-layout.js +29 -0
  687. package/dist/ui/parent/parent-home-page.d.ts +1 -0
  688. package/dist/ui/parent/parent-home-page.js +414 -0
  689. package/dist/ui/parent/parent-page.d.ts +2 -0
  690. package/dist/ui/parent/parent-page.js +507 -0
  691. package/dist/ui/shared/brand.d.ts +36 -0
  692. package/dist/ui/shared/brand.js +51 -0
  693. package/dist/ui/shared/favicon.d.ts +12 -0
  694. package/dist/ui/shared/favicon.js +96 -0
  695. package/dist/ui/shared/icon.d.ts +9 -0
  696. package/dist/ui/shared/icon.js +35 -0
  697. package/dist/ui/shared/inline-lib.d.ts +1 -0
  698. package/dist/ui/shared/inline-lib.js +28 -0
  699. package/dist/ui/shared/logo.d.ts +31 -0
  700. package/dist/ui/shared/logo.js +43 -0
  701. package/dist/ui/shared/page-dom.d.ts +12 -0
  702. package/dist/ui/shared/page-dom.js +36 -0
  703. package/dist/ui/shared/png.d.ts +1 -0
  704. package/dist/ui/shared/png.js +45 -0
  705. package/dist/ui/shared/qr-svg.d.ts +3 -0
  706. package/dist/ui/shared/qr-svg.js +32 -0
  707. package/dist/ui/shared/qr.d.ts +14 -0
  708. package/dist/ui/shared/qr.js +281 -0
  709. package/dist/ui/shared/reel-dock.d.ts +22 -0
  710. package/dist/ui/shared/reel-dock.js +365 -0
  711. package/dist/ui/shared/tutor-color.d.ts +15 -0
  712. package/dist/ui/shared/tutor-color.js +47 -0
  713. package/dist/ui/talk/page.d.ts +6 -0
  714. package/dist/ui/talk/page.js +294 -0
  715. package/grades/grade-2/README.md +11 -0
  716. package/grades/grade-2/VALIDATION.md +41 -0
  717. package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273.md +157 -0
  718. package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273./344/276/213/345/255/220.md +78 -0
  719. package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/200/237/344/274/236.jpg +0 -0
  720. package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/260/217/347/214/264/346/221/230/346/241/203.jpg +0 -0
  721. package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235.md +64 -0
  722. package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235./344/276/213/345/255/220.md +31 -0
  723. package/grades/grade-2/agents/chinese-tutor/activities//345/220/254/345/206/231.md +48 -0
  724. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +79 -0
  725. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  726. package/grades/grade-2/agents/chinese-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
  727. package/grades/grade-2/agents/chinese-tutor/activities//346/211/271/346/224/271.md +81 -0
  728. package/grades/grade-2/agents/chinese-tutor/activities//346/211/271/346/224/271./344/276/213/345/255/220.md +35 -0
  729. package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235.md +94 -0
  730. package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235./344/276/213/345/255/220.md +63 -0
  731. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +82 -0
  732. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  733. package/grades/grade-2/agents/chinese-tutor.md +59 -0
  734. package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276.md +149 -0
  735. package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276./344/276/213/345/255/220.md +13 -0
  736. package/grades/grade-2/agents/english-tutor/activities//345/220/254/345/206/231.md +47 -0
  737. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +78 -0
  738. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  739. package/grades/grade-2/agents/english-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
  740. package/grades/grade-2/agents/english-tutor/activities//346/211/271/346/224/271.md +80 -0
  741. package/grades/grade-2/agents/english-tutor/activities//346/211/271/346/224/271./344/276/213/345/255/220.md +35 -0
  742. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +86 -0
  743. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  744. package/grades/grade-2/agents/english-tutor.md +57 -0
  745. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +78 -0
  746. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  747. package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202.md +56 -0
  748. package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202./344/276/213/345/255/220.md +52 -0
  749. package/grades/grade-2/agents/math-tutor/activities//346/211/271/346/224/271.md +80 -0
  750. package/grades/grade-2/agents/math-tutor/activities//346/211/271/346/224/271./344/276/213/345/255/220.md +35 -0
  751. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +81 -0
  752. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  753. package/grades/grade-2/agents/math-tutor.md +51 -0
  754. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/index.json +44 -0
  755. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-1.mp3 +0 -0
  756. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-2.mp3 +0 -0
  757. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-3.mp3 +0 -0
  758. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-4.mp3 +0 -0
  759. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-5.mp3 +0 -0
  760. package/grades/grade-2/examples/2026-10-27-pingjunfen/bake.json +1 -0
  761. package/grades/grade-2/examples/2026-10-27-pingjunfen/manifest.json +63 -0
  762. package/grades/grade-2/examples/2026-10-27-pingjunfen/scene.json +247 -0
  763. package/grades/grade-2/examples/2026-10-27-pingjunfen.md +40 -0
  764. package/grades/grade-2/examples/2026-10-27-pingjunfen.ts +26 -0
  765. package/grades/grade-2/examples//345/271/263/345/235/207/345/210/206./344/276/213/345/255/220.md +24 -0
  766. package/grades/grade-2/examples//346/225/260/345/255/246/345/271/263/345/235/207/345/210/206.md +42 -0
  767. package/grades/grade-2/validation/2026-10-27.json +4410 -0
  768. package/package.json +32 -12
  769. package/skills/chalk-cli/SKILL.md +62 -0
  770. package/skills/chalk-scene/SKILL.md +91 -0
  771. package/skills/chalk-teaching/SKILL.md +82 -0
  772. package/skills/chalk-teaching/models//344/272/244/351/224/231/346/225/260/345/210/227/345/210/206/350/241/214.md +8 -0
  773. package/skills/chalk-teaching/models//345/207/221/345/215/201.md +7 -0
  774. package/skills/chalk-teaching/models//345/221/250/346/234/237/350/247/204/345/276/213.md +6 -0
  775. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/345/242/236/345/207/217.md +6 -0
  776. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/244/247/346/225/260/346/261/202/345/260/217/346/225/260.md +7 -0
  777. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/267/256.md +6 -0
  778. package/skills/chalk-teaching/models//345/260/217/346/243/222/350/277/233/344/275/215/345/212/240.md +7 -0
  779. package/skills/chalk-teaching/models//345/260/217/346/243/222/351/200/200/344/275/215/345/207/217.md +6 -0
  780. package/skills/chalk-teaching/models//346/216/222/351/230/237/347/202/271/351/230/265.md +7 -0
  781. package/skills/chalk-teaching/models//346/227/266/351/227/264/346/225/260/350/275/264.md +6 -0
  782. package/skills/chalk-teaching/models//346/245/274/345/261/202/351/227/264/351/232/224/345/233/276.md +7 -0
  783. package/skills/chalk-teaching/models//347/240/264/345/215/201.md +7 -0
  784. package/skills/chalk-teaching/models//347/253/226/345/274/217/351/200/200/344/275/215.md +6 -0
  785. package/skills/chalk-teaching/models//347/272/277/346/256/265/345/233/276.md +7 -0
  786. package/skills/chalk-teaching/models-index.md +24 -0
  787. package/skills/chalk-teaching/references//350/256/262/350/247/243/346/250/241/346/235/277/344/270/211/346/241/243.md +32 -0
  788. package/skills/chalk-teaching/references//350/257/264/346/230/216/346/226/207/344/273/266.md +102 -0
  789. package/skills/chalk-verify/SKILL.md +57 -0
  790. package/skills/chalk-verify/references//350/257/204/345/210/244/346/240/207/345/207/206.md +69 -0
  791. package/skills/cotutor-analyze/SKILL.md +106 -0
  792. package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +31 -0
  793. package/skills/cotutor-board/SKILL.md +373 -0
  794. package/skills/cotutor-board/references/README.md +38 -0
  795. package/skills/cotutor-board/references/canvas.md +68 -0
  796. package/skills/cotutor-board/references/choice.md +55 -0
  797. package/skills/cotutor-board/references/code.md +46 -0
  798. package/skills/cotutor-board/references/image.md +52 -0
  799. package/skills/cotutor-board/references/lecture.md +69 -0
  800. package/skills/cotutor-board/references/paint.md +77 -0
  801. package/skills/cotutor-board/references/photo.md +61 -0
  802. package/skills/cotutor-board/references/read.md +51 -0
  803. package/skills/cotutor-board/references/text.md +69 -0
  804. package/skills/cotutor-board/references/tianzige.md +78 -0
  805. package/skills/cotutor-board/references/word.md +91 -0
  806. package/skills/cotutor-home/SKILL.md +169 -0
  807. package/skills/cotutor-home/references/dictation.md +97 -0
  808. package/skills/cotutor-home/references/tutor.md +148 -0
  809. package/skills/cotutor-home/references//345/221/275/344/273/244.md +21 -0
  810. package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +142 -0
  811. package/skills/cotutor-tune/SKILL.md +147 -0
  812. package/skills/cotutor-tune/references//345/255/227/346/256/265.md +50 -0
  813. package/skills/cotutor-tutor/SKILL.md +261 -0
  814. package/skills/cotutor-tutor/references//344/270/212/344/270/213/346/226/207/345/214/205.md +78 -0
  815. package/themes/default/kid.css +86 -0
  816. package/themes/default/theme.json +26 -0
  817. package/agents/chinese-teacher.md +0 -19
  818. package/agents/homework-aide.md +0 -17
  819. package/agents/math-teacher.md +0 -19
  820. package/agents/planner.md +0 -18
  821. package/agents/reading-teacher.md +0 -19
  822. package/dist/cli/doctor.d.ts +0 -19
  823. package/dist/cli/doctor.js +0 -190
  824. package/dist/cli/init.js +0 -114
  825. package/dist/cli/main.js +0 -123
  826. package/dist/cli/serve.d.ts +0 -19
  827. package/dist/cli/serve.js +0 -22
  828. package/dist/cli/skeleton.d.ts +0 -21
  829. package/dist/cli/skeleton.js +0 -82
  830. package/dist/cli/workspace.d.ts +0 -61
  831. package/dist/lib/agent-file.d.ts +0 -10
  832. package/dist/lib/agent-file.js +0 -16
  833. package/dist/lib/context-pack.js +0 -46
  834. package/dist/lib/conversation.d.ts +0 -28
  835. package/dist/lib/conversation.js +0 -44
  836. package/dist/lib/kid-view.d.ts +0 -26
  837. package/dist/lib/kid-view.js +0 -38
  838. package/dist/lib/ledger.d.ts +0 -21
  839. package/dist/lib/sections.d.ts +0 -12
  840. package/dist/lib/sections.js +0 -151
  841. package/dist/lib/transcript.d.ts +0 -36
  842. package/dist/lib/transcript.js +0 -95
  843. package/dist/server/app.d.ts +0 -13
  844. package/dist/server/app.js +0 -45
  845. /package/dist/{cli → app/cli}/main.d.ts +0 -0
@@ -0,0 +1,617 @@
1
+ /**
2
+ * cotutor doctor:环境 + workspace逐项体检,失败项附修复命令(错误信息即修复指南)。
3
+ * 静默失败摆到明面:配置坏了、老师链断了、账本有坏行、角色指向踩空。
4
+ * exit 约定同 drawtell / voxtell doctor:必需项全过 exit 0,否则 1;--json 带 ok 与整份 checks。
5
+ */
6
+ import { isRealtimeVoice } from "../../realtime/voices.js";
7
+ import { dashscopeAuth } from "../../realtime/bailian.js";
8
+ import { TTS_MODEL, ttsTarget } from "../../media/tts.js";
9
+ import { adapterFor, parserOf, prepareCli, processEnv, runtimeIssue } from "../../brain/index.js";
10
+ import { execFile } from 'node:child_process';
11
+ import { access, lstat, readFile, readdir, stat } from 'node:fs/promises';
12
+ import { constants as fsConstants } from 'node:fs';
13
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
14
+ import { promisify } from 'node:util';
15
+ import { boardPreloaded, planRun, runtimeUses } from "../../brain/cli/run-plan.js";
16
+ import { BOARD_GUIDE_PATH, boardGuideBody, takesTutorRules } from "../../runtime/context/tutor-rules.js";
17
+ import { parseAgentFile } from "../../runtime/agent-file.js";
18
+ import { localDate } from "../../shared/time.js";
19
+ import { MIN_CONTRAST, contrastWithWhite } from "../../ui/shared/tutor-color.js";
20
+ import { memoryPath, missingEntry, pickNotes } from "../../memory/vault-notes.js";
21
+ import { SKILLS_DIR, isApiRuntime, listTutors, mouthVoice, resolvePolicy, tutorFilePath } from "../../schema/index.js";
22
+ import { kindsFor, tutorCardKinds } from "../../board/cards/index.js";
23
+ import { scanVault } from "../../memory/vault.js";
24
+ import { parseArtifactEvents } from "../../runtime/journal/ledger.js";
25
+ import { parseTimetable } from "../../memory/timetable.js";
26
+ import { configGapsOf, legacyLayout } from "./migrate.js";
27
+ import { httpsFiles } from "./serve.js";
28
+ import { readPin } from "../http/access.js";
29
+ import { gradeOfSemester } from "../../memory/grade.js";
30
+ import { DIRS } from "./skeleton.js";
31
+ import { tutorStatuses, readManifest } from "./tutors.js";
32
+ import { tokenText } from "../../shared/tokens.js";
33
+ import { workspaceGradePack } from "./grades.js";
34
+ import { CONFIG_FILE, ConfigError, USER_CONFIG, UsageError, assembleWorkspace, expandPath, parseConfig, readJson, redactHome, resolveRoot, } from "../workspace.js";
35
+ const execFileP = promisify(execFile);
36
+ /** doctor --live 发给老师的那句 */
37
+ const LIVE_PROMPT = '只回一个字:好';
38
+ async function statOrNull(p) {
39
+ return stat(p).catch(() => null);
40
+ }
41
+ async function evictedCount(dir) {
42
+ try {
43
+ return (await readdir(dir)).filter((f) => f.endsWith('.icloud')).length;
44
+ }
45
+ catch {
46
+ return 0;
47
+ }
48
+ }
49
+ /** 运行时的 CLI 名(run[0] 的 basename) */
50
+ export function runtimeCli(run) {
51
+ return basename(run[0] ?? '');
52
+ }
53
+ /** 已知的环境坑,从 CLI 的输出里认出来给修复命令(错误信息即修复指南) */
54
+ export function explainLlmFailure(text, runtime) {
55
+ const bin = runtimeCli(runtime);
56
+ if (/does not support this model|version [\d.]+ or newer is required/i.test(text)) {
57
+ return `这版 ${bin} 不认模型:检查 cotutor.json 的运行时 run / resume 中 -m 后的模型名,或运行 ${bin} update 升级`;
58
+ }
59
+ if (/403|request not allowed/i.test(text))
60
+ return `API 拒了(403):检查 ${bin} 使用的千问 key、端点与模型权限`;
61
+ if (/not logged in|login|authentication|401|unauthorized/i.test(text))
62
+ return `${bin} 的 key 无效:检查 QWENCLOUD_TOKEN_PLAN_API_KEY 或 DASHSCOPE_API_KEY,再跑 cotutor doctor`;
63
+ if (/ENOENT|command not found/i.test(text))
64
+ return `PATH 里没有 ${bin}`;
65
+ return `看上面的原文;不认识的错先在终端手跑一次同样的命令`;
66
+ }
67
+ /**
68
+ * --live:真起一次老师(运行时的 run 模板,第一位开着的老师,只回一个字),把 API 层的坑(模型不认、没登录)摆到明面。
69
+ * 花一分钱级别的费用,所以缺省不跑。配音同理:老师配了 voice 就合成一句。
70
+ */
71
+ async function probeLive(ws, push, env) {
72
+ const { spawn } = await import('node:child_process');
73
+ const { listTutors } = await import("../../schema/index.js");
74
+ const { synthesize } = await import("../../media/tts.js");
75
+ const { parseTranscript } = await import("../../brain/transcript.js");
76
+ const { parseAgentFile } = await import("../../runtime/agent-file.js");
77
+ const { tmpdir } = await import('node:os');
78
+ const { mkdtemp, rm } = await import('node:fs/promises');
79
+ const tutors = listTutors(ws.config).filter((t) => t.enabled);
80
+ const first = tutors[0];
81
+ if (!first) {
82
+ push({ name: 'live.agent', ok: false, required: false, detail: '没有开着的老师,没法探', fix: 'cotutor.json 里至少开一位' });
83
+ return;
84
+ }
85
+ const runtimeName = ws.config.runtimes.default;
86
+ const runtime = ws.config.runtimes[runtimeName];
87
+ if (!runtime || typeof runtime === 'string')
88
+ return;
89
+ if (isApiRuntime(runtime)) {
90
+ push({ name: `live.${runtimeName}`, ok: true, required: false, detail: `API 运行时(百炼 ${runtime.model}),不起 CLI;真跑一轮用 cotutor send` });
91
+ return;
92
+ }
93
+ if (runtimeIssue(runtime))
94
+ return; // 静态体检已报退役,不启动旧 CLI
95
+ let agentBody;
96
+ try {
97
+ agentBody = parseAgentFile(await readFile(tutorFilePath(ws.dirs.agents, first.name), 'utf8')).body;
98
+ }
99
+ catch {
100
+ /* 上面 tutor.* 已报 */
101
+ }
102
+ let boardBody;
103
+ try {
104
+ boardBody = boardGuideBody(await readFile(join(ws.root, BOARD_GUIDE_PATH), 'utf8'));
105
+ }
106
+ catch {
107
+ /* skills.* 已报 */
108
+ }
109
+ const systemBody = agentBody !== undefined ? (boardBody && takesTutorRules(first.name) ? `${agentBody}\n\n${boardBody}` : agentBody) : undefined;
110
+ // 和真跑同一条规划:命令行、stdin 那行、环境变量都由这个运行时的 CLI 适配器定
111
+ const plan = planRun(ws.config, { session: null }, { agent: first.name, prompt: LIVE_PROMPT, agentBody, systemBody, boardFile: join(ws.root, BOARD_GUIDE_PATH), runtime: runtimeName, root: ws.root, readDirs: [...new Set([ws.root, ws.paths.vault])] });
112
+ const { argv } = plan;
113
+ const parse = parserOf(plan.cli);
114
+ await prepareCli(plan.cli, ws.root);
115
+ const cwd = join(ws.dirs.agents, first.name);
116
+ const run = await new Promise((resolveRun) => {
117
+ let out = '';
118
+ let err = '';
119
+ const child = spawn(argv[0], argv.slice(1), { cwd, env: processEnv(plan, { ...env, COTUTOR_WORKSPACE: ws.root }), stdio: [plan.stdin ? 'pipe' : 'ignore', 'pipe', 'pipe'] });
120
+ const timer = setTimeout(() => child.kill(), 120_000);
121
+ // 消息走 stdin 的运行时:写一条,看到收尾就关 stdin
122
+ if (plan.stdin) {
123
+ child.stdin?.on('error', () => { });
124
+ child.stdin?.write(plan.stdin);
125
+ }
126
+ let rest = '';
127
+ child.stdout?.on('data', (d) => {
128
+ out += d.toString();
129
+ rest += d.toString();
130
+ const lines = rest.split('\n');
131
+ rest = lines.pop() ?? '';
132
+ if (plan.stdin && lines.some((l) => parse(l).some((e) => e.kind === 'result')))
133
+ child.stdin?.end();
134
+ });
135
+ child.stderr?.on('data', (d) => (err += d.toString()));
136
+ child.once('error', (e) => {
137
+ clearTimeout(timer);
138
+ resolveRun({ out, err, code: null, spawnErr: e.message });
139
+ });
140
+ child.once('close', (code) => {
141
+ clearTimeout(timer);
142
+ resolveRun({ out, err, code });
143
+ });
144
+ });
145
+ const t = parseTranscript(run.out, parse);
146
+ const said = t.items.filter((i) => i.kind === 'text' && !i.sub).map((i) => i.text).join(' ');
147
+ const ok = Boolean(t.final?.ok) && !run.spawnErr;
148
+ const raw = run.spawnErr ?? (t.final ? `${t.final.reason ?? ''} ${said}`.trim() : `没有 result 事件(exit ${run.code}):${run.err.trim().split('\n').slice(-3).join(' / ')}`);
149
+ push({
150
+ name: `live.${runtimeName}`,
151
+ ok,
152
+ required: false,
153
+ detail: ok ? `${first.display} 回了「${(t.final?.text ?? '').slice(0, 20)}」${t.final?.inTokens !== undefined ? ` · ${tokenText({ in: t.final.inTokens, out: t.final.outTokens ?? 0 })}` : ''}` : `起不来或没答上:${raw.slice(0, 300)}`,
154
+ fix: ok ? undefined : explainLlmFailure(raw, runtime.run),
155
+ });
156
+ const voiced = tutors.find((x) => x.voice);
157
+ if (!voiced) {
158
+ push({ name: 'live.tts', ok: true, required: false, detail: '没有老师配 voice,不探配音(点读、单词卡没有声音)' });
159
+ return;
160
+ }
161
+ // 缓存放临时目录:真合成一次,不拿 workspace 里存着的那份充数
162
+ const dir = await mkdtemp(join(tmpdir(), 'cotutor-doctor-'));
163
+ const r = await synthesize({ text: '你好', voice: voiced.voice }, { cacheDir: dir, env });
164
+ const size = r.file ? ((await stat(r.file).catch(() => null))?.size ?? 0) : 0;
165
+ await rm(dir, { recursive: true, force: true });
166
+ push({ name: 'live.tts', ok: Boolean(r.file), required: false, detail: r.file ? `${voiced.display} 的音色 ${voiced.voice} 能合成(${size} 字节)` : `${voiced.display} 的音色 ${voiced.voice} 没合成:${r.error}`, fix: r.file ? undefined : '家长端「首页」里填百炼的 key 与业务空间 id;音色到工作台音色页「卡的配音」重挑' });
167
+ }
168
+ export async function doctorWorkspace(override, opts = {}) {
169
+ const probeEnv = opts.probeEnv ?? true;
170
+ const env = opts.env ?? process.env;
171
+ const checks = [];
172
+ const push = (c) => checks.push(c);
173
+ const [maj, min] = process.versions.node.split('.').map(Number);
174
+ const nodeOk = maj > 22 || (maj === 22 && min >= 18);
175
+ push({ name: 'node', ok: nodeOk, required: true, detail: `node ${process.versions.node}`, fix: nodeOk ? undefined : '需要 Node ≥ 22.18' });
176
+ let root = null;
177
+ let source = null;
178
+ try {
179
+ ({ root, source } = resolveRoot(override, opts));
180
+ }
181
+ catch (err) {
182
+ if (!(err instanceof UsageError))
183
+ throw err;
184
+ push({
185
+ name: err instanceof ConfigError ? 'user-config' : 'workspace',
186
+ ok: false,
187
+ required: true,
188
+ detail: err.message,
189
+ fix: err instanceof ConfigError ? `修复 ${redactHome(err.file)} 的 JSON,或删掉它` : 'cotutor init <slug>,或用 --workspace / COTUTOR_WORKSPACE 指定',
190
+ });
191
+ return { ok: false, root: null, source: null, checks };
192
+ }
193
+ push({ name: 'workspace', ok: true, required: true, detail: `${redactHome(root)}(来源:${source})` });
194
+ // ---- cotutor.json ----
195
+ let ws = null;
196
+ const configFile = join(root, CONFIG_FILE);
197
+ try {
198
+ const raw = readJson(configFile);
199
+ if (raw === null) {
200
+ push({ name: CONFIG_FILE, ok: false, required: true, detail: '不存在', fix: `cotutor init <slug> --dir ${redactHome(root)}(已有文件不动)` });
201
+ }
202
+ else {
203
+ const config = parseConfig(raw, configFile);
204
+ ws = assembleWorkspace(root, source, config);
205
+ push({
206
+ name: CONFIG_FILE,
207
+ ok: true,
208
+ required: true,
209
+ detail: `kid ${config.kid.slug}、${Object.keys(config.tutors).length} 位老师、运行时 ${config.runtimes.default}、端口 ${config.server.port}`,
210
+ });
211
+ }
212
+ }
213
+ catch (err) {
214
+ if (!(err instanceof ConfigError))
215
+ throw err;
216
+ push({ name: CONFIG_FILE, ok: false, required: true, detail: err.message, fix: '按上面逐条修;政策文件机器不重建,改坏了靠 git 回退' });
217
+ }
218
+ let teachingPack = null;
219
+ try {
220
+ const manifest = await readManifest(root);
221
+ teachingPack = await workspaceGradePack(root);
222
+ push({ name: 'teachingPack', ok: true, required: true, detail: `${teachingPack.grade} 年级教学包 · ${redactHome(teachingPack.dir)}` });
223
+ if (!manifest.teachingPack)
224
+ push({ name: 'teachingPack.origin', ok: false, required: false, detail: '旧版出厂内容按二年级来源读取,尚未补记;孩子档案不变', fix: 'cotutor upgrade 补记来源,保留自定义文件与原 hash' });
225
+ }
226
+ catch (err) {
227
+ push({ name: 'teachingPack', ok: false, required: true, detail: err.message, fix: '检查 .cotutor/shipped.json 的 teachingPack 与本包 grades/;恢复原来源记录或安装包含该年级的版本,不要改成其他年级绕过' });
228
+ }
229
+ // ---- 配置迁移:包更新带来的新出厂件(老师 / 运行时 / 命令模板旗标)不会自己进政策文件,缺了是静默的 ----
230
+ {
231
+ const gaps = await configGapsOf(root);
232
+ push({
233
+ name: 'config.migrate',
234
+ ok: gaps.length === 0,
235
+ required: false,
236
+ detail: gaps.length ? `${gaps.length} 项要改:${gaps.map((g) => g.detail).join(';')}` : 'cotutor.json 有出厂模板里的全部老师、运行时与旗标,也没有不再用的段',
237
+ fix: gaps.length ? 'cotutor upgrade --config --dry-run 先看,再 cotutor upgrade --config 改(只加缺的、删不再用的出厂段,你改过的值不动)' : undefined,
238
+ });
239
+ }
240
+ // ---- 骨架 ----
241
+ const missing = [];
242
+ for (const d of DIRS)
243
+ if (!(await statOrNull(join(root, d)))?.isDirectory())
244
+ missing.push(`${d}/`);
245
+ push({
246
+ name: 'skeleton',
247
+ ok: missing.length === 0,
248
+ required: true,
249
+ detail: missing.length ? `缺 ${missing.join(' ')}` : [...DIRS].map((d) => `${d}/`).join(' '),
250
+ fix: missing.length ? `cotutor init <slug> --dir ${redactHome(root)}(幂等补缺)` : undefined,
251
+ });
252
+ // 老布局(2026-10-30 之前):老师文件在 .claude/agents/、技能在 .claude/skills/,现在都读不到了
253
+ const legacy = await legacyLayout(root);
254
+ if (legacy.length)
255
+ push({ name: 'layout', ok: false, required: true, detail: `${legacy.join(' ')} 是老位置:老师文件现在在 agents/<老师>.md,技能在 ${SKILLS_DIR}/`, fix: 'cotutor upgrade(原样挪过去,不改内容;新位置已有同名的不挪、会点名)' });
256
+ // 家规(根的 QWEN.md)2026-09-15 起不出厂:有就是家长自建的共同规矩,提一句;没有不缺
257
+ const rulesThere = (await Promise.all(['QWEN.md'].map(async (f) => ((await statOrNull(join(root, f))) ? f : null)))).filter(Boolean);
258
+ push({ name: 'rules', ok: true, required: false, detail: rulesThere.length ? `家长自建的家规 ${rulesThere.join(' ')} 在,所有老师每轮都读` : '没有根目录的 QWEN.md(不需要:老师要知道的都在老师文件与技能里)' });
259
+ if (ws) {
260
+ // ---- 老师:定义文件(拷贝)+ 名字一致 + 出厂 / 自定义状态 + 老师目录 ----
261
+ // 老师文件只有 agents/<name>.md 这一份:应用读正文递进系统提示({agentBody} / {systemBody})
262
+ const statuses = new Map((teachingPack ? await tutorStatuses(root) : []).map((s) => [s.name, s]));
263
+ for (const name of Object.keys(ws.config.tutors)) {
264
+ const shipped = statuses.has(name);
265
+ const file = tutorFilePath(ws.dirs.agents, name);
266
+ let detail;
267
+ let ok = false;
268
+ try {
269
+ const { frontmatter } = parseAgentFile(await readFile(file, 'utf8'));
270
+ ok = frontmatter.name === name;
271
+ detail = ok ? `${redactHome(file)} name 一致` : `${redactHome(file)} 的 frontmatter name 是 "${frontmatter.name ?? ''}",与老师键 ${name} 不一致`;
272
+ }
273
+ catch {
274
+ detail = `${redactHome(file)} 读不到`;
275
+ }
276
+ push({
277
+ name: `tutor.${name}.file`,
278
+ ok,
279
+ required: true,
280
+ detail,
281
+ fix: ok
282
+ ? undefined
283
+ : shipped
284
+ ? `cotutor init 补拷(拷自本包 agents/${name}.md),或把老师键改成文件里的 name`
285
+ : `这是自家加的老师:写 agents/${name}.md(frontmatter name: ${name}),cotutor add ${name} --display <显示名> 可出模板;或把 cotutor.json 里这条删掉`,
286
+ });
287
+ const st = statuses.get(name);
288
+ if (!st)
289
+ push({ name: `tutor.${name}.origin`, ok: true, required: false, detail: teachingPack ? '自家加的老师(不是出厂件,upgrade 不碰)' : '出厂来源无法确认,先修复 teachingPack 检查项' });
290
+ if (st) {
291
+ const label = { latest: '出厂件,最新', upgradable: `出厂件,基于 ${st.basedOn},包已更新`, custom: `自定义(基于 ${st.basedOn})`, untracked: '自定义(没有出厂记录)', missing: '缺', broken: '读不到' };
292
+ push({
293
+ name: `tutor.${name}.origin`,
294
+ ok: st.state !== 'upgradable',
295
+ required: false,
296
+ detail: label[st.state],
297
+ fix: st.state === 'upgradable' ? 'cotutor upgrade 换新版' : undefined,
298
+ });
299
+ }
300
+ const home = join(ws.dirs.agents, name);
301
+ const there = (await statOrNull(home))?.isDirectory() ?? false;
302
+ push({ name: `tutor.${name}.home`, ok: there, required: true, detail: there ? `agents/${name}/ 在(会话 cwd)` : `agents/${name}/ 不在`, fix: there ? undefined : 'cotutor init 补建' });
303
+ // 音色:一老师一音色走 API 是正路;没配的孩子端只剩浏览器合成声(只该在测试里);hidden 的老师不对孩子说话,不用音色
304
+ const tc = ws.config.tutors[name];
305
+ if (tc.enabled && !tc.hidden && !tc.voice)
306
+ push({ name: `tutor.${name}.voice`, ok: false, required: false, detail: `${tc.display} 没配音色(cotutor.json tutors.${name}.voice),卡上的声音(点读卡、单词卡)配不了——只适合测试`, fix: '工作台 /dev 的「音色」页「卡的配音」能听着挑(挑中直接写进去)' });
307
+ // 卡的清单(《卡片协议.md》「谁拿到哪些卡」):有脸的老师才递板书写法;只是报一声讲哪几种
308
+ if (takesTutorRules(name)) {
309
+ const kinds = tutorCardKinds(tc.cards);
310
+ push({ name: `tutor.${name}.cards`, ok: true, required: false, detail: kinds ? `板书写法讲 ${kinds.join(' ')}` : `没写 cards,板书写法讲全部 ${kindsFor('board').length} 种` });
311
+ }
312
+ // 头像是图片路径时(figshot 写的 avatars/<name>.png)查文件在不在、在不在根以内;emoji 不查
313
+ if (tc.avatar && /\.(png|jpe?g|webp|gif|svg)$/i.test(tc.avatar)) {
314
+ const file = resolve(root, tc.avatar);
315
+ const inside = !tc.avatar.startsWith('/') && file.startsWith(root + sep);
316
+ const there = inside && ((await statOrNull(file))?.isFile() ?? false);
317
+ push({ name: `tutor.${name}.avatar`, ok: there, required: false, detail: there ? `头像 ${tc.avatar}` : inside ? `头像 ${tc.avatar} 不在,孩子端退回显示首字` : `头像 ${tc.avatar} 在 workspace 根之外,页面不给`, fix: there ? undefined : `figshot pick --workspace ${redactHome(root)} 存一张,或把 cotutor.json 的 tutors.${name}.avatar 改回 emoji` });
318
+ }
319
+ }
320
+ {
321
+ // 舞台包:小课堂 / 画板卡要它;没有只是重卡打不开,轻卡与对话照常。做课包(cotutor chalk bake / snap)与服务端烤画面也开它,在本机的 Chrome 里
322
+ const { stageBuilt } = await import("../http/stage.js");
323
+ const built = stageBuilt();
324
+ push({ name: 'stage.bundle', ok: built, required: false, detail: built ? 'dist/stage/ 在(舞台包:小课堂与画板卡的舞台)' : 'dist/stage/ 不在,小课堂与画板卡的舞台打不开(轻卡照常)', fix: built ? undefined : '仓库根 pnpm run build:stage(npm 装的包自带)' });
325
+ const { chromePath } = await import("./chalk/browser.js");
326
+ const chrome = chromePath(env);
327
+ push({ name: 'chalk.chrome', ok: chrome !== null, required: false, detail: chrome ? `本机的 Chrome 在(${redactHome(chrome)}):做课包截图、烤画面用它` : '找不到 Chrome:cotutor chalk bake / snap 做不了,没烤过的课包孩子端在浏览器里现画(慢、iPad 上可能不顺)', fix: chrome ? undefined : '装 Google Chrome,或环境变量 CHROME 指到 Chrome / Chromium 的可执行文件' });
328
+ const { CLAUDE_SKILLS_LINK, skillStatuses } = await import("./skills.js");
329
+ for (const sk of teachingPack ? await skillStatuses(root) : []) {
330
+ const label = { latest: sk.machine ? '机器件,最新' : '出厂件,最新', upgradable: sk.machine ? '机器件,和包里不一样(改过或包已更新)' : `出厂件,基于 ${sk.basedOn},包已更新`, custom: `自定义(基于 ${sk.basedOn})`, untracked: '自定义(没有出厂记录)', missing: sk.machine ? '缺(机器件,老师或家长的技能靠它)' : '缺' };
331
+ push({ name: `skill.${sk.name}`, ok: sk.state !== 'missing' && sk.state !== 'upgradable', required: Boolean(sk.machine), detail: `${SKILLS_DIR}/${sk.name}/:${label[sk.state]}`, fix: sk.state === 'missing' ? 'cotutor init 补拷' : sk.state === 'upgradable' ? 'cotutor upgrade 换新版' : undefined });
332
+ }
333
+ // Claude Code 只认 .claude/skills:那里该是指向 .agents/skills 的链;老师用不到,家长在 workspace 里用 Claude Code 才要
334
+ const linked = (await lstat(join(root, CLAUDE_SKILLS_LINK)).catch(() => null))?.isSymbolicLink() ?? false;
335
+ push({ name: 'skills.claude', ok: linked, required: false, detail: linked ? `${CLAUDE_SKILLS_LINK} 链到 ${SKILLS_DIR}(Claude Code 读的是同一份)` : `${CLAUDE_SKILLS_LINK} 不是链,在 workspace 里用 Claude Code 看不到 ${SKILLS_DIR}/ 的技能`, fix: linked ? undefined : 'cotutor upgrade 建链(是实体目录的话先把里面的挪进 .agents/skills/ 再删掉)' });
336
+ // ---- activity:老师的家里 agents/<老师>/activities/<名>.md;出厂的看新旧,每份都过一遍 activity check ----
337
+ {
338
+ const { activityStatuses, checkActivities } = await import("./activities.js");
339
+ const states = new Map((teachingPack ? await activityStatuses(root) : []).map((s) => [s.key, s]));
340
+ const label = { latest: '出厂件,最新', upgradable: '出厂件,包已更新', custom: '自定义', untracked: '自定义(没有出厂记录)', missing: '缺', broken: '读不到' };
341
+ for (const s of states.values())
342
+ if (s.state === 'missing')
343
+ push({ name: `activity.${s.key}`, ok: false, required: false, detail: `agents/${s.key.replace('/', '/activities/')}.md 不在`, fix: 'cotutor init 补拷' });
344
+ // 学科色:手改 cotutor.json 写的颜色,新话题气泡的白字要压得住
345
+ for (const [name, t] of Object.entries(ws.config.tutors)) {
346
+ if (!t.color)
347
+ continue;
348
+ const cr = contrastWithWhite(t.color);
349
+ if (cr < MIN_CONTRAST)
350
+ push({ name: `tutors.${name}.color`, ok: false, required: false, detail: `${t.display}的学科色 ${t.color} 太浅:白字对比度 ${cr.toFixed(2)},要 ${MIN_CONTRAST} 以上,新话题气泡上的字看不清`, fix: `在工作台「老师团」里点一个色块,或删掉 cotutor.json 的 tutors.${name}.color 用自动的` });
351
+ }
352
+ for (const r of await checkActivities(root, ws.config.tutors)) {
353
+ const key = `${r.tutor}/${r.name}`;
354
+ // 老 workspace 改过老师文件:upgrade 只报 diff,里面同名那节还在,两处说法会打架
355
+ const agentText = await readFile(tutorFilePath(ws.dirs.agents, r.tutor), 'utf8').catch(() => '');
356
+ if (new RegExp(`^#{2,4}\\s*${r.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*$`, 'm').test(agentText))
357
+ push({ name: `activity.${key}.dup`, ok: false, required: false, detail: `agents/${r.tutor}.md 里还有一节「${r.name}」,和 activity 文件两处说法会打架`, fix: `删掉老师文件里那一节(做法以 agents/${r.tutor}/activities/${r.name}.md 为准)` });
358
+ const st = states.get(key);
359
+ const fixes = r.issues.filter((i) => i.level === 'fix');
360
+ const origin = st ? label[st.state] + (st.basedOn && st.state !== 'latest' ? `(基于 ${st.basedOn})` : '') : teachingPack ? '自家写的' : '出厂来源无法确认';
361
+ push({
362
+ name: `activity.${key}`,
363
+ ok: !fixes.length && st?.state !== 'upgradable',
364
+ required: false,
365
+ detail: fixes.length ? `${origin};${fixes.length} 条要改,第一条:${fixes[0].line ? `第 ${fixes[0].line} 行 ` : ''}${fixes[0].text}` : origin,
366
+ fix: fixes.length ? `cotutor activity check ${r.tutor} ${r.name} 看全` : st?.state === 'upgradable' ? 'cotutor upgrade 换新版' : undefined,
367
+ });
368
+ }
369
+ }
370
+ // ---- 主题:孩子端板书的样子,themes/<kid.theme>/;清单要过契约、css 要在;坏了服务退回出厂 default,孩子端不会没样子 ----
371
+ {
372
+ const { readTheme, themeDir, themeStatuses } = await import("./themes.js");
373
+ const name = ws.config.kid.theme;
374
+ const dir = themeDir(root, name);
375
+ try {
376
+ const t = await readTheme(dir);
377
+ push({ name: 'theme.manifest', ok: true, required: false, detail: `themes/${name}/:${Object.keys(t.manifest.tints).length} 个底色槽、${Object.keys(t.manifest.looks).length} 个字形槽、${Object.keys(t.manifest.pens).length} 支笔,default = ${t.manifest.default}` });
378
+ }
379
+ catch (err) {
380
+ push({ name: 'theme.manifest', ok: false, required: false, detail: `themes/${name}/ 用不了(服务退回出厂 default):${err instanceof Error ? err.message : String(err)}`, fix: name === 'default' ? 'cotutor init 补拷(已有的不动)或 cotutor upgrade' : `修 themes/${name}/theme.json 与 kid.css,或把 cotutor.json 的 kid.theme 改回 default` });
381
+ }
382
+ for (const th of teachingPack ? await themeStatuses(root) : []) {
383
+ if (!th.shipped) {
384
+ push({ name: `theme.${th.name}.origin`, ok: true, required: false, detail: `themes/${th.name}/:自家的主题(不是出厂件,upgrade 不碰)` });
385
+ continue;
386
+ }
387
+ const label = { latest: '出厂件,最新', upgradable: `出厂件,基于 ${th.basedOn},包已更新`, custom: `自定义(基于 ${th.basedOn})`, untracked: '自定义(没有出厂记录)', missing: '缺' };
388
+ push({ name: `theme.${th.name}.origin`, ok: th.state !== 'missing' && th.state !== 'upgradable', required: false, detail: `themes/${th.name}/:${label[th.state]}`, fix: th.state === 'missing' ? 'cotutor init 补拷' : th.state === 'upgradable' ? 'cotutor upgrade 换新版' : undefined });
389
+ }
390
+ }
391
+ }
392
+ // ---- 首页:没发布 = 缺省首页;坏了孩子端退回缺省;超过 3 天提醒;引用坏了的按钮孩子端不出现 ----
393
+ {
394
+ const { daysSince, publishedIssues, readPublished } = await import("../../runtime/home/store.js");
395
+ const now = opts.now ?? new Date();
396
+ const { home, error } = await readPublished(ws);
397
+ if (error)
398
+ push({ name: 'home.published', ok: false, required: false, detail: `${error};孩子端退回缺省首页`, fix: '用 cotutor-home 技能排一份再 cotutor home publish,或删掉 home/published.json' });
399
+ else if (!home)
400
+ push({ name: 'home.published', ok: true, required: false, detail: '没发布过首页:孩子端是缺省首页(每位老师一张只有「新话题」的卡)' });
401
+ else {
402
+ const days = daysSince(home, now);
403
+ push({ name: 'home.published', ok: days <= 3, required: false, detail: `已发布 ${home.id}(${days === 0 ? '今天' : `${days} 天前`})· ${home.cards.length} 张卡`, fix: days > 3 ? '首页是好几天前的:孩子学完后在 workspace 根开编程助手,用 cotutor-home 技能排一份新的' : undefined });
404
+ const broken = await publishedIssues(ws, home, now);
405
+ push({ name: 'home.refs', ok: !broken.length, required: false, detail: broken.length ? broken.map((i) => i.text).join(';') : '已发布那份里的老师与话题都在', fix: broken.length ? '坏了的按钮孩子端不出现;要补就改草稿重发(cotutor home check 看细节)' : undefined });
406
+ }
407
+ }
408
+ // ---- paths 角色指向:配了就该在 ----
409
+ for (const [role, value] of Object.entries(ws.config.paths)) {
410
+ const target = ws.paths[role] ?? expandPath(value, ws.root);
411
+ const st = await statOrNull(target);
412
+ if (!st) {
413
+ push({ name: `paths.${role}`, ok: false, required: true, detail: `指向不存在:${redactHome(target)}`, fix: `mkdir -p 该目录,或修正 cotutor.json 的 paths.${role};指向 Obsidian vault 时确认 vault 已建、iCloud 已同步` });
414
+ continue;
415
+ }
416
+ const evicted = target.includes('Mobile Documents') && st.isDirectory() ? await evictedCount(target) : 0;
417
+ push({ name: `paths.${role}`, ok: evicted === 0, required: false, detail: `${redactHome(target)}${evicted ? `——有 ${evicted} 个 .icloud 占位` : ''}`, fix: evicted ? '关掉 iCloud「优化 Mac 储存空间」,或把该目录下载到本地' : undefined });
418
+ }
419
+ // ---- 账本:坏行必须响(不可再生,别删)----
420
+ try {
421
+ const { rows, errors } = parseArtifactEvents(await readFile(ws.files.artifacts, 'utf8'));
422
+ push({ name: 'ledger.artifacts', ok: errors.length === 0, required: true, detail: errors.length ? errors.slice(0, 3).join(';') : `${rows.length} 行`, fix: errors.length ? '修那几行,或 git checkout 回退;账本不可再生,不要删了重来' : undefined });
423
+ }
424
+ catch {
425
+ push({ name: 'ledger.artifacts', ok: true, required: false, detail: '没有(还没记过);cotutor init 会建空文件' });
426
+ }
427
+ // ---- 作业照片(R5):paths.captures 要在 workspace 根以内(页面经 /api/kid/image 取图只认根以内)、能写;还没拍过就不存在,第一张时建 ----
428
+ {
429
+ const cap = ws.paths.captures;
430
+ const inside = cap === ws.root || cap.startsWith(ws.root + sep);
431
+ let dir = cap;
432
+ while (!(await stat(dir).catch(() => null))?.isDirectory() && dirname(dir) !== dir)
433
+ dir = dirname(dir);
434
+ const writable = await access(dir, fsConstants.W_OK).then(() => true, () => false);
435
+ const there = dir === cap;
436
+ const n = there ? (await readdir(cap).catch(() => [])).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d)).length : 0;
437
+ push({ name: 'captures', ok: inside && writable, required: false, detail: !inside ? `${redactHome(cap)} 在 workspace 根以外,页面看不到照片(/api/kid/image 只给根以内的)` : !writable ? `${redactHome(cap)} 写不了(最近的已有目录 ${redactHome(dir)} 没有写权限)` : there ? `${redactHome(cap)}:${n} 天的作业照片` : `${redactHome(cap)} 还没有(第一张照片时建)`, fix: !inside ? '把 cotutor.json 的 paths.captures 改回根以内(缺省 captures)' : !writable ? '改目录权限,或换 paths.captures' : undefined });
438
+ }
439
+ // ---- vault(《obsidian仓库设计.md》2026-09-17):档案与每位老师的入口文件按 frontmatter 找,原文整篇进上下文包 ----
440
+ {
441
+ const { notes, files } = await scanVault(ws);
442
+ const today = localDate(opts.now ?? new Date());
443
+ const base = pickNotes(notes, files, { date: today });
444
+ const currentGrade = gradeOfSemester(base.semester);
445
+ if (teachingPack && base.semester)
446
+ push({
447
+ name: 'teachingPack.grade', ok: currentGrade === teachingPack.grade, required: false,
448
+ detail: `档案学期 ${base.semester};已安装 ${teachingPack.grade} 年级教学包`,
449
+ fix: currentGrade === teachingPack.grade ? undefined : '核对档案学期与教学内容;升年级不会自动换文件。用 cotutor grades 查看可用年级,可自行调整教学文件或贡献相应年级包',
450
+ });
451
+ const limit = resolvePolicy(ws.config, '').contextPack.entryChars;
452
+ push({
453
+ name: 'vault.profile',
454
+ ok: !!base.profile && !!base.semester && !base.extraProfiles.length,
455
+ required: false,
456
+ detail: !base.profile ? `${redactHome(ws.paths.vault)} 里没有 cotutor: profile 的档案;老师不知道孩子是谁、几年级` : `${base.profile.path} · 学期 ${base.semester ?? '算不出'}${base.extraProfiles.length ? ` · 另有 ${base.extraProfiles.join('、')} 也是 profile,没用` : ''}`,
457
+ fix: !base.profile ? '建一篇(文件名随意),frontmatter 写 cotutor: profile、nickname、birthday、school_start: 2025-09(读一年级的年月),正文写孩子的情况;或 cotutor init 补一篇' : !base.semester ? `在 ${base.profile.path} 的 frontmatter 加 school_start: YYYY-MM(读一年级的年月);特殊情况直接写 semester: 二年级上` : base.extraProfiles.length ? '只留一篇 cotutor: profile' : undefined,
458
+ });
459
+ for (const t of listTutors(ws.config).filter((x) => x.enabled)) {
460
+ const m = pickNotes(notes, files, { date: today, agent: t.name });
461
+ const big = !!m.memory && m.memory.text.length > limit;
462
+ push({
463
+ name: `vault.memory.${t.name}`,
464
+ ok: !big && !m.extraMemories.length,
465
+ required: false,
466
+ detail: m.memory ? `${m.memory.path}(${m.memory.text.length} 字${big ? `,超过 ${limit},会截断` : ''})${m.extraMemories.length ? ` · 另有 ${m.extraMemories.join('、')} 没用` : ''}` : `还没有;${t.display}第一次记东西时建 ${memoryPath(t.display)}`,
467
+ fix: big ? '在 Obsidian 里把记忆文件理一理:合并重复的、删过时的;或调 policy.contextPack.entryChars' : m.extraMemories.length ? `agent: ${t.name} 的记忆文件只留一篇` : undefined,
468
+ });
469
+ }
470
+ for (const t of listTutors(ws.config).filter((x) => x.enabled && x.name.endsWith('-tutor'))) {
471
+ const p = pickNotes(notes, files, { date: today, subject: t.subject });
472
+ const big = p.entry && p.entry.text.length > limit;
473
+ const where = t.subject && p.semester ? `课程/${p.semester}/${t.subject}.md` : null;
474
+ // 入口文件是可选的(2026-10-30):配置都对、只是没写,算过,detail 里说一句可以怎么写;没配 subject、算不出学期才不过
475
+ const optional = !p.entry && !!t.subject && !!p.semester && !!where;
476
+ push({
477
+ name: `vault.entry.${t.name}`,
478
+ ok: optional || (!!p.entry && !big && !p.extraEntries.length),
479
+ required: false,
480
+ detail: p.entry ? `${p.entry.path}(${p.entry.text.length} 字${big ? `,超过 ${limit},会截断` : ''})${p.refs.length ? ` · 参考 ${p.refs.length} 篇` : ''}${p.extraEntries.length ? ` · 另有 ${p.extraEntries.join('、')} 没用` : ''}` : optional ? `没有(可选):想写这学期${t.subject}的情况,建 ${where},frontmatter 写 cotutor: subject、subject: ${t.subject}、semester: ${p.semester}` : missingEntry(t.subject, p),
481
+ fix: !t.subject ? `在 cotutor.json 的 tutors.${t.name} 里配 subject` : !p.semester ? '先让档案算得出学期(见 vault.profile)' : !p.entry && where ? `建 ${where}(路径随意),frontmatter 写 cotutor: subject、subject: ${t.subject}、semester: ${p.semester};正文写这学期这科的情况,${t.display}每个话题开头整篇读到` : big ? '长的内容挪到别的笔记,入口文件里 [[链过去]](老师只拿到路径,要用时自己读);或调 policy.contextPack.entryChars' : p.extraEntries.length ? '同一科同一学期只留一篇' : undefined,
482
+ });
483
+ }
484
+ }
485
+ try {
486
+ const dn = JSON.parse(await readFile(join(ws.paths.vault, '.obsidian', 'daily-notes.json'), 'utf8'));
487
+ const want = relative(ws.paths.vault, ws.paths.diary).replaceAll('\\', '/');
488
+ const folderOk = (dn.folder ?? '').replace(/\/+$/, '') === want;
489
+ const formatOk = !dn.format || dn.format === 'YYYY-MM-DD';
490
+ push({ name: 'vault.diary', ok: folderOk && formatOk, required: false, detail: `Obsidian daily notes:folder=${dn.folder ?? '(未设)'} format=${dn.format ?? 'YYYY-MM-DD'};记账写 ${want}/<YYYY-MM-DD>.md`, fix: folderOk && formatOk ? undefined : `在 Obsidian 设置 → 日记 里把目录设成 ${want}、格式 YYYY-MM-DD(或改 cotutor.json 的 paths.diary),否则「今天」按钮开的不是机器追加的那篇` });
491
+ }
492
+ catch {
493
+ /* 不是 Obsidian vault 或没设 daily notes:不查 */
494
+ }
495
+ // ---- 课程表:有就要能解析(孩子端首页与上下文包的 slot 靠它;没有不算错)----
496
+ try {
497
+ const tt = parseTimetable(await readFile(ws.paths.timetable, 'utf8'));
498
+ push({
499
+ name: 'timetable',
500
+ ok: tt.found && tt.errors.length === 0,
501
+ required: false,
502
+ detail: tt.found ? (tt.errors.length ? tt.errors.slice(0, 3).join(';') : `${tt.entries.length} 个时段`) : tt.errors[0],
503
+ fix: tt.found && !tt.errors.length ? undefined : `改 ${redactHome(ws.paths.timetable)}:表头 星期/时间/学科(孩子列可选),时间形如 19:00–19:40`,
504
+ });
505
+ }
506
+ catch {
507
+ push({ name: 'timetable', ok: true, required: false, detail: `没有课程表(${redactHome(ws.paths.timetable)});孩子端「今天」只画老师,不画时段` });
508
+ }
509
+ // ---- 家长端与工作台的锁(src/app/http/access.ts):机器级 PIN;没设不算错,提一句 ----
510
+ const pin = await readPin();
511
+ push({ name: 'server.access', ok: pin !== null, required: false, detail: pin ? '设了 PIN:家长端 /parent 与工作台 /dev 要输一次,本机免密' : 'serve 对局域网全开,家长端 /parent 与工作台 /dev 没有锁:孩子在 iPad 上输 /parent 能看到答案,输 /dev 能改配置。家长在旁边、用完收回 iPad 的可以不设', fix: pin ? undefined : 'cotutor pin(机器级,所有 workspace 共用;cotutor pin off 撤掉)' });
512
+ // ---- HTTPS:iPad 上录音要;没有只提醒 ----
513
+ const tls = httpsFiles(ws);
514
+ push({ name: 'https', ok: tls !== null, required: false, detail: tls ? `${redactHome(tls.cert)}` : '没有证书,serve 走 HTTP(iPad / iPhone 上按住说话不可用)', fix: tls ? undefined : 'cotutor cert(需要 mkcert:brew install mkcert && mkcert -install)' });
515
+ // ---- 板书写法怎么递给老师:有脸的老师用到的运行时逐个说清,run / resume 不一致点名 ----
516
+ {
517
+ const used = new Set();
518
+ for (const [name, t] of Object.entries(ws.config.tutors))
519
+ if (takesTutorRules(name) && t.enabled !== false)
520
+ used.add(t.runtime ?? ws.config.runtimes.default);
521
+ for (const name of used) {
522
+ const rt = ws.config.runtimes[name];
523
+ if (!rt || typeof rt === 'string')
524
+ continue;
525
+ if (isApiRuntime(rt)) {
526
+ push({ name: `runtime.${name}.board`, ok: true, required: false, detail: 'API 运行时:板书写法连老师正文一起进系统提示' });
527
+ continue;
528
+ }
529
+ const inRun = boardPreloaded({ run: rt.run, resume: rt.run });
530
+ const inResume = boardPreloaded({ run: rt.resume, resume: rt.resume });
531
+ const how = runtimeUses(rt, '{boardFile}') ? '{boardFile} 追加进系统提示' : runtimeUses(rt, '{systemBody}') ? '{systemBody} 连老师正文一起进系统提示' : '模板里写死的 SKILL.md 路径';
532
+ if (inRun !== inResume)
533
+ push({ name: `runtime.${name}.board`, ok: false, required: false, detail: `板书写法的预载只在 ${inRun ? 'run' : 'resume'} 模板里,两边不一致(压缩后系统提示按 resume 的旗标重建)`, fix: 'cotutor upgrade --config 补旗标,或手改 cotutor.json 让两条模板一致' });
534
+ else
535
+ push({ name: `runtime.${name}.board`, ok: true, required: false, detail: inRun ? `板书写法预载进系统提示(${how}),各话题共享缓存` : '板书写法由应用放进每个话题第一条(<cotutor-board>);这个 CLI 能从文件或参数收系统提示的话,模板里用 {boardFile} / {systemBody} 更省' });
536
+ }
537
+ }
538
+ // ---- 百炼:老师靠实时模型开口,没配孩子端打不开;--live 真试连一次(握手就挂,不花钱) ----
539
+ const bl = dashscopeAuth(env);
540
+ push({ name: 'bailian', ok: Boolean(bl), required: true, detail: bl ? `百炼按量 key …${bl.key.slice(-4)}、业务空间 ${bl.workspaceId}` : '没有百炼按量的 key 或业务空间 id:孩子端打不开(只出「老师们还没到」)', fix: bl ? undefined : '家长端「首页」里配(在这台电脑上开,或者先 cotutor pin 设家长密码);Token Plan 的 sk-sp- 不行' });
541
+ if (bl && opts.live) {
542
+ const { probeRealtime } = await import("../../realtime/preview.js");
543
+ const { probeMessage } = await import("../machine/bailian.js");
544
+ const why = await probeRealtime({ env, model: ws.config.live.model });
545
+ push({ name: 'live.bailian', ok: why === null, required: true, detail: why === null ? '试连百炼实时模型:通' : probeMessage(why), fix: why === null ? undefined : '家长端「首页」里换一把 key,或查百炼控制台的余额与业务空间' });
546
+ }
547
+ // 卡的配音(点读段、单词、听写念的词):应用直连百炼合成,看 key 与业务空间齐不齐;--live 真合成一句(live.tts)
548
+ const tt = ttsTarget(TTS_MODEL, env);
549
+ push({ name: 'tts', ok: !('error' in tt), required: false, detail: 'error' in tt ? `卡不配音(点读、单词卡没有声音):${tt.error}` : `卡的配音走${tt.plan ? ' Token Plan' : '百炼按量'};老师配了 voice 的,点读段、单词会配音`, fix: 'error' in tt ? '家长端「首页」里填百炼' : undefined });
550
+ // ---- 实时老师的嘴:音色写错了连接照样连得上,第一次开口才报错,孩子那边嘴一直不出声 ----
551
+ const lv = ws.config.live;
552
+ {
553
+ const bad = Object.entries(lv.voices).filter(([, v]) => !isRealtimeVoice(v));
554
+ const who = listTutors(ws.config, { kidOnly: true }).map((t) => `${t.name} 用 ${mouthVoice(lv, t.name)}`).join('、') || '没有有脸的老师';
555
+ push({ name: 'live.voices', ok: !bad.length, required: false, detail: bad.length ? `${bad.map(([t, v]) => `${t} 的「${v}」`).join('、')}:这个模型不认,嘴会一直不出声` : `嘴的音色:${who}`, fix: bad.length ? '工作台「音色」页「实时的嘴」里换一个(这个模型认的 56 个在 src/realtime/voices.ts),或改 cotutor.json 的 live.voices' : undefined });
556
+ }
557
+ // 旧配置允许加载与升级;退役运行时不探版本、不启动。
558
+ const usedRuntimes = new Set([ws.config.runtimes.default, ...Object.values(ws.config.tutors).filter((t) => t.enabled).map((t) => t.runtime ?? ws.config.runtimes.default)]);
559
+ for (const [name, rt] of Object.entries(ws.config.runtimes)) {
560
+ if (typeof rt === 'string' || isApiRuntime(rt))
561
+ continue;
562
+ const issue = runtimeIssue(rt);
563
+ if (issue)
564
+ push({ name: `runtime.${name}`, ok: false, required: usedRuntimes.has(name), detail: issue, fix: '按 README.md 的「旧 workspace 换运行时」说明迁移' });
565
+ }
566
+ // ---- 运行时的 CLI 在不在 ----
567
+ if (probeEnv) {
568
+ for (const [runtime, p] of Object.entries(ws.config.runtimes)) {
569
+ if (runtime === 'default' || typeof p === 'string')
570
+ continue;
571
+ if (isApiRuntime(p)) {
572
+ push({ name: `runtime.${runtime}`, ok: true, required: usedRuntimes.has(runtime), detail: `API 运行时(百炼 ${p.model}),不用装 CLI;凭证看 bailian 那条` });
573
+ continue;
574
+ }
575
+ if (runtimeIssue(p))
576
+ continue;
577
+ const bin = runtimeCli(p.run);
578
+ const required = usedRuntimes.has(runtime);
579
+ // 没有老师用的 CLI 运行时(新装缺省 api,Qwen Code 那两条是备用):不探,免得没装 qwen 的家长看到一串「起不来」
580
+ if (!required) {
581
+ push({ name: `runtime.${runtime}`, ok: true, required: false, detail: `备用(${bin}),没有老师用它;要用就把老师的 runtime 改成 ${runtime},再跑一次 doctor` });
582
+ continue;
583
+ }
584
+ try {
585
+ const { stdout } = await execFileP(bin, ['--version'], { timeout: 8000 });
586
+ const version = stdout.trim().split('\n')[0];
587
+ push({ name: `runtime.${runtime}`, ok: true, required, detail: `${bin} ${version}` });
588
+ // 这个 CLI 的适配器要多看的几样(qwen:版本、百炼 key)
589
+ for (const c of adapterFor(p).check?.({ version, env }) ?? [])
590
+ push({ name: `runtime.${runtime}.${c.name}`, ok: c.ok, required, detail: c.detail, fix: c.fix });
591
+ }
592
+ catch {
593
+ push({ name: `runtime.${runtime}`, ok: false, required, detail: `PATH 里没有 ${bin}`, fix: required ? `装 ${bin},或把 runtimes.default 改成装了的那个运行时` : undefined });
594
+ }
595
+ }
596
+ }
597
+ }
598
+ if (ws && opts.live)
599
+ await probeLive(ws, push, env);
600
+ // ---- 用户配置 ----
601
+ try {
602
+ const text = await readFile(USER_CONFIG, 'utf8');
603
+ try {
604
+ const u = JSON.parse(text);
605
+ push({ name: 'user-config', ok: true, required: false, detail: u.workspace ? `workspace → ${redactHome(expandPath(u.workspace, root))}` : '有,但未设 workspace' });
606
+ }
607
+ catch {
608
+ push({ name: 'user-config', ok: false, required: false, detail: `${redactHome(USER_CONFIG)} 解析失败`, fix: '修复 JSON 或删掉它' });
609
+ }
610
+ }
611
+ catch {
612
+ push({ name: 'user-config', ok: true, required: false, detail: `未配置(可选):{"workspace": "…"} 写进 ${redactHome(USER_CONFIG)};cotutor init 会代劳` });
613
+ }
614
+ const gitThere = (await statOrNull(join(root, '.git'))) !== null;
615
+ push({ name: 'git', ok: gitThere, required: false, detail: gitThere ? 'workspace是 git 仓' : 'workspace不在 git 里', fix: gitThere ? undefined : '建议 git init:老师记忆、账本、政策都是不可再生状态' });
616
+ return { ok: checks.filter((c) => c.required).every((c) => c.ok), root, source, checks };
617
+ }