feat(ai): expand Quiz tools — edit/delete questions, edit meta, stats

Completes the Quiz CRUD surface for the AI agent. Five new tools:

- update_quiz (propose) — rename/archive/pin + description/category
- update_quiz_question (propose) — text, type+options, explanation;
  rejects a type swap without a matching optionsJson
- delete_quiz_question (propose) — symmetric to add_quiz_question
- get_quiz_questions (auto) — lets the planner see existing questions
  before appending more (avoids duplicates)
- get_quiz_stats (auto) — attemptCount / avgScore / bestScore /
  lastAttemptAt; enables adaptive missions like "analyze my weak spots
  and generate harder questions"

delete_quiz deliberately left out — too destructive to leave in the
AI's hands when the user can delete manually in two clicks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-19 19:50:24 +02:00
parent dd756c4664
commit 7fb31e41b5
3 changed files with 346 additions and 4 deletions

View file

@ -974,6 +974,31 @@ export const AI_TOOL_CATALOG: readonly ToolSchema[] = [
},
],
},
{
name: 'update_quiz',
module: 'quiz',
description:
'Aktualisiert Metadaten eines bestehenden Quiz. Nur die mitgegebenen Felder werden geschrieben. Leerstring bei description/category loescht den Wert',
defaultPolicy: 'propose',
parameters: [
{ name: 'quizId', type: 'string', description: 'ID des Quiz', required: true },
{ name: 'title', type: 'string', description: 'Neuer Titel', required: false },
{ name: 'description', type: 'string', description: 'Neue Beschreibung', required: false },
{ name: 'category', type: 'string', description: 'Neue Kategorie', required: false },
{
name: 'isPinned',
type: 'boolean',
description: 'Quiz oben anpinnen',
required: false,
},
{
name: 'isArchived',
type: 'boolean',
description: 'Quiz archivieren (aus Liste ausblenden)',
required: false,
},
],
},
{
name: 'add_quiz_question',
module: 'quiz',
@ -1004,6 +1029,50 @@ export const AI_TOOL_CATALOG: readonly ToolSchema[] = [
},
],
},
{
name: 'update_quiz_question',
module: 'quiz',
description:
'Aktualisiert eine vorhandene Frage. Beim Aendern der Antworten muessen type + optionsJson zusammen uebergeben werden (gleiches Format wie bei add_quiz_question). Text und Erklaerung koennen unabhaengig geaendert werden',
defaultPolicy: 'propose',
parameters: [
{ name: 'questionId', type: 'string', description: 'ID der Frage', required: true },
{
name: 'questionText',
type: 'string',
description: 'Neue Fragestellung',
required: false,
},
{
name: 'type',
type: 'string',
description: 'Neuer Fragetyp (wenn optionsJson mitgegeben wird)',
required: false,
enum: ['single', 'multi', 'truefalse', 'text'],
},
{
name: 'optionsJson',
type: 'string',
description: 'Neue Antwortdaten — Format abhaengig vom type',
required: false,
},
{
name: 'explanation',
type: 'string',
description: 'Neue Erklaerung (Leerstring loescht)',
required: false,
},
],
},
{
name: 'delete_quiz_question',
module: 'quiz',
description: 'Loescht eine Frage aus einem Quiz',
defaultPolicy: 'propose',
parameters: [
{ name: 'questionId', type: 'string', description: 'ID der Frage', required: true },
],
},
{
name: 'list_quizzes',
module: 'quiz',
@ -1025,6 +1094,22 @@ export const AI_TOOL_CATALOG: readonly ToolSchema[] = [
},
],
},
{
name: 'get_quiz_questions',
module: 'quiz',
description:
'Liest alle Fragen eines Quiz (id, order, type, questionText, options, explanation). Nutze dies bevor du weitere Fragen ergaenzt, um Duplikate zu vermeiden',
defaultPolicy: 'auto',
parameters: [{ name: 'quizId', type: 'string', description: 'ID des Quiz', required: true }],
},
{
name: 'get_quiz_stats',
module: 'quiz',
description:
'Gibt Statistiken zu einem Quiz zurueck: Anzahl der Versuche, Durchschnitts-Score, bester Score, letzter Versuch. Nuetzlich fuer adaptive Missionen (Schwachstellen erkennen)',
defaultPolicy: 'auto',
parameters: [{ name: 'quizId', type: 'string', description: 'ID des Quiz', required: true }],
},
];
// ═══════════════════════════════════════════════════════════════