monash-study-kit 1.2.0__tar.gz → 1.3.0__tar.gz
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.
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/CHANGELOG.md +9 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/PKG-INFO +18 -6
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/README.md +16 -4
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/README.zh-CN.md +16 -4
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/pyproject.toml +1 -1
- monash_study_kit-1.3.0/skills/monash-study-kit/SKILL.md +65 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/__init__.py +1 -1
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/help_menu.py +11 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/mcp_server.py +3 -1
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_help_menu.py +12 -1
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_mcp.py +1 -1
- monash_study_kit-1.3.0/tests/test_skill.py +49 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/pull_request_template.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/workflows/release.yml +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/workflows/test.yml +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.gitignore +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/CODE_OF_CONDUCT.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/CONTRIBUTING.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/LICENSE +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/SECURITY.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/adr/0001-one-shared-core.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/adr/0002-read-only-public-package.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/adr/0003-release-from-a-tag.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/adr/README.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/releasing.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/docs/security.md +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/glama.json +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/scripts/check_dist.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/scripts/measure_tokens.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/scripts/smoke_test.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/__main__.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/browser_login.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/cdp.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/claude_setup.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/cli.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/cli_ed.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/clients.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/content_index.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/doctor.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/doctor_deep.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/edlib.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/edquery.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/edsync.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/features.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/htmldom.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/jobs.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/lesson_reader.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/lessons.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/moodlelib.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/output.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/paths.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/recordings.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/syncer.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/textextract.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/todo.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/transcribe.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/update_check.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/src/monash_study_kit/webnotes.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/conftest.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_cdp.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_cli.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_clients.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_content_index.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_doctor.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_doctor_deep.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_ed_auth.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_ed_markdown.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_ed_query.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_ed_quiz.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_features.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_layout.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_lesson_reader.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_recordings.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_removed_files.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_security.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_setup.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_shared_core.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_sync_robustness.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_todo.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_update_check.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/tests/test_webnotes.py +0 -0
- {monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/uv.lock +0 -0
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Version numbers follow `__version__` in `src/monash_study_kit/__init__.py`: patch for bug fixes, minor for new features.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 1.3.0
|
|
8
|
+
|
|
9
|
+
- Questions about an assignment now follow a fixed order of sources. The Moodle brief and marking rubric come first. On Ed, only replies from teaching staff and answers they endorsed count; other students' replies are leads, not rules. A later staff correction wins, but when the brief and Ed disagree Claude lists both with their dates and suggests asking staff instead of picking one. Every rule it states names where it came from. This is in the server instructions, so it applies without doing anything.
|
|
10
|
+
- New preset prompt "作业要求" (assignment requirements) in the "+" menu: give it an assignment name and it reads the brief, the rubric and staff answers on Ed, and lists what to hand in, how it is marked, deadlines and format, and Ed clarifications, each with its source.
|
|
11
|
+
- Agent skill at `skills/monash-study-kit/SKILL.md`, installable with `npx skills add Waldo0926/monash-study-kit`. It maps questions to `monash` commands, carries the same rule for assignment questions, and lists what the agent must leave to the student (logins, tokens, submitting). A test checks every command in it against the CLI and the exit codes against `EXIT_CODES`.
|
|
12
|
+
- The package author is listed as Waldo.
|
|
13
|
+
|
|
5
14
|
## 1.2.0
|
|
6
15
|
|
|
7
16
|
- `monash connect <client>` connects Study Kit to Codex, Cursor, VS Code, Windsurf or Gemini CLI in one command. It adds only the `monash` entry with the full path to the program, keeps the rest of the config file and saves a backup first. Codex uses `codex mcp add` when the `codex` command is installed. `monash connect` alone lists the clients and whether each is connected, `--print` shows the entry without writing it, and `--remove` takes it out. A config file that isn't plain JSON (for example with comments) is left alone and the entry is printed to paste by hand.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: monash-study-kit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Connect Monash Moodle and Ed to Claude via a local MCP server and CLI: deadlines, announcements, grades, full-text search over course materials and Ed posts, all on your own machine.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Waldo0926/monash-study-kit
|
|
6
6
|
Project-URL: Issues, https://github.com/Waldo0926/monash-study-kit/issues
|
|
7
7
|
Project-URL: Changelog, https://github.com/Waldo0926/monash-study-kit/blob/main/CHANGELOG.md
|
|
8
|
-
Author:
|
|
8
|
+
Author: Waldo
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Keywords: claude,cli,edstem,mcp,model-context-protocol,monash,moodle,study-tools
|
|
@@ -151,7 +151,7 @@ Use your own words: Claude picks tools by meaning, not keywords, so both "anythi
|
|
|
151
151
|
To see everything it can do:
|
|
152
152
|
|
|
153
153
|
- Ask Claude "what can you do", or choose one of monash's preset prompts from the **"+" menu**: feature overview, this week's to-dos,
|
|
154
|
-
Ed updates, find a topic, quiz review, grades and feedback. Presets are only sent when you click them, so they cost nothing otherwise.
|
|
154
|
+
Ed updates, find a topic, quiz review, assignment requirements, grades and feedback. Presets are only sent when you click them, so they cost nothing otherwise.
|
|
155
155
|
- Run `monash help` in the terminal: every feature with an example question and the matching command.
|
|
156
156
|
|
|
157
157
|
### When the Moodle login expires
|
|
@@ -254,6 +254,16 @@ If the file already lists other servers, add the `monash` entry rather than over
|
|
|
254
254
|
- Tool descriptions are written with Claude in mind. Other models usually follow them fine, but this hasn't been verified.
|
|
255
255
|
- If you'd rather not bother, the CLI works with no AI at all (see [Works without Claude usage](#works-without-claude-usage)); you can paste the output of `monash todo` and friends into any AI.
|
|
256
256
|
|
|
257
|
+
### Agent skill
|
|
258
|
+
|
|
259
|
+
For coding agents that read skills (Claude Code, Codex, Cursor and others), there is a skill that tells the agent which `monash` command answers which question, how to handle assignment questions (brief and rubric first, then staff answers on Ed, each with a source), and what it must leave to you (logins, tokens, submitting):
|
|
260
|
+
|
|
261
|
+
```sh
|
|
262
|
+
npx skills add Waldo0926/monash-study-kit
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The file is [`skills/monash-study-kit/SKILL.md`](skills/monash-study-kit/SKILL.md). A test checks that every command in it still exists. `npx skills` is a third-party installer from Vercel and needs Node.js; you can also copy the file into your agent's skills folder by hand.
|
|
266
|
+
|
|
257
267
|
### What can be searched
|
|
258
268
|
|
|
259
269
|
`monash grep` and Claude's course-material search cover:
|
|
@@ -290,13 +300,15 @@ If the file already lists other servers, add the `monash` entry rather than over
|
|
|
290
300
|
|
|
291
301
|
## Token use
|
|
292
302
|
|
|
293
|
-
Every MCP tool's name, description and parameters are sent to Claude at the start of each conversation, so they cost tokens even when unused. Measured with `scripts/measure_tokens.py` on 1.
|
|
303
|
+
Every MCP tool's name, description and parameters are sent to Claude at the start of each conversation, so they cost tokens even when unused. Measured with `scripts/measure_tokens.py` on 1.3.0 (estimate: 4 ASCII characters or 1 Chinese character per token):
|
|
294
304
|
|
|
295
305
|
| Sent once per conversation | Tokens |
|
|
296
306
|
| --- | ---: |
|
|
297
307
|
| 17 tool definitions | 2,785 |
|
|
298
|
-
| Server instructions |
|
|
299
|
-
| **Total** | **
|
|
308
|
+
| Server instructions | 270 |
|
|
309
|
+
| **Total** | **3,055** |
|
|
310
|
+
|
|
311
|
+
The instructions grew from 187 in 1.2.0 when the rule for assignment questions was added (brief and rubric first, then staff answers on Ed).
|
|
300
312
|
|
|
301
313
|
The largest single tool is `search_ed_threads` at 324.
|
|
302
314
|
|
|
@@ -122,7 +122,7 @@ Use your own words: Claude picks tools by meaning, not keywords, so both "anythi
|
|
|
122
122
|
To see everything it can do:
|
|
123
123
|
|
|
124
124
|
- Ask Claude "what can you do", or choose one of monash's preset prompts from the **"+" menu**: feature overview, this week's to-dos,
|
|
125
|
-
Ed updates, find a topic, quiz review, grades and feedback. Presets are only sent when you click them, so they cost nothing otherwise.
|
|
125
|
+
Ed updates, find a topic, quiz review, assignment requirements, grades and feedback. Presets are only sent when you click them, so they cost nothing otherwise.
|
|
126
126
|
- Run `monash help` in the terminal: every feature with an example question and the matching command.
|
|
127
127
|
|
|
128
128
|
### When the Moodle login expires
|
|
@@ -225,6 +225,16 @@ If the file already lists other servers, add the `monash` entry rather than over
|
|
|
225
225
|
- Tool descriptions are written with Claude in mind. Other models usually follow them fine, but this hasn't been verified.
|
|
226
226
|
- If you'd rather not bother, the CLI works with no AI at all (see [Works without Claude usage](#works-without-claude-usage)); you can paste the output of `monash todo` and friends into any AI.
|
|
227
227
|
|
|
228
|
+
### Agent skill
|
|
229
|
+
|
|
230
|
+
For coding agents that read skills (Claude Code, Codex, Cursor and others), there is a skill that tells the agent which `monash` command answers which question, how to handle assignment questions (brief and rubric first, then staff answers on Ed, each with a source), and what it must leave to you (logins, tokens, submitting):
|
|
231
|
+
|
|
232
|
+
```sh
|
|
233
|
+
npx skills add Waldo0926/monash-study-kit
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The file is [`skills/monash-study-kit/SKILL.md`](skills/monash-study-kit/SKILL.md). A test checks that every command in it still exists. `npx skills` is a third-party installer from Vercel and needs Node.js; you can also copy the file into your agent's skills folder by hand.
|
|
237
|
+
|
|
228
238
|
### What can be searched
|
|
229
239
|
|
|
230
240
|
`monash grep` and Claude's course-material search cover:
|
|
@@ -261,13 +271,15 @@ If the file already lists other servers, add the `monash` entry rather than over
|
|
|
261
271
|
|
|
262
272
|
## Token use
|
|
263
273
|
|
|
264
|
-
Every MCP tool's name, description and parameters are sent to Claude at the start of each conversation, so they cost tokens even when unused. Measured with `scripts/measure_tokens.py` on 1.
|
|
274
|
+
Every MCP tool's name, description and parameters are sent to Claude at the start of each conversation, so they cost tokens even when unused. Measured with `scripts/measure_tokens.py` on 1.3.0 (estimate: 4 ASCII characters or 1 Chinese character per token):
|
|
265
275
|
|
|
266
276
|
| Sent once per conversation | Tokens |
|
|
267
277
|
| --- | ---: |
|
|
268
278
|
| 17 tool definitions | 2,785 |
|
|
269
|
-
| Server instructions |
|
|
270
|
-
| **Total** | **
|
|
279
|
+
| Server instructions | 270 |
|
|
280
|
+
| **Total** | **3,055** |
|
|
281
|
+
|
|
282
|
+
The instructions grew from 187 in 1.2.0 when the rule for assignment questions was added (brief and rubric first, then staff answers on Ed).
|
|
271
283
|
|
|
272
284
|
The largest single tool is `search_ed_threads` at 324.
|
|
273
285
|
|
|
@@ -115,7 +115,7 @@ monash setup
|
|
|
115
115
|
|
|
116
116
|
直接用自己的话问就行:Claude 按意思挑工具,不是靠关键词触发,“这周有啥要交的”和“我是不是漏交了什么”都能用到待办。想看都能做什么:
|
|
117
117
|
|
|
118
|
-
- 在 Claude 里问“你能做什么”,或者从输入框的 **“+” 菜单**里选 monash 的预设提示:功能大全、本周待办、Ed
|
|
118
|
+
- 在 Claude 里问“你能做什么”,或者从输入框的 **“+” 菜单**里选 monash 的预设提示:功能大全、本周待办、Ed 新消息、找知识点、测验题复习、作业要求、成绩和反馈。预设提示点了才发给 Claude,平时不占额度。
|
|
119
119
|
- 终端里运行 `monash help`:每项功能附一句示例问法和对应的命令。
|
|
120
120
|
|
|
121
121
|
### Moodle 登录过期了怎么办
|
|
@@ -212,6 +212,16 @@ VS Code(`mcp.json`,注意外层是 `servers` 不是 `mcpServers`):
|
|
|
212
212
|
- 工具说明是按 Claude 写的,别的模型一般也能照着用,但效果没验证过。
|
|
213
213
|
- 不想折腾的话,命令行本身跟任何 AI 都无关(见[没额度了也能用](#claude-没额度了也能用)),可以把 `monash todo` 之类的输出直接复制给任何 AI。
|
|
214
214
|
|
|
215
|
+
### 给 AI 用的技能包
|
|
216
|
+
|
|
217
|
+
会读 skill 的 AI 编程工具(Claude Code、Codex、Cursor 等)可以装这个技能包。它告诉 AI 哪个问题用哪条 `monash` 命令、问作业要求时怎么用资料(作业说明和评分细则优先,再看 Ed 上教职员工的回答,每条标出处),以及哪些事必须留给你自己(登录、令牌、交作业):
|
|
218
|
+
|
|
219
|
+
```sh
|
|
220
|
+
npx skills add Waldo0926/monash-study-kit
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
文件在 [`skills/monash-study-kit/SKILL.md`](skills/monash-study-kit/SKILL.md),有测试检查里面的命令都还存在。`npx skills` 是 Vercel 做的第三方安装工具,要装 Node.js;也可以把文件手动复制到 AI 的 skills 文件夹。
|
|
224
|
+
|
|
215
225
|
### 搜得到哪些内容
|
|
216
226
|
|
|
217
227
|
`monash grep` 和 Claude 里的课件搜索覆盖:
|
|
@@ -242,13 +252,15 @@ VS Code(`mcp.json`,注意外层是 `servers` 不是 `mcpServers`):
|
|
|
242
252
|
|
|
243
253
|
## Token 用量
|
|
244
254
|
|
|
245
|
-
每个 MCP 工具的名字、说明和参数在每次对话开始时都会发给 Claude,不调用也占 token。用 `scripts/measure_tokens.py` 在 1.
|
|
255
|
+
每个 MCP 工具的名字、说明和参数在每次对话开始时都会发给 Claude,不调用也占 token。用 `scripts/measure_tokens.py` 在 1.3.0 上量的结果(估算:4 个 ASCII 字符或 1 个中文字符算 1 个 token):
|
|
246
256
|
|
|
247
257
|
| 每次对话发一次 | Token |
|
|
248
258
|
| --- | ---: |
|
|
249
259
|
| 17 个工具定义 | 2,785 |
|
|
250
|
-
| 服务说明 |
|
|
251
|
-
| **合计** | **
|
|
260
|
+
| 服务说明 | 270 |
|
|
261
|
+
| **合计** | **3,055** |
|
|
262
|
+
|
|
263
|
+
服务说明在 1.2.0 是 187,1.3.0 加了作业问题的规则(作业说明和评分细则优先,再看 Ed 上教职员工的回答)后变成 270。
|
|
252
264
|
|
|
253
265
|
最大的单个工具是 `search_ed_threads`,324。
|
|
254
266
|
|
|
@@ -5,7 +5,7 @@ description = "Connect Monash Moodle and Ed to Claude via a local MCP server and
|
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
7
|
requires-python = ">=3.10"
|
|
8
|
-
authors = [{ name = "
|
|
8
|
+
authors = [{ name = "Waldo" }]
|
|
9
9
|
keywords = ["mcp", "model-context-protocol", "claude", "moodle", "edstem", "monash", "cli", "study-tools"]
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monash-study-kit
|
|
3
|
+
description: Read a Monash student's own Moodle and Ed Discussion through Monash Study Kit (the `monash` CLI or its MCP tools) - this week's to-dos, deadlines, possibly missed assignments, grades and feedback, announcements, Ed posts and replies, Ed lessons, and full-text search over course files and lecture transcripts with page numbers or timestamps. Use when the student asks about their Monash units, assignments, due dates, course materials or Ed. Read-only - it never submits work, answers quizzes or posts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Monash Study Kit
|
|
7
|
+
|
|
8
|
+
Study Kit keeps a local copy of the student's Moodle and Ed and answers questions from it. Source and docs: https://github.com/Waldo0926/monash-study-kit.
|
|
9
|
+
|
|
10
|
+
## Which way to call it
|
|
11
|
+
|
|
12
|
+
- If tools from the `monash` MCP server are available (`get_study_todo`, `search_content`, `read_ed_thread` ...), use them. They read the same local data and their descriptions say which one fits.
|
|
13
|
+
- Otherwise run the `monash` CLI. Add `--json` when you parse the output. Results go to stdout, hints and errors to stderr.
|
|
14
|
+
- If `monash` is not installed, point the student to https://docs.secureview.tech/study-kit/ and stop. Do not install it without asking.
|
|
15
|
+
|
|
16
|
+
Talk to the student in the language they use with you. Command output is in Chinese.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
| The student asks | Run |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| What do I need to do this week | `monash todo` |
|
|
23
|
+
| When is something due | `monash due` or `monash due FIT2102` |
|
|
24
|
+
| Did I miss a submission | `monash moodle assignments --missing` |
|
|
25
|
+
| Grades and feedback | `monash moodle grades FIT2102` |
|
|
26
|
+
| Moodle announcements | `monash moodle news FIT2102` |
|
|
27
|
+
| Where a topic is covered | `monash grep "monad" FIT2102` |
|
|
28
|
+
| A unit's files for one week | `monash moodle ls FIT2109 5` |
|
|
29
|
+
| What's new on Ed | `monash ed new` |
|
|
30
|
+
| Did anyone reply to my posts | `monash ed following` |
|
|
31
|
+
| Search Ed | `monash ed search FIT2102 lodash` |
|
|
32
|
+
| Read one Ed post with all replies | `monash ed show FIT2102#42` |
|
|
33
|
+
| Read an Ed lesson | `monash ed read FIT2109 "W3 Pre-Class"` |
|
|
34
|
+
| Revise with Ed lesson quiz questions | `monash ed quiz FIT2109 --module "Week 3"` |
|
|
35
|
+
| Something doesn't work | `monash doctor` |
|
|
36
|
+
|
|
37
|
+
`monash grep` searches slides, PDFs, course web pages, Ed lessons and lecture transcripts (`.transcript.md`, with timestamps). Each hit has a file path under the student's Study Kit folder. Read that file to quote the original. No hit can mean the data is not synced yet: suggest `monash sync`. `monash help` lists every feature with an example question.
|
|
38
|
+
|
|
39
|
+
## Questions about an assignment
|
|
40
|
+
|
|
41
|
+
When the student asks what an assignment wants, how it is marked, or its format or deadline:
|
|
42
|
+
|
|
43
|
+
1. **The brief and the marking rubric come first.** Find them with `monash grep` or `monash moodle ls` and read the original text.
|
|
44
|
+
2. **Ed adds to the brief.** Count only replies from teaching staff (marked 教师 or 助教 in the output) and answers they endorsed (✅). Other students' replies are leads, not rules.
|
|
45
|
+
3. **A later staff correction wins.** But when the brief and Ed disagree, list both with their dates, say which is newer, and suggest the student confirm with staff. Do not decide for them.
|
|
46
|
+
4. **Give a source for every rule:** file name and page, or post number, who said it and the date. If you cannot find a source, say so. Do not fill the gap yourself.
|
|
47
|
+
|
|
48
|
+
Help the student understand the requirements. The work they hand in has to be their own, and unit rules on AI use apply.
|
|
49
|
+
|
|
50
|
+
## Things only the student does
|
|
51
|
+
|
|
52
|
+
- **Logging in.** Exit code 2 means a login is needed. Moodle: ask the student to run `monash login` (it opens a browser window for Okta and MFA). Ed: `monash login ed` in their own terminal. Never ask for, accept or repeat an Ed token, password or MFA code. If the student pastes a token into the chat, tell them to delete it at https://edstem.org/au/settings/api-tokens and make a new one.
|
|
53
|
+
- **Submitting, quizzes and posting.** Study Kit cannot do these, and you should not try through other means. They do it on the website.
|
|
54
|
+
- **Setup and settings.** Do not run `monash setup`, `monash courses`, `monash config` or `monash uninstall` yourself; they are interactive or change the student's setup. Never print anything from the `secrets` folder in the Study Kit data folder.
|
|
55
|
+
|
|
56
|
+
## Exit codes
|
|
57
|
+
|
|
58
|
+
| Code | Meaning |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| 0 | OK |
|
|
61
|
+
| 1 | Bad arguments (see stderr) |
|
|
62
|
+
| 2 | Login needed: `monash login` (Moodle) or `monash login ed` |
|
|
63
|
+
| 3 | Could not reach Moodle or Ed, or it returned an error; try again later |
|
|
64
|
+
| 4 | Unit or post not found |
|
|
65
|
+
| 130 | Interrupted |
|
|
@@ -83,6 +83,15 @@ def _quiz(a):
|
|
|
83
83
|
"Ed 不公开答案,请根据课件判断,拿不准的地方直接说。")
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def _assignment(a):
|
|
87
|
+
course = f"{a['course']} 的" if a.get("course") else ""
|
|
88
|
+
return (f"帮我弄清楚{course}“{a['assignment']}”的要求。先找到作业说明和评分细则读原文,"
|
|
89
|
+
"再在 Ed 上搜这个作业的帖子,只采用教师和助教的回复、被 endorse 的回答,同学的回复只当线索。"
|
|
90
|
+
"按要交什么、怎么评分、截止和格式、Ed 上的补充说明列出来,每条写出处"
|
|
91
|
+
"(文件名和页码,或帖子编号、谁说的、日期)。"
|
|
92
|
+
"作业说明和 Ed 不一致的地方单独列出,标明哪个更新,不要替我下结论,提醒我去问老师确认。")
|
|
93
|
+
|
|
94
|
+
|
|
86
95
|
def _grades(a):
|
|
87
96
|
return "我各门课现在的成绩和老师反馈怎么样?有没有需要特别注意的?"
|
|
88
97
|
|
|
@@ -99,6 +108,8 @@ PROMPTS = {
|
|
|
99
108
|
[("topic", "要找的知识点,如 monad", True), ("course", "课号,可不填", False)], _topic),
|
|
100
109
|
"quiz_review": ("测验题复习", "用 Ed Lessons 的测验题一题一题练",
|
|
101
110
|
[("course", "课号,如 FIT2109", True), ("week", "第几周,可不填", False)], _quiz),
|
|
111
|
+
"assignment_rules": ("作业要求", "读作业说明、评分细则和老师在 Ed 上的答疑,每条标出处",
|
|
112
|
+
[("assignment", "作业名,如 A2", True), ("course", "课号,可不填", False)], _assignment),
|
|
102
113
|
"grades": ("成绩和反馈", "各课成绩和老师反馈", [], _grades),
|
|
103
114
|
}
|
|
104
115
|
|
|
@@ -42,7 +42,9 @@ INSTRUCTIONS = (
|
|
|
42
42
|
"知识点在哪份资料用 search_content 再 read_file;Ed 新消息用 list_ed_updates。数据每小时后台同步。"
|
|
43
43
|
"报“Moodle 登录已过期”时问用户要不要登录,同意就调 start_monash_login;Ed 令牌失效请用户在终端运行 "
|
|
44
44
|
"monash login ed(令牌别贴进聊天)。用户问“你能做什么/help”时,按类别列出功能并各给一句示例问法,"
|
|
45
|
-
"并告诉他输入框“+”菜单里有 monash 的预设提示。"
|
|
45
|
+
"并告诉他输入框“+”菜单里有 monash 的预设提示。"
|
|
46
|
+
"问作业要求时以 Moodle 作业说明和评分细则为准,Ed 只认教师/助教回复和 endorsed 回答,同学的只当线索;"
|
|
47
|
+
"教师后来的更正优先,两边不一致就都列出并注明日期,建议问教师;每条注明出处。")
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
# ---------------------------------------------------------------- 日志
|
|
@@ -33,7 +33,8 @@ def test_prompts_advertised_and_fetchable():
|
|
|
33
33
|
init = call("initialize", {"protocolVersion": "2025-06-18"})["result"]
|
|
34
34
|
assert "prompts" in init["capabilities"] and "help" in init["instructions"]
|
|
35
35
|
prompts = {p["name"]: p for p in call("prompts/list")["result"]["prompts"]}
|
|
36
|
-
assert {"help", "weekly_todo", "ed_catchup", "find_topic", "quiz_review", "
|
|
36
|
+
assert {"help", "weekly_todo", "ed_catchup", "find_topic", "quiz_review", "assignment_rules",
|
|
37
|
+
"grades"} == set(prompts)
|
|
37
38
|
assert prompts["find_topic"]["arguments"][0] == {"name": "topic", "description": "要找的知识点,如 monad",
|
|
38
39
|
"required": True}
|
|
39
40
|
msg = call("prompts/get", {"name": "find_topic", "arguments": {"topic": "monad", "course": "FIT2102"}})
|
|
@@ -43,6 +44,16 @@ def test_prompts_advertised_and_fetchable():
|
|
|
43
44
|
assert "Ed 论坛" in help_text
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
def test_assignment_prompt_puts_the_brief_first():
|
|
48
|
+
"""作业说明和评分细则为准,Ed 只认教职员工;不一致时列出来让用户去问,不替用户下结论。"""
|
|
49
|
+
msg = call("prompts/get", {"name": "assignment_rules", "arguments": {"assignment": "A2", "course": "FIT2099"}})
|
|
50
|
+
text = msg["result"]["messages"][0]["content"]["text"]
|
|
51
|
+
assert "FIT2099" in text and "A2" in text
|
|
52
|
+
assert "评分细则" in text and "助教" in text and "出处" in text and "问老师" in text
|
|
53
|
+
init = call("initialize", {"protocolVersion": "2025-06-18"})["result"]
|
|
54
|
+
assert "作业说明和评分细则为准" in init["instructions"] and "同学的只当线索" in init["instructions"]
|
|
55
|
+
|
|
56
|
+
|
|
46
57
|
def test_prompt_errors():
|
|
47
58
|
assert call("prompts/get", {"name": "nope"})["error"]["code"] == -32602
|
|
48
59
|
err = call("prompts/get", {"name": "quiz_review", "arguments": {"week": "3"}})["error"]
|
|
@@ -94,7 +94,7 @@ def test_catalog_stays_small():
|
|
|
94
94
|
tools = call("tools/list")["result"]["tools"]
|
|
95
95
|
size = len(json.dumps(tools, ensure_ascii=False))
|
|
96
96
|
assert len(tools) <= 17 and size <= 9200, (len(tools), size)
|
|
97
|
-
assert len(M.INSTRUCTIONS) <=
|
|
97
|
+
assert len(M.INSTRUCTIONS) <= 400
|
|
98
98
|
enums = tools[[t["name"] for t in tools].index("search_ed_threads")]["inputSchema"]["properties"]["only"]["enum"]
|
|
99
99
|
assert set(enums) == {"starred", "watching", "unseen", "mine", "unread_replies"}
|
|
100
100
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""skills/monash-study-kit/SKILL.md 是给 AI 看的说明书:里面写的命令和退出码要和 CLI 对得上。"""
|
|
2
|
+
import io
|
|
3
|
+
import re
|
|
4
|
+
import shlex
|
|
5
|
+
from contextlib import redirect_stderr, redirect_stdout
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from monash_study_kit import cli
|
|
11
|
+
|
|
12
|
+
SKILL = Path(__file__).resolve().parents[1] / "skills" / "monash-study-kit" / "SKILL.md"
|
|
13
|
+
TEXT = SKILL.read_text(encoding="utf-8")
|
|
14
|
+
# 表格里的是能直接跑的完整命令;正文里提到的(如 `monash grep`)可能没带参数,只查子命令存在
|
|
15
|
+
TABLE = sorted(set(re.findall(r"^\|.*`(monash [^`]+)` \|$", TEXT, re.M)))
|
|
16
|
+
MENTIONED = sorted(set(re.findall(r"`(monash [^`]+)`", TEXT)) - set(TABLE))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_frontmatter():
|
|
20
|
+
head = TEXT.split("---")[1]
|
|
21
|
+
assert re.search(r"^name: monash-study-kit$", head, re.M)
|
|
22
|
+
desc = re.search(r"^description: (.+)$", head, re.M).group(1)
|
|
23
|
+
assert len(desc) <= 1024 and "Read-only" in desc
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.parametrize("cmd", TABLE)
|
|
27
|
+
def test_every_command_parses(cmd):
|
|
28
|
+
"""命令改名或删掉时这里会失败,免得 AI 照着说明书跑不存在的命令。"""
|
|
29
|
+
with redirect_stderr(io.StringIO()):
|
|
30
|
+
args = cli.build_parser().parse_args(shlex.split(cmd)[1:])
|
|
31
|
+
assert getattr(args, "fn", None)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.mark.parametrize("cmd", MENTIONED)
|
|
35
|
+
def test_every_mentioned_command_exists(cmd):
|
|
36
|
+
with redirect_stdout(io.StringIO()), pytest.raises(SystemExit) as e:
|
|
37
|
+
cli.build_parser().parse_args(shlex.split(cmd)[1:] + ["--help"])
|
|
38
|
+
assert e.value.code == 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_exit_codes_match_cli():
|
|
42
|
+
rows = dict(re.findall(r"^\| (\d+) \| (.+) \|$", TEXT, re.M))
|
|
43
|
+
assert sorted(map(int, rows)) == sorted(cli.EXIT_CODES)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_assignment_rules_and_limits():
|
|
47
|
+
assert "brief and the marking rubric come first" in TEXT
|
|
48
|
+
assert "Other students' replies are leads" in TEXT
|
|
49
|
+
assert "Never ask for, accept or repeat an Ed token" in TEXT
|
|
File without changes
|
|
File without changes
|
{monash_study_kit-1.2.0 → monash_study_kit-1.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|