@novedu/cli 0.9.0 → 0.11.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/dist/main.js +174 -86
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -186,7 +186,7 @@ function displayName(result) {
|
|
|
186
186
|
}
|
|
187
187
|
//#endregion
|
|
188
188
|
//#region src/server-url.ts
|
|
189
|
-
const DEFAULT_SERVER = "https://novedu
|
|
189
|
+
const DEFAULT_SERVER = "https://novedu.at";
|
|
190
190
|
function resolveServerUrl(cliOption) {
|
|
191
191
|
return cliOption || process.env.NOVEDU_SERVER || DEFAULT_SERVER;
|
|
192
192
|
}
|
|
@@ -610,66 +610,106 @@ function validate(value, schema, code, url) {
|
|
|
610
610
|
* scheme (`ftp:`, `mailto:`, …) so a typo can't smuggle in a non-http(s) target — the
|
|
611
611
|
* refine reads as "if it carries a URI scheme at all, that scheme must be http(s)".
|
|
612
612
|
* Strings without a scheme (relative paths) pass through and are resolved at load time.
|
|
613
|
+
*
|
|
614
|
+
* The `.refine()` is the RUNTIME enforcement. `z.toJSONSchema()` cannot derive a pattern
|
|
615
|
+
* from a refinement, so the equivalent editor constraint is re-expressed via `.meta({ pattern })`
|
|
616
|
+
* — the two MUST be kept consistent (both mean "http(s) URL or a scheme-less relative path").
|
|
613
617
|
*/
|
|
614
|
-
const FragmentUrlRef = z.string().min(1).refine((u) => !/^[a-z][a-z0-9+.-]*:/i.test(u) || /^https?:\/\//i.test(u), { message: "Must be an http(s) URL or a relative path" })
|
|
618
|
+
const FragmentUrlRef = z.string().min(1).refine((u) => !/^[a-z][a-z0-9+.-]*:/i.test(u) || /^https?:\/\//i.test(u), { message: "Must be an http(s) URL or a relative path" }).meta({
|
|
619
|
+
pattern: "^(https?://|(?![A-Za-z][A-Za-z0-9+.-]*:).+)$",
|
|
620
|
+
description: "HTTP(S) URL or relative path to the fragment library."
|
|
621
|
+
});
|
|
615
622
|
/**
|
|
616
623
|
* A declared property is a string, a boolean, or an array of strings. Each may carry an
|
|
617
624
|
* optional `default`, typed to match its `type` (a string default on a boolean property
|
|
618
625
|
* is a schema error). When the activity omits the variable, the default is used; supplying
|
|
619
626
|
* a value overrides it. See `consistency.ts` for where defaults are injected.
|
|
620
627
|
*/
|
|
628
|
+
const StringPropertySchema = z.strictObject({
|
|
629
|
+
type: z.literal("string").meta({ description: "Declares a string variable." }),
|
|
630
|
+
default: z.string().optional().meta({ description: "Default string used when the activity omits this variable." })
|
|
631
|
+
}).meta({
|
|
632
|
+
id: "stringProperty",
|
|
633
|
+
description: "A string variable, with an optional default."
|
|
634
|
+
});
|
|
635
|
+
const BooleanPropertySchema = z.strictObject({
|
|
636
|
+
type: z.literal("boolean").meta({ description: "Declares a boolean variable." }),
|
|
637
|
+
default: z.boolean().optional().meta({ description: "Default boolean used when the activity omits this variable." })
|
|
638
|
+
}).meta({
|
|
639
|
+
id: "booleanProperty",
|
|
640
|
+
description: "A boolean variable, with an optional default."
|
|
641
|
+
});
|
|
642
|
+
const StringArrayPropertySchema = z.strictObject({
|
|
643
|
+
type: z.literal("array").meta({ description: "Declares an array-of-strings variable." }),
|
|
644
|
+
items: z.strictObject({ type: z.literal("string").meta({ description: "Array elements are strings." }) }).meta({ description: "The element type of the array (always string)." }),
|
|
645
|
+
default: z.array(z.string()).optional().meta({ description: "Default string array used when the activity omits this variable." })
|
|
646
|
+
}).meta({
|
|
647
|
+
id: "stringArrayProperty",
|
|
648
|
+
description: "An array-of-strings variable, with an optional default."
|
|
649
|
+
});
|
|
621
650
|
const PropertySchema = z.discriminatedUnion("type", [
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}),
|
|
630
|
-
z.strictObject({
|
|
631
|
-
type: z.literal("array"),
|
|
632
|
-
items: z.strictObject({ type: z.literal("string") }),
|
|
633
|
-
default: z.array(z.string()).optional()
|
|
634
|
-
})
|
|
635
|
-
]);
|
|
651
|
+
StringPropertySchema,
|
|
652
|
+
BooleanPropertySchema,
|
|
653
|
+
StringArrayPropertySchema
|
|
654
|
+
]).meta({
|
|
655
|
+
id: "inputProperty",
|
|
656
|
+
description: "A declared variable: a string, a boolean, or an array of strings, each with an optional matching default."
|
|
657
|
+
});
|
|
636
658
|
const InputSchema = z.strictObject({
|
|
637
|
-
type: z.literal("object"),
|
|
638
|
-
required: z.array(z.string()).default([]),
|
|
639
|
-
properties: z.record(z.string(), PropertySchema).default({})
|
|
659
|
+
type: z.literal("object").meta({ description: "Always \"object\"." }),
|
|
660
|
+
required: z.array(z.string()).default([]).meta({ description: "Names of the variables that must be supplied." }),
|
|
661
|
+
properties: z.record(z.string(), PropertySchema).default({}).meta({ description: "The declared variables, keyed by variable name." })
|
|
662
|
+
}).meta({
|
|
663
|
+
id: "inputSchema",
|
|
664
|
+
description: "A constrained mini JSON-schema declaring the variables a fragment accepts."
|
|
640
665
|
});
|
|
641
666
|
const ClassificationSchema = z.strictObject({
|
|
642
|
-
type: z.string(),
|
|
643
|
-
override_allowed: z.boolean().optional()
|
|
667
|
+
type: z.string().meta({ description: "Classification label for this fragment." }),
|
|
668
|
+
override_allowed: z.boolean().optional().meta({ description: "Whether an activity may override this classification." })
|
|
669
|
+
}).meta({
|
|
670
|
+
id: "classification",
|
|
671
|
+
description: "Optional classification metadata for a fragment."
|
|
644
672
|
});
|
|
645
673
|
const FragmentSchema = z.strictObject({
|
|
646
|
-
id: z.string(),
|
|
647
|
-
version: z.number(),
|
|
648
|
-
priority: z.number(),
|
|
674
|
+
id: z.string().meta({ description: "Unique fragment id within this library." }),
|
|
675
|
+
version: z.number().meta({ description: "Fragment version number." }),
|
|
676
|
+
priority: z.number().meta({ description: "Assembly order. Lower priorities appear earlier." }),
|
|
649
677
|
input_schema: InputSchema.optional(),
|
|
650
678
|
classification: ClassificationSchema.optional(),
|
|
651
|
-
content: z.string()
|
|
679
|
+
content: z.string().meta({ description: "Prompt text as a Handlebars template." })
|
|
680
|
+
}).meta({
|
|
681
|
+
id: "fragment",
|
|
682
|
+
description: "One reusable, parameterized prompt fragment."
|
|
652
683
|
});
|
|
653
684
|
const FragmentFileSchema = z.strictObject({
|
|
654
|
-
id: z.string(),
|
|
655
|
-
fragments: z.array(FragmentSchema).min(1)
|
|
685
|
+
id: z.string().meta({ description: "Machine-readable id of this fragment library." }),
|
|
686
|
+
fragments: z.array(FragmentSchema).min(1).meta({ description: "The reusable fragments this library provides (at least one)." })
|
|
656
687
|
});
|
|
657
688
|
/** A supplied variable value mirrors what `input_schema` can declare. */
|
|
658
689
|
const VariableValueSchema = z.union([
|
|
659
690
|
z.string(),
|
|
660
691
|
z.boolean(),
|
|
661
692
|
z.array(z.string())
|
|
662
|
-
])
|
|
693
|
+
]).meta({
|
|
694
|
+
id: "variableValue",
|
|
695
|
+
description: "A literal variable value: a string, a boolean, or an array of strings."
|
|
696
|
+
});
|
|
663
697
|
const FragmentFileRefSchema = z.strictObject({
|
|
664
|
-
id: z.string(),
|
|
698
|
+
id: z.string().meta({ description: "Local alias for this library, referenced by each fragment's `file`." }),
|
|
665
699
|
url: FragmentUrlRef
|
|
700
|
+
}).meta({
|
|
701
|
+
id: "fragmentFileRef",
|
|
702
|
+
description: "A reference to a fragment library by alias + URL."
|
|
666
703
|
});
|
|
667
704
|
const FragmentRefSchema = z.strictObject({
|
|
668
|
-
file: z.string(),
|
|
669
|
-
id: z.string(),
|
|
670
|
-
variables: z.record(z.string(), VariableValueSchema).optional(),
|
|
671
|
-
bind: z.record(z.string(), z.string()).optional(),
|
|
672
|
-
required: z.boolean().optional()
|
|
705
|
+
file: z.string().meta({ description: "The alias of the fragment library this fragment is drawn from." }),
|
|
706
|
+
id: z.string().meta({ description: "Fragment id inside the referenced library." }),
|
|
707
|
+
variables: z.record(z.string(), VariableValueSchema).optional().meta({ description: "Literal values passed into the fragment template." }),
|
|
708
|
+
bind: z.record(z.string(), z.string()).optional().meta({ description: "Accepted for compatibility but ignored by the current assembler." }),
|
|
709
|
+
required: z.boolean().optional().meta({ description: "Marker for important fragments. Accepted but not currently enforced." })
|
|
710
|
+
}).meta({
|
|
711
|
+
id: "fragmentRef",
|
|
712
|
+
description: "Selects one fragment from a referenced library."
|
|
673
713
|
});
|
|
674
714
|
//#endregion
|
|
675
715
|
//#region ../lib/prompt-fragments/fragment.ts
|
|
@@ -978,20 +1018,23 @@ async function loadAndCheckFragmentFile(url, fetchImpl, opts = {}) {
|
|
|
978
1018
|
};
|
|
979
1019
|
return checkFragmentFileValue(yaml.value, url);
|
|
980
1020
|
}
|
|
981
|
-
const providerSchema = z.enum(["SCCH", "Azure Foundry"]).default("SCCH");
|
|
1021
|
+
const providerSchema = z.enum(["SCCH", "Azure Foundry"]).default("SCCH").meta({ description: "The LLM provider serving the model. For Azure Foundry, model is the deployment name." });
|
|
982
1022
|
//#endregion
|
|
983
1023
|
//#region ../lib/coding-schema.ts
|
|
984
1024
|
const CodingYamlSchema = z.strictObject({
|
|
985
|
-
id: z.string().min(1),
|
|
986
|
-
name: z.string().optional(),
|
|
987
|
-
title: z.string().optional(),
|
|
1025
|
+
id: z.string().min(1).meta({ description: "Short machine-readable activity id, e.g. beginner-typescript." }),
|
|
1026
|
+
name: z.string().optional().meta({ description: "Optional human-readable label (not shown to the student)." }),
|
|
1027
|
+
title: z.string().optional().meta({ description: "Optional label shown to the student on the /<code> connection page." }),
|
|
988
1028
|
llm: z.strictObject({
|
|
989
|
-
model: z.string().min(1),
|
|
1029
|
+
model: z.string().min(1).meta({ description: "The model that answers. SERVER-ONLY and PINNED: the proxy always uses this model and ignores whatever model the coding agent sends." }),
|
|
990
1030
|
provider: providerSchema
|
|
1031
|
+
}).meta({
|
|
1032
|
+
id: "llm",
|
|
1033
|
+
description: "The pinned model and provider that answer coding requests."
|
|
991
1034
|
}),
|
|
992
|
-
fragment_files: z.array(FragmentFileRefSchema).default([]),
|
|
993
|
-
fragments: z.array(FragmentRefSchema).default([]),
|
|
994
|
-
instructions: z.string().min(1)
|
|
1035
|
+
fragment_files: z.array(FragmentFileRefSchema).default([]).meta({ description: "Optional fragment libraries this activity pulls shared prompt fragments from." }),
|
|
1036
|
+
fragments: z.array(FragmentRefSchema).default([]).meta({ description: "Optional fragments selected from fragment_files. Assembled in priority order and prepended AHEAD of instructions." }),
|
|
1037
|
+
instructions: z.string().min(1).meta({ description: "The assistant's system prompt. SERVER-ONLY: never sent to the browser or the coding agent, and appended AFTER the coding tool's own prompt (so the teacher has the final word). Constrain the assistant to what your class has learned." })
|
|
995
1038
|
});
|
|
996
1039
|
//#endregion
|
|
997
1040
|
//#region ../lib/coding-validate.ts
|
|
@@ -1052,10 +1095,16 @@ async function loadAndCheckCoding(url, fetchImpl, opts = {}) {
|
|
|
1052
1095
|
//#region ../lib/quiz-schema.ts
|
|
1053
1096
|
/** An optional content image attached to a question (carries no secret). */
|
|
1054
1097
|
const ImageRefSchema = z.strictObject({
|
|
1055
|
-
hosted: z.boolean().optional()
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1098
|
+
hosted: z.boolean().optional().meta({
|
|
1099
|
+
default: false,
|
|
1100
|
+
description: "When true, src is an app-hosted image NAME resolved server-side; otherwise src is an absolute URL or a path relative to the quiz's own URL."
|
|
1101
|
+
}),
|
|
1102
|
+
src: z.string().min(1).meta({ description: "The hosted image name (when hosted) or the image URL / relative path." }),
|
|
1103
|
+
alt: z.string().optional().meta({ description: "Accessible description shown if the image cannot be loaded." }),
|
|
1104
|
+
credit: z.string().optional().meta({ description: "Optional attribution (\"Content Credentials\") shown small below the image." })
|
|
1105
|
+
}).meta({
|
|
1106
|
+
id: "image",
|
|
1107
|
+
description: "An optional content image attached to a question."
|
|
1059
1108
|
});
|
|
1060
1109
|
/**
|
|
1061
1110
|
* One question. `id` keys the per-question stats (must be unique — see
|
|
@@ -1063,29 +1112,47 @@ const ImageRefSchema = z.strictObject({
|
|
|
1063
1112
|
* `evaluation` is the server-only grading prompt.
|
|
1064
1113
|
*/
|
|
1065
1114
|
const QuizQuestionSchema = z.strictObject({
|
|
1066
|
-
id: z.string().min(1),
|
|
1067
|
-
title: z.string().optional(),
|
|
1068
|
-
question: z.string().min(1),
|
|
1069
|
-
evaluation: z.string().min(1),
|
|
1115
|
+
id: z.string().min(1).meta({ description: "Stable question id, unique within the quiz (the per-question stats key)." }),
|
|
1116
|
+
title: z.string().optional().meta({ description: "Optional short label for the stats table and progress display." }),
|
|
1117
|
+
question: z.string().min(1).meta({ description: "The Markdown shown to the student." }),
|
|
1118
|
+
evaluation: z.string().min(1).meta({ description: "The grading prompt. SERVER-ONLY: never sent to the browser, so it may embed the expected answer and the grading rubric." }),
|
|
1070
1119
|
image: ImageRefSchema.optional(),
|
|
1071
|
-
imageInput: z.boolean().optional()
|
|
1120
|
+
imageInput: z.boolean().optional().meta({ description: "Overrides the quiz-level llm.imageInput for this question only (photo answers on/off)." })
|
|
1121
|
+
}).meta({
|
|
1122
|
+
id: "question",
|
|
1123
|
+
description: "One open-ended, LLM-graded quiz question."
|
|
1072
1124
|
});
|
|
1073
1125
|
const QuizYamlSchema = z.strictObject({
|
|
1074
|
-
id: z.string().min(1),
|
|
1075
|
-
name: z.string().optional(),
|
|
1076
|
-
title: z.string().optional(),
|
|
1077
|
-
description: z.string().optional(),
|
|
1078
|
-
anonymous: z.boolean().optional()
|
|
1079
|
-
|
|
1126
|
+
id: z.string().min(1).meta({ description: "Short machine-readable quiz id, e.g. countries-basics. Used as the per-quiz identity." }),
|
|
1127
|
+
name: z.string().optional().meta({ description: "Optional human-readable quiz title (used as a label)." }),
|
|
1128
|
+
title: z.string().optional().meta({ description: "Optional greeting shown to students on the welcome screen instead of the default message." }),
|
|
1129
|
+
description: z.string().optional().meta({ description: "Optional description shown to students below the welcome greeting (Markdown)." }),
|
|
1130
|
+
anonymous: z.boolean().optional().meta({
|
|
1131
|
+
default: true,
|
|
1132
|
+
description: "Quizzes are anonymous by default: answers are recorded for aggregate stats but not linked to a student. Set to false to attribute each attempt to the signed-in student."
|
|
1133
|
+
}),
|
|
1134
|
+
shuffle: z.boolean().optional().meta({
|
|
1135
|
+
default: true,
|
|
1136
|
+
description: "Present questions in a random order per attempt. Set to false to keep the authored order."
|
|
1137
|
+
}),
|
|
1080
1138
|
llm: z.strictObject({
|
|
1081
|
-
model: z.string().min(1),
|
|
1139
|
+
model: z.string().min(1).meta({ description: "The model that grades answers and drives the per-question discussion chat." }),
|
|
1082
1140
|
provider: providerSchema,
|
|
1083
|
-
imageInput: z.boolean().optional()
|
|
1141
|
+
imageInput: z.boolean().optional().meta({
|
|
1142
|
+
default: false,
|
|
1143
|
+
description: "Default for all questions: students may attach photos (up to 3, 5 MB each) to their answers. The model must be vision-capable. A per-question imageInput overrides it."
|
|
1144
|
+
})
|
|
1145
|
+
}).meta({
|
|
1146
|
+
id: "llm",
|
|
1147
|
+
description: "The single model + provider that grades and discusses answers."
|
|
1084
1148
|
}),
|
|
1085
|
-
discussion: z.strictObject({ instructions: z.string().min(1) }).optional()
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1149
|
+
discussion: z.strictObject({ instructions: z.string().min(1).meta({ description: "Optional guidance appended to the per-question follow-up discussion chat's system prompt." }) }).optional().meta({
|
|
1150
|
+
id: "discussion",
|
|
1151
|
+
description: "Optional guidance for the per-question follow-up discussion chat."
|
|
1152
|
+
}),
|
|
1153
|
+
fragment_files: z.array(FragmentFileRefSchema).default([]).meta({ description: "Optional fragment libraries this quiz pulls shared prompt fragments from." }),
|
|
1154
|
+
fragments: z.array(FragmentRefSchema).default([]).meta({ description: "Optional fragments selected from fragment_files. Assembled in priority order and prepended to BOTH the grader prompt and the discussion chat's system prompt." }),
|
|
1155
|
+
questions: z.array(QuizQuestionSchema).min(1).meta({ description: "The quiz questions. Each is open-ended and graded by the LLM via its evaluation prompt." })
|
|
1089
1156
|
});
|
|
1090
1157
|
//#endregion
|
|
1091
1158
|
//#region ../lib/quiz-validate.ts
|
|
@@ -1175,25 +1242,40 @@ async function loadAndCheckQuiz(url, fetchImpl, opts = {}) {
|
|
|
1175
1242
|
* input on click. Tutors may define any number; the UI samples at most 5.
|
|
1176
1243
|
*/
|
|
1177
1244
|
const ExampleQuestionSchema = z.strictObject({
|
|
1178
|
-
title: z.string().min(1),
|
|
1179
|
-
question: z.string().min(1)
|
|
1245
|
+
title: z.string().min(1).meta({ description: "Short clickable label shown on the welcome screen." }),
|
|
1246
|
+
question: z.string().min(1).meta({ description: "Full question text. Shown as a tooltip and placed into the chat input on click." })
|
|
1247
|
+
}).meta({
|
|
1248
|
+
id: "exampleQuestion",
|
|
1249
|
+
description: "An example question shown on the welcome screen."
|
|
1180
1250
|
});
|
|
1181
1251
|
const TutorSchema = z.strictObject({
|
|
1182
|
-
id: z.string(),
|
|
1183
|
-
name: z.string(),
|
|
1184
|
-
title: z.string().optional(),
|
|
1185
|
-
description: z.string(),
|
|
1186
|
-
exampleQuestions: z.array(ExampleQuestionSchema).optional(),
|
|
1187
|
-
anonymous: z.boolean().optional()
|
|
1252
|
+
id: z.string().meta({ description: "Short machine-readable tutor id, e.g. fractions-de." }),
|
|
1253
|
+
name: z.string().meta({ description: "Human-readable tutor title." }),
|
|
1254
|
+
title: z.string().optional().meta({ description: "Optional greeting shown to students on the empty chat instead of the default welcome message." }),
|
|
1255
|
+
description: z.string().meta({ description: "Short description of what this tutor does. Shown to students below the welcome greeting." }),
|
|
1256
|
+
exampleQuestions: z.array(ExampleQuestionSchema).optional().meta({ description: "Optional example questions shown to students below the description on the empty chat. Clicking one puts the question text into the chat input. At most 5 are shown; with more, a random 5 are picked per page load." }),
|
|
1257
|
+
anonymous: z.boolean().optional().meta({
|
|
1258
|
+
default: true,
|
|
1259
|
+
description: "Chats are anonymous by default: no link between the signed-in student and their chat is stored. Set to false to record which student each chat belongs to."
|
|
1260
|
+
}),
|
|
1188
1261
|
llm: z.strictObject({
|
|
1189
|
-
model: z.string(),
|
|
1262
|
+
model: z.string().meta({ description: "Model used for this tutor." }),
|
|
1190
1263
|
provider: providerSchema,
|
|
1191
|
-
imageInput: z.boolean().optional()
|
|
1264
|
+
imageInput: z.boolean().optional().meta({
|
|
1265
|
+
default: true,
|
|
1266
|
+
description: "Image uploads are enabled by default. Set to false to hide the upload UI for text-only tutors or non-vision-capable models."
|
|
1267
|
+
})
|
|
1268
|
+
}).meta({
|
|
1269
|
+
id: "llm",
|
|
1270
|
+
description: "The model and provider that back this tutor."
|
|
1192
1271
|
}),
|
|
1193
1272
|
prompt: z.strictObject({
|
|
1194
|
-
fragment_files: z.array(FragmentFileRefSchema).default([]),
|
|
1195
|
-
fragments: z.array(FragmentRefSchema).default([]),
|
|
1196
|
-
tutor_instructions: z.string()
|
|
1273
|
+
fragment_files: z.array(FragmentFileRefSchema).default([]).meta({ description: "Optional fragment libraries used by this tutor." }),
|
|
1274
|
+
fragments: z.array(FragmentRefSchema).default([]).meta({ description: "Optional fragments selected from fragment_files." }),
|
|
1275
|
+
tutor_instructions: z.string().meta({ description: "Final tutor-specific system-prompt instructions. For single-file tutors, this can be the whole prompt." })
|
|
1276
|
+
}).meta({
|
|
1277
|
+
id: "prompt",
|
|
1278
|
+
description: "The assembled system prompt: fragments plus tutor instructions."
|
|
1197
1279
|
})
|
|
1198
1280
|
});
|
|
1199
1281
|
//#endregion
|
|
@@ -1236,19 +1318,25 @@ async function loadAndBuildTutorPrompt(url, fetchImpl, opts = {}) {
|
|
|
1236
1318
|
//#endregion
|
|
1237
1319
|
//#region ../lib/writing-schema.ts
|
|
1238
1320
|
const WritingYamlSchema = z.strictObject({
|
|
1239
|
-
id: z.string().min(1),
|
|
1240
|
-
name: z.string().optional(),
|
|
1241
|
-
title: z.string().optional(),
|
|
1242
|
-
description: z.string().optional(),
|
|
1243
|
-
anonymous: z.boolean().optional()
|
|
1321
|
+
id: z.string().min(1).meta({ description: "Short machine-readable activity id, e.g. human-animal-short-story." }),
|
|
1322
|
+
name: z.string().optional().meta({ description: "Optional human-readable title (used as a label)." }),
|
|
1323
|
+
title: z.string().optional().meta({ description: "Optional greeting shown to students on the welcome screen instead of the default message." }),
|
|
1324
|
+
description: z.string().optional().meta({ description: "Optional description shown to students below the welcome greeting (Markdown)." }),
|
|
1325
|
+
anonymous: z.boolean().optional().meta({
|
|
1326
|
+
default: false,
|
|
1327
|
+
description: "Writing DIVERGES: it defaults to false (attributed), because review and the Save feature need to know whose text it is. Set to true for ephemeral, unattributed writing — which also disables saving."
|
|
1328
|
+
}),
|
|
1244
1329
|
llm: z.strictObject({
|
|
1245
|
-
model: z.string().min(1),
|
|
1330
|
+
model: z.string().min(1).meta({ description: "The model that drives the feedback chat." }),
|
|
1246
1331
|
provider: providerSchema
|
|
1332
|
+
}).meta({
|
|
1333
|
+
id: "llm",
|
|
1334
|
+
description: "The model and provider that back the writing coach."
|
|
1247
1335
|
}),
|
|
1248
|
-
fragment_files: z.array(FragmentFileRefSchema).default([]),
|
|
1249
|
-
fragments: z.array(FragmentRefSchema).default([]),
|
|
1250
|
-
instructions: z.string().min(1),
|
|
1251
|
-
placeholder: z.string().optional()
|
|
1336
|
+
fragment_files: z.array(FragmentFileRefSchema).default([]).meta({ description: "Optional fragment libraries this activity pulls shared prompt fragments from." }),
|
|
1337
|
+
fragments: z.array(FragmentRefSchema).default([]).meta({ description: "Optional fragments selected from fragment_files. Assembled in priority order and prepended AHEAD of instructions." }),
|
|
1338
|
+
instructions: z.string().min(1).meta({ description: "The writing coach's system prompt. SERVER-ONLY: never sent to the browser, so it may describe the assessment criteria and coaching strategy." }),
|
|
1339
|
+
placeholder: z.string().optional().meta({ description: "Optional starter text prefilled into the editor. Empty for a blank page." })
|
|
1252
1340
|
});
|
|
1253
1341
|
//#endregion
|
|
1254
1342
|
//#region ../lib/writing-validate.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novedu/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Command-line companion for the Novedu chat app. Validates tutor, fragment, quiz, writing and coding YAML definitions; signs in with Entra ID and manages codes and app-hosted files over the app's API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|