dsh-plugin-lookatstudy 0.20.1 → 0.21.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.
- package/lib/client.js +236 -3
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +118 -43
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -201,6 +201,9 @@ const ZH = {
|
|
|
201
201
|
"note.retryRender": "重试渲染",
|
|
202
202
|
"zoom.in": "放大面板字号",
|
|
203
203
|
"zoom.out": "缩小面板字号",
|
|
204
|
+
"pane.rail.aria": "调整课程栏宽度",
|
|
205
|
+
"pane.chat.aria": "调整对话栏宽度",
|
|
206
|
+
"pane.tip": "拖拽调宽,双击重置",
|
|
204
207
|
"palette.label": "命令面板",
|
|
205
208
|
"palette.placeholder": "搜索课时/课程,或选择动作…",
|
|
206
209
|
"palette.lessons": "课时",
|
|
@@ -544,6 +547,9 @@ const EN = {
|
|
|
544
547
|
"note.retryRender": "Retry render",
|
|
545
548
|
"zoom.in": "Increase panel font size",
|
|
546
549
|
"zoom.out": "Decrease panel font size",
|
|
550
|
+
"pane.rail.aria": "Resize the course pane width",
|
|
551
|
+
"pane.chat.aria": "Resize the chat pane width",
|
|
552
|
+
"pane.tip": "Drag to resize, double-click to reset",
|
|
547
553
|
"palette.label": "Command palette",
|
|
548
554
|
"palette.placeholder": "Search lessons/courses or pick an action…",
|
|
549
555
|
"palette.lessons": "Lesson",
|
|
@@ -1485,9 +1491,11 @@ function freshLesson(title, anchor, body, kind = "study") {
|
|
|
1485
1491
|
* @param parsed - course tree from an importer.
|
|
1486
1492
|
* @param source - import origin.
|
|
1487
1493
|
* @param sourceRef - markdown/folder/repo reference for display.
|
|
1494
|
+
* @param languageTarget - the taught language (BCP-47) when the course IS a
|
|
1495
|
+
* language course (design-apply path only; markdown imports stay undefined).
|
|
1488
1496
|
* @returns the imported (or pre-existing) course.
|
|
1489
1497
|
*/
|
|
1490
|
-
function importCourse(state, parsed, source, sourceRef) {
|
|
1498
|
+
function importCourse(state, parsed, source, sourceRef, languageTarget) {
|
|
1491
1499
|
const id = slugify(parsed.title);
|
|
1492
1500
|
const existing = state.courses.find((c) => c.id === id);
|
|
1493
1501
|
if (existing) return existing;
|
|
@@ -1497,6 +1505,7 @@ function importCourse(state, parsed, source, sourceRef) {
|
|
|
1497
1505
|
source,
|
|
1498
1506
|
sourceRef,
|
|
1499
1507
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1508
|
+
languageTarget: languageTarget ?? null,
|
|
1500
1509
|
sections: parsed.sections.map((section) => {
|
|
1501
1510
|
const lessons = section.lessons.map((lesson) => (() => {
|
|
1502
1511
|
const st = freshLesson(lesson.title, lesson.anchor, lesson.body, lesson.world === "practice" ? "practice" : "study");
|
|
@@ -5045,8 +5054,13 @@ function renderDesignBrief(pending, part = 1) {
|
|
|
5045
5054
|
lines.push("- if the directory layout is already clear (e.g. lessons/N-Topic/), keep its sections; do not over-reorganize.");
|
|
5046
5055
|
lines.push(`- anchor is the full heading text used to slice the body; omit it for whole-file lessons.${shown.length > 80 ? " This part is large: design at file granularity (omit anchors, one lesson per file) to keep the JSON manageable." : ""}`);
|
|
5047
5056
|
lines.push("");
|
|
5057
|
+
lines.push("### Language-course check (decide while designing)");
|
|
5058
|
+
lines.push("Is this course teaching a language ITSELF (vocabulary, grammar, reading/writing/listening of English/Japanese/Korean/Chinese/…), rather than a knowledge/tech course that merely happens to be written in some language? Typical tells: vocab lists, grammar-point explanations, dialogues/readings, sentence-translation pairs, level markers (N5/N1, TOEFL/IELTS, HSK, JLPT), pronunciation content.");
|
|
5059
|
+
lines.push("- Yes → add \"languageTarget\": \"<BCP-47 of the TAUGHT language>\" (teaching English → \"en\", Japanese → \"ja\"). The writing language is NOT the taught language — a Chinese-written English-textbook repo has languageTarget \"en\".");
|
|
5060
|
+
lines.push("- No, or unsure → omit it entirely (a normal course misflagged as a language course is worse than the reverse).");
|
|
5061
|
+
lines.push("");
|
|
5048
5062
|
lines.push("Now design the course and call study_apply_design with:");
|
|
5049
|
-
lines.push("{ \"sections\": [ { \"title\": \"...\", \"lessons\": [ { \"title\": \"...\", \"file\": \"<exact path from this brief>\", \"anchor\": \"<optional full heading text>\", \"world\": \"study\" | \"practice\" } ] } ] }");
|
|
5063
|
+
lines.push("{ \"languageTarget\": \"<only for language courses>\", \"sections\": [ { \"title\": \"...\", \"lessons\": [ { \"title\": \"...\", \"file\": \"<exact path from this brief>\", \"anchor\": \"<optional full heading text>\", \"world\": \"study\" | \"practice\" } ] } ] }");
|
|
5050
5064
|
lines.push("Use ONLY file paths that appear in this brief — anything else is dropped. Apply directly, then walk the learner through the course map.");
|
|
5051
5065
|
return lines.join("\n");
|
|
5052
5066
|
}
|
|
@@ -6126,50 +6140,56 @@ function studyTools(store, deps = {}) {
|
|
|
6126
6140
|
const applyDesign = defineTool({
|
|
6127
6141
|
name: "study_apply_design",
|
|
6128
6142
|
description: "Apply the tutor-designed course structure to the pending import (the one study_import_github or study_import_folder returned design_required for). Every lesson's file must come from the design brief — unknown paths are dropped (anti-hallucination); lesson bodies are sliced by their anchor heading and the course is imported. On a validation or fetch error the tutor fixes the design and simply calls again.",
|
|
6129
|
-
parameters: {
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6143
|
+
parameters: {
|
|
6144
|
+
languageTarget: {
|
|
6145
|
+
type: "string",
|
|
6146
|
+
description: "Set ONLY when this course teaches a language ITSELF (vocabulary, grammar, reading/writing of English/Japanese/Chinese/…) rather than being a knowledge/tech course that merely happens to be written in some language: the taught language as a BCP-47 tag (\"en\", \"ja\", \"zh-CN\", …). The writing language is NOT the taught language — a Chinese-written English-textbook repo has languageTarget \"en\". Typical tells: vocab lists, grammar points, dialogues/readings, sentence-translation pairs, level markers (N5/N1, TOEFL, HSK, JLPT), pronunciation content. When unsure, omit it — a normal course misflagged as a language course is worse than the reverse."
|
|
6147
|
+
},
|
|
6148
|
+
sections: {
|
|
6149
|
+
type: "array",
|
|
6150
|
+
required: true,
|
|
6151
|
+
description: "Designed sections in learning order.",
|
|
6152
|
+
items: {
|
|
6153
|
+
type: "object",
|
|
6154
|
+
additionalProperties: false,
|
|
6155
|
+
properties: {
|
|
6156
|
+
title: {
|
|
6157
|
+
type: "string",
|
|
6158
|
+
required: true,
|
|
6159
|
+
description: "Section title (in the learner's language)."
|
|
6160
|
+
},
|
|
6161
|
+
lessons: {
|
|
6162
|
+
type: "array",
|
|
6163
|
+
required: true,
|
|
6164
|
+
items: {
|
|
6165
|
+
type: "object",
|
|
6166
|
+
additionalProperties: false,
|
|
6167
|
+
properties: {
|
|
6168
|
+
title: {
|
|
6169
|
+
type: "string",
|
|
6170
|
+
required: true,
|
|
6171
|
+
description: "Lesson title."
|
|
6172
|
+
},
|
|
6173
|
+
file: {
|
|
6174
|
+
type: "string",
|
|
6175
|
+
required: true,
|
|
6176
|
+
description: "Exact file path from the design brief."
|
|
6177
|
+
},
|
|
6178
|
+
anchor: {
|
|
6179
|
+
type: "string",
|
|
6180
|
+
description: "Full H2/H3 heading text the lesson body starts at; omit for whole-file lessons."
|
|
6181
|
+
},
|
|
6182
|
+
world: {
|
|
6183
|
+
type: "string",
|
|
6184
|
+
description: "\"study\" (explanation) or \"practice\" (exercise/lab/notebook); anything else is treated as study."
|
|
6185
|
+
}
|
|
6166
6186
|
}
|
|
6167
6187
|
}
|
|
6168
6188
|
}
|
|
6169
6189
|
}
|
|
6170
6190
|
}
|
|
6171
6191
|
}
|
|
6172
|
-
}
|
|
6192
|
+
},
|
|
6173
6193
|
output: {
|
|
6174
6194
|
schema: {
|
|
6175
6195
|
type: "object",
|
|
@@ -6202,12 +6222,16 @@ function studyTools(store, deps = {}) {
|
|
|
6202
6222
|
droppedLessons: {
|
|
6203
6223
|
type: "integer",
|
|
6204
6224
|
required: true
|
|
6225
|
+
},
|
|
6226
|
+
languageTarget: {
|
|
6227
|
+
type: "string",
|
|
6228
|
+
description: "Present only when the course teaches a language itself (BCP-47); the tutor then keeps quiz material in the target language."
|
|
6205
6229
|
}
|
|
6206
6230
|
}
|
|
6207
6231
|
},
|
|
6208
6232
|
render: (_args, value) => [{
|
|
6209
6233
|
type: "text",
|
|
6210
|
-
text: `Imported designed course “${value.title}” (${value.sections} sections, ${value.lessons} lessons${value.droppedLessons > 0 ? `, ${value.droppedLessons} hallucinated lesson(s) dropped` : ""}). First lesson: “${value.firstLessonTitle}” (id ${value.firstLessonId}). Present the course map to the learner.`
|
|
6234
|
+
text: `Imported designed course “${value.title}” (${value.sections} sections, ${value.lessons} lessons${value.droppedLessons > 0 ? `, ${value.droppedLessons} hallucinated lesson(s) dropped` : ""})${typeof value.languageTarget === "string" ? ` — language course (${value.languageTarget}): keep reading passages, example sentences, and quiz language material in the target language` : ""}. First lesson: “${value.firstLessonTitle}” (id ${value.firstLessonId}). Present the course map to the learner.`
|
|
6211
6235
|
}]
|
|
6212
6236
|
},
|
|
6213
6237
|
async execute(args, exec) {
|
|
@@ -6236,12 +6260,14 @@ function studyTools(store, deps = {}) {
|
|
|
6236
6260
|
}
|
|
6237
6261
|
const parsed = buildCourseFromDesign(pd.part !== void 0 && pd.partCount !== void 0 && pd.partCount > 1 ? `${pd.courseTitle} (part ${pd.part})` : pd.courseTitle, validated, contents, pd.translations);
|
|
6238
6262
|
requireParsedLessons(parsed);
|
|
6239
|
-
const
|
|
6263
|
+
const languageTarget = typeof args.languageTarget === "string" && args.languageTarget.trim() !== "" ? args.languageTarget.trim().slice(0, 35) : null;
|
|
6264
|
+
const value = mutate((state) => toImportValue(importCourse(state, parsed, pd.source, pd.url, languageTarget)));
|
|
6240
6265
|
pendingDesign = null;
|
|
6241
6266
|
pendingPart = 1;
|
|
6242
6267
|
return {
|
|
6243
6268
|
...value,
|
|
6244
|
-
droppedLessons: validated.droppedLessons
|
|
6269
|
+
droppedLessons: validated.droppedLessons,
|
|
6270
|
+
...languageTarget !== null ? { languageTarget } : {}
|
|
6245
6271
|
};
|
|
6246
6272
|
},
|
|
6247
6273
|
timeoutMs: 18e4,
|
|
@@ -8690,6 +8716,45 @@ function studyTools(store, deps = {}) {
|
|
|
8690
8716
|
];
|
|
8691
8717
|
}
|
|
8692
8718
|
//#endregion
|
|
8719
|
+
//#region src/vendor/locale-names.ts
|
|
8720
|
+
/**
|
|
8721
|
+
* Vendored from LookatStudy shared/locales.ts (MIT License,
|
|
8722
|
+
* https://github.com/Kaiji-Z/LookatStudy), upstream v0.33.2 — only the
|
|
8723
|
+
* name-map + lookup function (the language-course round's BCP-47 → display
|
|
8724
|
+
* name need). Unmodified except this provenance header.
|
|
8725
|
+
*
|
|
8726
|
+
* locales —— BCP-47 → 人类可读语言名的共享映射(纯函数)。
|
|
8727
|
+
* 未知 locale 一律原样返回(如 "pt" → "pt"),不猜名字。
|
|
8728
|
+
*/
|
|
8729
|
+
/** locale → 该语言的自称名(尽量用母语写法,语言学习者认得出) */
|
|
8730
|
+
const LOCALE_LANGUAGE_NAMES = {
|
|
8731
|
+
en: "English",
|
|
8732
|
+
"zh-CN": "中文",
|
|
8733
|
+
"zh-TW": "繁體中文",
|
|
8734
|
+
ja: "日本語",
|
|
8735
|
+
ko: "한국어",
|
|
8736
|
+
fr: "Français",
|
|
8737
|
+
de: "Deutsch",
|
|
8738
|
+
es: "Español",
|
|
8739
|
+
"pt-BR": "Português",
|
|
8740
|
+
ru: "Русский",
|
|
8741
|
+
it: "Italiano",
|
|
8742
|
+
ar: "العربية",
|
|
8743
|
+
hi: "हिन्दी",
|
|
8744
|
+
tr: "Türkçe",
|
|
8745
|
+
pl: "Polski",
|
|
8746
|
+
nl: "Nederlands",
|
|
8747
|
+
id: "Indonesia",
|
|
8748
|
+
vi: "Tiếng Việt",
|
|
8749
|
+
th: "ไทย",
|
|
8750
|
+
sv: "Svenska",
|
|
8751
|
+
fi: "Suomi"
|
|
8752
|
+
};
|
|
8753
|
+
/** BCP-47 → 语言名;未映射的 locale 原样返回 */
|
|
8754
|
+
function localeToLanguageName(locale) {
|
|
8755
|
+
return LOCALE_LANGUAGE_NAMES[locale] ?? locale;
|
|
8756
|
+
}
|
|
8757
|
+
//#endregion
|
|
8693
8758
|
//#region src/surface.ts
|
|
8694
8759
|
/**
|
|
8695
8760
|
* Stable tutor core (ported from LookatStudy's BASE_AGENT_PROMPT plus its
|
|
@@ -8800,6 +8865,16 @@ function snapshotSectionText(state) {
|
|
|
8800
8865
|
if (memory.length > 0) lines.push(`记忆: ${memory.join(" | ")}`);
|
|
8801
8866
|
if (snap.dueCount > 0) lines.push(`今日待复习: ${snap.dueCount} 项`);
|
|
8802
8867
|
if (snap.pendingProposal !== null) lines.push(`待决提案 ${snap.pendingProposal.id}: ${snap.pendingProposal.rationale}(等学习者表态)`);
|
|
8868
|
+
const focusCourse = state.courses.find((c) => c.id === snap.focus?.courseId);
|
|
8869
|
+
if (focusCourse?.languageTarget != null && focusCourse.languageTarget !== "") {
|
|
8870
|
+
const t = localeToLanguageName(focusCourse.languageTarget);
|
|
8871
|
+
lines.push("");
|
|
8872
|
+
lines.push(`【语言教学姿态】这门课程教的是${t}——教学语言(讲解、指令、反馈)跟随学习者的界面语言,${t}是学习对象:`);
|
|
8873
|
+
lines.push(`- 课文引用、例句、题目素材保持${t}原文,考察目标语言本身——不要把目标语言材料翻译成教学语言来讲解或出题(生词首次出现可附教学语言注释,注释是辅助不是替代);`);
|
|
8874
|
+
lines.push(`- 出题双轴:题干和选项中的语言素材(句子/单词/短语/语法点)用${t}原文,指令性文字和解析用教学语言;`);
|
|
8875
|
+
lines.push(`- 学习者用${t}造句或表达时,先肯定再纠正其中的语言错误(语法/用词/拼写),用教学语言讲清错在哪;`);
|
|
8876
|
+
lines.push(`- 随掌握度上升,渐进提高${t}素材的占比,把学习者往"直接用${t}读"带。`);
|
|
8877
|
+
}
|
|
8803
8878
|
if (state.historyBudget === true) lines.push("历史预算已开启:当会话变长时,主动概括旧轮次的要点并基于概要继续,避免逐字复读历史。");
|
|
8804
8879
|
return lines.join("\n");
|
|
8805
8880
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-lookatstudy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"packageManager": "pnpm@11.7.0",
|
|
5
5
|
"description": "Turn any markdown, local folder, or GitHub learning repo into a guided course inside DeepSeek Harness: gated skill-tree progression, BKT mastery tracking, SM-2 spaced repetition.",
|
|
6
6
|
"type": "module",
|