@novedu/cli 0.17.0 → 0.19.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 (3) hide show
  1. package/README.md +134 -1
  2. package/dist/main.js +1857 -343
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -5,7 +5,8 @@ requires Node >= 20). It covers two jobs:
5
5
 
6
6
  - **Validate activity YAML** — tutors, fragment libraries, quizzes, writing
7
7
  activities, and coding activities — with the app's exact validation pipeline,
8
- offline and without signing in.
8
+ offline and without signing in. `prompts` dumps the exact system prompts an
9
+ activity produces, the same way.
9
10
  - **Manage the app as a teacher** — sign in with Microsoft Entra ID, then mint
10
11
  activity codes, upload app-hosted YAML files and images, and triage student
11
12
  reports, straight from the terminal (or from a coding agent, see below).
@@ -42,6 +43,54 @@ npx @novedu/cli validate ./my-quiz.yaml --kind quiz --json
42
43
  validates every fragment library it references.
43
44
  - Exit code `0` = valid, `1` = errors found — usable as a pre-commit / CI gate.
44
45
 
46
+ ## Seeing the exact prompts: `prompts`
47
+
48
+ `prompts` prints the **exact system prompts** an activity YAML produces — the
49
+ strings the app really sends to the model. Offline and sign-in-free, exactly like
50
+ `validate`.
51
+
52
+ ```bash
53
+ # A tutor's assembled system prompt (summary: kind, id, model, size per prompt)
54
+ npx @novedu/cli prompts ./activities/examples/sorting-algorithms/sorting-tutor.yaml
55
+
56
+ # A quiz: one grading prompt per question + the discussion prompt, full text as JSON
57
+ npx @novedu/cli prompts ./sorting-quiz.yaml --kind quiz --json
58
+
59
+ # A writing activity's coach prompt, a coding activity's injected system prompt
60
+ npx @novedu/cli prompts ./my-writing.yaml --kind writing
61
+ npx @novedu/cli prompts ./my-coding.yaml --kind coding
62
+
63
+ # A published activity by URL (same argument as `validate`)
64
+ npx @novedu/cli prompts https://raw.githubusercontent.com/Teaching-HTL-Leonding/novedu-chat-mvp/refs/heads/main/activities/examples/sorting-algorithms/sorting-tutor.yaml
65
+
66
+ # Pull out one question's grading prompt
67
+ npx @novedu/cli prompts ./sorting-quiz.yaml --kind quiz --json \
68
+ | jq -r '.grading.questions[] | select(.id=="q3") | .system'
69
+ ```
70
+
71
+ - The argument is a **local path or a public `http(s)` URL**, exactly like
72
+ `validate`'s; relative `fragment_files` / `quiz_files` / `text_files` resolve
73
+ against the activity's own location (sibling file, or sibling URL). "Offline"
74
+ means no app server, no database and no LLM call — not "no network".
75
+ - `--kind` accepts `tutor` (default), `quiz`, `writing` or `coding` — the same
76
+ caller-declared flag as `validate`. There is no `fragment` kind: a library has
77
+ no prompt of its own; its fragments appear **rendered in place** inside the
78
+ activity that places them.
79
+ - Every dump comes out of the app's own prompt builders and runtime loaders (no
80
+ re-implementation), so what you see is what the model gets: fragments resolved,
81
+ and — for a compound quiz — every `quiz_files` include fetched, each imported
82
+ question carrying its source quiz's preamble.
83
+ - Every dump carries `{ kind, id, llm: { provider, model } }`. A **quiz** adds
84
+ `grading` (a `system` prompt per question, the user-message templates and the
85
+ grader's JSON-Schema response contract) and `discussion` (the chat's `system`
86
+ prompt, the three seed-message templates and the verdict wording). A **coding**
87
+ activity also reports `upstreamSystemMessage` — what the proxy puts on the wire.
88
+ - The **activity's own** `llm` block is reported; a code's per-code LLM override
89
+ is not applied (a dump describes a file, and a file has no code).
90
+ - This runs the runtime load path, so a file that cannot be loaded exits `1` with
91
+ JSON errors on stderr. Use `validate` for the strict authoring check — the two
92
+ are complementary.
93
+
45
94
  ## Authentication
46
95
 
47
96
  Commands that talk to the running app authenticate with Microsoft Entra ID:
@@ -87,6 +136,7 @@ codes create --module <tutor|quiz|writing|coding> --file <url>
87
136
  [--start <iso>] [--end <iso>] [--note <text>]
88
137
  [--llm-provider <p> --llm-model <m>]
89
138
  codes list [--search <q>] [--module <m>] [--all]
139
+ codes sync <registry-file> [--lock <path>] [--dry-run] [--json]
90
140
  files upload <name> [--kind <tutor|fragment|quiz|writing|coding>]
91
141
  (--file <path> | reads stdin)
92
142
  files list [--search <q>] [--all]
@@ -103,6 +153,8 @@ images list [--search <q>] [--all]
103
153
  shareable `url`. `--start`/`--end` must be ISO 8601 **with an explicit
104
154
  offset or `Z`** (e.g. `2026-07-07T08:00:00Z`); the
105
155
  `--llm-provider`/`--llm-model` override pair is both-or-nothing.
156
+ - `codes sync <registry-file>` mints codes for a whole **course** at once — see
157
+ [Many activities at once](#many-activities-at-once-codes-sync) below.
106
158
  - `files upload <name>` is an **upsert**: creating a new file requires
107
159
  `--kind`; an existing file's kind is frozen at create time (a contradicting
108
160
  `--kind` fails with 409). The YAML comes from `--file <path>` or stdin.
@@ -150,6 +202,87 @@ image:
150
202
  alt: Merge sort splitting an array
151
203
  ```
152
204
 
205
+ ## Many activities at once: `codes sync`
206
+
207
+ A course with twenty quizzes should not be twenty `codes create` calls whose
208
+ codes you paste into twenty files by hand. Instead, keep an **activity registry**
209
+ next to the material: one hand-written YAML file listing every activity under a
210
+ stable key, plus a **lock file** the CLI generates and you commit.
211
+
212
+ ```yaml
213
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/Teaching-HTL-Leonding/novedu-chat-mvp/refs/heads/main/activities/registry/registry-yaml.schema.json
214
+ # ddp-activities.yaml — the registry (you write this)
215
+ base-url: "https://raw.githubusercontent.com/acme/course/refs/heads/main/"
216
+
217
+ activities:
218
+ quizzes:
219
+ welcome:
220
+ file: 0010-introduction/0010-welcome-quiz.yaml
221
+ note: "Creative Coding book: Welcome (0010)"
222
+ number-systems:
223
+ file: 0030-conditions/0050-number-systems-quiz.yaml
224
+ start: 2026-09-01T00:00:00+02:00
225
+ end: 2027-01-31T23:59:59+01:00
226
+ tutors:
227
+ sorting:
228
+ url: https://novedu.at/api/files/sorting-tutor
229
+ ```
230
+
231
+ ```bash
232
+ npx @novedu/cli codes sync ddp-activities.yaml
233
+ # ddp-activities.yaml: 3 entries
234
+ # reused welcome cu4afwoa23 https://novedu.at/cu4afwoa23
235
+ # minted number-systems hb34gpvahn https://novedu.at/hb34gpvahn
236
+ # reused sorting nlc90ezf5z https://novedu.at/nlc90ezf5z
237
+ #
238
+ # 2 reused, 1 minted, 0 failed
239
+ # Lock file: ddp-activities.lock.yaml
240
+ ```
241
+
242
+ ```yaml
243
+ # ddp-activities.lock.yaml — generated; commit it, do not edit it
244
+ activity-codes:
245
+ number-systems: hb34gpvahn
246
+ sorting: nlc90ezf5z
247
+ welcome: cu4afwoa23
248
+ ```
249
+
250
+ - **Groups decide the module:** `quizzes`, `tutors`, `writing`, `coding`. Each
251
+ entry gives either `file` (relative to `base-url`, which must end in `/`) or
252
+ an absolute `url`, plus any of `start`/`end` (ISO 8601 **with an offset or
253
+ `Z`**, whole seconds), `note`, and an `llm: {provider, model}` override.
254
+ - **Keys are yours and must be unique across all groups** — lowercase letters,
255
+ digits and hyphens. Your material references the key; the lock file maps it to
256
+ the code.
257
+ - **Re-runs are safe.** An entry whose activity, window and model override match
258
+ an existing code of yours **reuses** that code; only entries without a match
259
+ are minted. So `codes sync` after every edit is the normal workflow, and the
260
+ first run against already-minted codes should report all-reused.
261
+ - **Changing a window or override mints a NEW code.** The old one is not touched
262
+ (it keeps working) and is reported as superseded — delete it in the web app
263
+ when the class has moved on. Changing only the `note` never forks a code.
264
+ - `--dry-run` shows what would happen without minting or writing anything;
265
+ `--json` prints the machine-readable report; `--lock <path>` puts the lock file
266
+ somewhere else.
267
+ - One broken activity does not stop the run: it is reported as `failed`, the
268
+ other entries still sync, the lock keeps that entry's previous code, and the
269
+ command exits 1.
270
+ - **A key keeps its code.** Two keys may point at the same activity on purpose
271
+ (one quiz linked from two chapters, each with its own statistics); they get one
272
+ code each, and neither moves on a later run.
273
+ - Unknown extra keys are ignored, so you can annotate entries freely — but an
274
+ entry with nothing under it is an error, not an annotation, because that is
275
+ what a mis-indented entry looks like.
276
+ - The `# yaml-language-server:` line on top is optional: it gives editors with
277
+ YAML support field completion, hover help and a warning on a misspelled group
278
+ name. `codes sync` is still the authority — it checks things a schema cannot,
279
+ such as key uniqueness and whether `end` is after `start`.
280
+
281
+ Publications read the lock file offline. In a Quarto book, for example, add
282
+ `metadata-files: [ddp-activities.lock.yaml]` to `_quarto.yml` and let the
283
+ shortcode look the key up in `activity-codes` — the book then renders without
284
+ ever calling the app.
285
+
153
286
  ## Triaging student reports (teacher account required)
154
287
 
155
288
  Students can flag an AI interaction — a chat or a graded quiz answer — with a