@bendyline/gilde 0.1.92 → 0.1.93

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 (109) hide show
  1. package/README.md +6 -4
  2. package/data/craftbook-templates/fr/freeze-scope/versions/1.4.0/craftbook.json +94 -0
  3. package/data/craftbook-templates/fr/freeze-scope/versions/1.4.0/test.json +131 -0
  4. package/data/craftbook-templates/index.json +1 -1
  5. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.7/craftbook.json +315 -0
  6. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.7/test.json +89 -0
  7. package/data/craftbook-templates/ph/photo-library-nightly/manifest.json +22 -0
  8. package/data/craftbook-templates/ph/photo-library-nightly/versions/1.0.0/craftbook.json +186 -0
  9. package/data/craftbook-templates/ph/photo-library-nightly/versions/1.0.0/test.json +69 -0
  10. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.17/craftbook.json +486 -0
  11. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.17/test.json +265 -0
  12. package/data/craftbook-templates/raw-index.json +1 -1
  13. package/data/craftbook-templates/re/report-pdf/versions/1.1.7/craftbook.json +316 -0
  14. package/data/craftbook-templates/re/report-pdf/versions/1.1.7/test.json +101 -0
  15. package/data/craftbook-templates/re/research-to-document/versions/1.2.7/craftbook.json +387 -0
  16. package/data/craftbook-templates/re/research-to-document/versions/1.2.7/test.json +242 -0
  17. package/data/gezel-templates/cu/curator/manifest.json +20 -0
  18. package/data/gezel-templates/cu/curator/versions/1.0.0/about.md +22 -0
  19. package/data/gezel-templates/cu/curator/versions/1.0.0/manifest.json +23 -0
  20. package/data/gezel-templates/index.json +1 -1
  21. package/data/gezel-templates/pu/puzzelaar/manifest.json +19 -0
  22. package/data/gezel-templates/pu/puzzelaar/versions/1.0.0/about.md +18 -0
  23. package/data/gezel-templates/pu/puzzelaar/versions/1.0.0/manifest.json +16 -0
  24. package/data/gezel-templates/raw-index.json +1 -1
  25. package/data/gezel-templates/wo/woordsmid/manifest.json +19 -0
  26. package/data/gezel-templates/wo/woordsmid/versions/1.0.0/about.md +18 -0
  27. package/data/gezel-templates/wo/woordsmid/versions/1.0.0/manifest.json +16 -0
  28. package/data/knowledge-catalogs/be/bendyline-how-to-books/manifest.json +26 -0
  29. package/data/knowledge-catalogs/be/bendyline-how-to-books/versions/2026.10.8/manifest.json +8950 -0
  30. package/data/knowledge-catalogs/index.json +1 -1
  31. package/data/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/manifest.json +22 -0
  32. package/data/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/versions/2026.10.16/manifest.json +18270 -0
  33. package/data/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/manifest.json +23 -0
  34. package/data/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/versions/2026.10.16/manifest.json +1098 -0
  35. package/data/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/manifest.json +23 -0
  36. package/data/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/versions/2026.10.16/manifest.json +722 -0
  37. package/data/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/manifest.json +23 -0
  38. package/data/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/versions/2026.10.16/manifest.json +530 -0
  39. package/data/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/manifest.json +23 -0
  40. package/data/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/versions/2026.10.16/manifest.json +602 -0
  41. package/data/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/manifest.json +23 -0
  42. package/data/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/versions/2026.10.16/manifest.json +250 -0
  43. package/data/knowledge-catalogs/mi/microsoftdocs-dotnet-en/manifest.json +22 -0
  44. package/data/knowledge-catalogs/mi/microsoftdocs-dotnet-en/versions/2026.10.16/manifest.json +16794 -0
  45. package/data/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/manifest.json +22 -0
  46. package/data/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/versions/2026.10.16/manifest.json +1106 -0
  47. package/data/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/manifest.json +22 -0
  48. package/data/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/versions/2026.10.16/manifest.json +234 -0
  49. package/data/knowledge-catalogs/mi/microsoftdocs-powershell-en/manifest.json +22 -0
  50. package/data/knowledge-catalogs/mi/microsoftdocs-powershell-en/versions/2026.10.16/manifest.json +522 -0
  51. package/data/knowledge-catalogs/mi/microsoftdocs-sql-server-en/manifest.json +22 -0
  52. package/data/knowledge-catalogs/mi/microsoftdocs-sql-server-en/versions/2026.10.16/manifest.json +1850 -0
  53. package/data/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/manifest.json +22 -0
  54. package/data/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/versions/2026.10.16/manifest.json +1734 -0
  55. package/data/knowledge-catalogs/mi/microsoftdocs-windows-server-en/manifest.json +22 -0
  56. package/data/knowledge-catalogs/mi/microsoftdocs-windows-server-en/versions/2026.10.16/manifest.json +774 -0
  57. package/data/knowledge-catalogs/raw-index.json +1 -1
  58. package/data/project-types/ch/checkers/versions/1.2.3/about.md +9 -0
  59. package/data/project-types/ch/checkers/versions/1.2.3/game.json +77 -0
  60. package/data/project-types/ch/checkers/versions/1.2.3/manifest.json +182 -0
  61. package/data/project-types/ch/checkers/versions/1.2.3/mission.md +9 -0
  62. package/data/project-types/ch/checkers/versions/1.2.3/pages/board/index.html +1158 -0
  63. package/data/project-types/ch/chess/versions/1.0.6/about.md +9 -0
  64. package/data/project-types/ch/chess/versions/1.0.6/game.json +127 -0
  65. package/data/project-types/ch/chess/versions/1.0.6/manifest.json +196 -0
  66. package/data/project-types/ch/chess/versions/1.0.6/mission.md +8 -0
  67. package/data/project-types/ch/chess/versions/1.0.6/pages/board/index.html +851 -0
  68. package/data/project-types/fi/fitness-coach/versions/1.1.0/about.md +8 -0
  69. package/data/project-types/fi/fitness-coach/versions/1.1.0/craftbooks/training-recap.json +34 -0
  70. package/data/project-types/fi/fitness-coach/versions/1.1.0/manifest.json +229 -0
  71. package/data/project-types/fi/fitness-coach/versions/1.1.0/mission.md +7 -0
  72. package/data/project-types/fi/fitness-coach/versions/1.1.0/pages/dashboard/index.html +383 -0
  73. package/data/project-types/fi/fitness-coach/versions/1.1.0/training.json +6 -0
  74. package/data/project-types/fl/flashcards/versions/1.1.2/about.md +8 -0
  75. package/data/project-types/fl/flashcards/versions/1.1.2/deck.json +7 -0
  76. package/data/project-types/fl/flashcards/versions/1.1.2/manifest.json +169 -0
  77. package/data/project-types/fl/flashcards/versions/1.1.2/mission.md +6 -0
  78. package/data/project-types/fl/flashcards/versions/1.1.2/pages/review/index.html +477 -0
  79. package/data/project-types/fl/flashcards/versions/1.2.0/about.md +8 -0
  80. package/data/project-types/fl/flashcards/versions/1.2.0/deck.json +7 -0
  81. package/data/project-types/fl/flashcards/versions/1.2.0/manifest.json +207 -0
  82. package/data/project-types/fl/flashcards/versions/1.2.0/mission.md +6 -0
  83. package/data/project-types/fl/flashcards/versions/1.2.0/pages/review/index.html +549 -0
  84. package/data/project-types/go/go/versions/1.0.4/about.md +9 -0
  85. package/data/project-types/go/go/versions/1.0.4/game.json +103 -0
  86. package/data/project-types/go/go/versions/1.0.4/manifest.json +182 -0
  87. package/data/project-types/go/go/versions/1.0.4/mission.md +8 -0
  88. package/data/project-types/go/go/versions/1.0.4/pages/board/index.html +855 -0
  89. package/data/project-types/index.json +1 -1
  90. package/data/project-types/la/language-trainer/versions/1.1.0/about.md +8 -0
  91. package/data/project-types/la/language-trainer/versions/1.1.0/manifest.json +245 -0
  92. package/data/project-types/la/language-trainer/versions/1.1.0/mission.md +4 -0
  93. package/data/project-types/la/language-trainer/versions/1.1.0/pages/dashboard/index.html +468 -0
  94. package/data/project-types/la/language-trainer/versions/1.1.0/progress.json +10 -0
  95. package/data/project-types/la/language-trainer/versions/1.1.0/reviews.json +3 -0
  96. package/data/project-types/la/language-trainer/versions/1.1.0/scenarios.json +84 -0
  97. package/data/project-types/raw-index.json +1 -1
  98. package/data/project-types/wo/word-clues/manifest.json +20 -0
  99. package/data/project-types/wo/word-clues/versions/1.0.0/about.md +7 -0
  100. package/data/project-types/wo/word-clues/versions/1.0.0/game.json +14 -0
  101. package/data/project-types/wo/word-clues/versions/1.0.0/manifest.json +205 -0
  102. package/data/project-types/wo/word-clues/versions/1.0.0/mission.md +3 -0
  103. package/data/project-types/wo/word-clues/versions/1.0.0/pages/board/index.html +399 -0
  104. package/data/project-types/wo/word-clues/versions/1.0.0/words.json +216 -0
  105. package/package.json +5 -3
  106. package/schemas/craftbook-doc.schema.json +14 -0
  107. package/schemas/craftbook-template-version.schema.json +14 -0
  108. package/schemas/craftbook-test.schema.json +2 -0
  109. package/schemas/project-type-version.schema.json +131 -0
@@ -0,0 +1,207 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "version": "1.2.0",
4
+ "releasedAt": "2026-10-07T00:00:00Z",
5
+ "meesterManaged": false,
6
+ "params": {
7
+ "type": "object",
8
+ "properties": {
9
+ "subject": {
10
+ "type": "string",
11
+ "title": "What are you learning?",
12
+ "description": "The deck's subject — e.g. Spanish vocabulary, AWS certification, organic chemistry.",
13
+ "default": "Spanish vocabulary"
14
+ }
15
+ },
16
+ "required": [
17
+ "subject"
18
+ ]
19
+ },
20
+ "nameTemplate": "{{subject}} Flashcards",
21
+ "aboutTemplate": "about.md",
22
+ "missionTemplate": "mission.md",
23
+ "gezels": [
24
+ {
25
+ "templateId": "study-buddy",
26
+ "voorman": true
27
+ }
28
+ ],
29
+ "scripts": {
30
+ "deck-store": "import { type InferredInput, defineScript, gezel } from '@bendyline/gezel-sdk';\n\nexport const meta = defineScript({\n name: 'deck-store',\n description:\n \"Read and update the flashcard deck (deck.json). Call action:'add_cards' with a cards array ([{front, back}, ...]) to add practice cards; action:'state' to see due counts and box distribution; action:'list_deck' to read every card. The review page drives 'record_review', 'finish_session' and 'stage_notes' itself.\",\n inputs: {\n action: {\n type: 'string',\n description:\n \"One of 'state', 'list_deck', 'add_cards', 'record_review', 'finish_session', or 'stage_notes'.\",\n required: true,\n },\n cards: {\n type: 'json',\n description: 'For add_cards: an array of {front, back} objects.',\n },\n cardId: { type: 'string', description: \"For record_review: the card id, e.g. 'card-3'.\" },\n verdict: { type: 'string', description: \"For record_review: 'got-it' or 'missed'.\" },\n reviewed: { type: 'number', description: 'For finish_session: how many cards were reviewed.' },\n missedIds: { type: 'json', description: 'For finish_session: array of card ids that were missed.' },\n notes: { type: 'string', description: 'For stage_notes: notes the student pasted to turn into cards.' },\n },\n outputs: {\n total: { type: 'number', description: 'Cards in the deck.' },\n due: { type: 'number', description: 'Cards due for review now.' },\n summary: { type: 'string', description: 'Human-readable deck or session summary.' },\n missedList: { type: 'string', description: 'Fronts of the cards missed this session (finish_session).' },\n status: { type: 'string', description: \"'forging' after stage_notes, else 'idle'.\" },\n notes: { type: 'string', description: 'The staged notes (stage_notes), clipped to 3000 characters.' },\n },\n requires: ['workspace.read', 'workspace.write', 'reminders'],\n} as const);\n\nconst input = gezel.input as InferredInput<typeof meta>;\n\n// ── leitner-rules-start ──\n// Pure Leitner-box scheduling (3 boxes). Box 1 reviews daily, box 2 every\n// 3 days, box 3 weekly. A got-it promotes (capped at 3); a miss goes back\n// to box 1. Never-reviewed cards are always due. Kept self-contained\n// between these sentinels so unit tests execute the shipped bytes.\n\nvar BOX_INTERVAL_DAYS = { 1: 1, 2: 3, 3: 7 };\nvar DAY_MS = 24 * 60 * 60 * 1000;\n\nfunction isDue(card, nowMs) {\n if (!card.lastReviewedAt) return true;\n var interval = BOX_INTERVAL_DAYS[card.box] || 1;\n return nowMs - Date.parse(card.lastReviewedAt) >= interval * DAY_MS;\n}\n\nfunction computeDueIds(cards, nowMs) {\n return cards\n .filter(function (c) {\n return isDue(c, nowMs);\n })\n .sort(function (a, b) {\n if (a.box !== b.box) return a.box - b.box;\n return a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0;\n })\n .map(function (c) {\n return c.id;\n });\n}\n\nfunction applyReview(card, verdict, nowIso) {\n if (verdict === 'got-it') card.box = Math.min(3, card.box + 1);\n else card.box = 1;\n card.lastReviewedAt = nowIso;\n card.stats = card.stats || { gotIt: 0, missed: 0 };\n if (verdict === 'got-it') card.stats.gotIt += 1;\n else card.stats.missed += 1;\n return card;\n}\n\nfunction nextCardId(cards) {\n var max = 0;\n for (var i = 0; i < cards.length; i += 1) {\n var m = /^card-(\\d+)$/.exec(cards[i].id);\n if (m) max = Math.max(max, Number(m[1]));\n }\n return 'card-' + (max + 1);\n}\n// ── leitner-rules-end ──\n\nconst DECK_FILE = 'deck.json';\n\ninterface Card {\n id: string;\n front: string;\n back: string;\n box: number;\n lastReviewedAt: string | null;\n createdAt: string;\n stats: { gotIt: number; missed: number };\n}\n\ninterface Deck {\n version: number;\n subject: string;\n cards: Card[];\n dueCardIds: string[];\n updatedAt: string;\n}\n\nasync function load(): Promise<Deck> {\n let raw: string;\n try {\n raw = await gezel.fs.read(DECK_FILE);\n } catch {\n return { version: 1, subject: 'your subject', cards: [], dueCardIds: [], updatedAt: new Date().toISOString() };\n }\n const parsed = JSON.parse(raw) as Partial<Deck>;\n return {\n version: 1,\n subject: typeof parsed.subject === 'string' ? parsed.subject : 'your subject',\n cards: Array.isArray(parsed.cards) ? (parsed.cards as Card[]) : [],\n dueCardIds: [],\n updatedAt: new Date().toISOString(),\n };\n}\n\n// ── reminder-start ──\n// The next time a reviewed card comes due, worked out from the deck itself:\n// the one moment a reminder is earned. Nothing left to come due clears it.\n// Kept self-contained between these sentinels so unit tests execute the\n// shipped bytes.\nasync function remind(deck, nowMs) {\n var next = Infinity;\n for (var i = 0; i < deck.cards.length; i += 1) {\n var card = deck.cards[i];\n if (!card.lastReviewedAt) continue;\n var dueAt = Date.parse(card.lastReviewedAt) + (BOX_INTERVAL_DAYS[card.box] || 1) * DAY_MS;\n if (dueAt > nowMs && dueAt < next) next = dueAt;\n }\n try {\n if (!isFinite(next)) {\n await gezel.reminder.clear();\n return;\n }\n var count = computeDueIds(deck.cards, next).length;\n await gezel.reminder.set({\n at: new Date(next).toISOString(),\n title: 'Flashcards are due',\n body: count + (count === 1 ? ' card' : ' cards') + ' on \"' + deck.subject + '\" ready to review.',\n });\n } catch (err) {\n // A host without reminders still keeps the deck.\n }\n}\n// ── reminder-end ──\n\nasync function persist(deck: Deck): Promise<Deck> {\n const nowMs = Date.now();\n deck.dueCardIds = computeDueIds(deck.cards, nowMs);\n deck.updatedAt = new Date(nowMs).toISOString();\n await gezel.fs.write(DECK_FILE, `${JSON.stringify(deck, null, 2)}\\n`);\n await remind(deck, nowMs);\n return deck;\n}\n\nconst deck = await load();\ndeck.dueCardIds = computeDueIds(deck.cards, Date.now());\nlet summary = '';\nlet missedList = '';\nlet status = 'idle';\nlet notes = '';\n// Cards from one call: enough for a set of notes, short enough that a small\n// model writes them in one turn.\nconst MAX_NEW_CARDS = 8;\n\nif (input.action === 'add_cards') {\n const raw = Array.isArray(input.cards) ? input.cards : [];\n const added: string[] = [];\n for (const entry of raw.slice(0, MAX_NEW_CARDS)) {\n const front = typeof (entry as { front?: unknown }).front === 'string' ? (entry as { front: string }).front.trim() : '';\n const back = typeof (entry as { back?: unknown }).back === 'string' ? (entry as { back: string }).back.trim() : '';\n if (!front || !back) continue;\n const card: Card = {\n id: nextCardId(deck.cards),\n front: front.slice(0, 300),\n back: back.slice(0, 500),\n box: 1,\n lastReviewedAt: null,\n createdAt: new Date().toISOString(),\n stats: { gotIt: 0, missed: 0 },\n };\n deck.cards.push(card);\n added.push(card.id);\n }\n if (added.length === 0) {\n throw new Error(\"add_cards needs a non-empty 'cards' array of {front, back} objects.\");\n }\n await persist(deck);\n summary = `Added ${added.length} card(s). ${deck.cards.length} in the deck, ${deck.dueCardIds.length} due now.`;\n} else if (input.action === 'record_review') {\n if (!input.cardId || (input.verdict !== 'got-it' && input.verdict !== 'missed')) {\n throw new Error(\"record_review needs 'cardId' and verdict 'got-it' or 'missed'.\");\n }\n const card = deck.cards.find((c) => c.id === input.cardId);\n if (!card) throw new Error(`No card '${input.cardId}' in the deck.`);\n applyReview(card, input.verdict, new Date().toISOString());\n await persist(deck);\n summary = `${card.id} marked ${input.verdict} (now box ${card.box}). ${deck.dueCardIds.length} still due.`;\n} else if (input.action === 'finish_session') {\n const reviewed = typeof input.reviewed === 'number' ? input.reviewed : 0;\n const missedIds = Array.isArray(input.missedIds) ? input.missedIds.map(String) : [];\n const missedFronts = missedIds\n .map((id) => deck.cards.find((c) => c.id === id)?.front)\n .filter((f): f is string => typeof f === 'string');\n missedList = missedFronts.length ? missedFronts.join(' | ') : '(none)';\n summary = `Reviewed ${reviewed} card(s): ${reviewed - missedIds.length} got it, ${missedIds.length} missed. ${deck.dueCardIds.length} due next.`;\n} else if (input.action === 'list_deck') {\n summary = deck.cards.length\n ? deck.cards\n .map((c) => `${c.id} [box ${c.box}] ${c.front} -> ${c.back}`)\n .join('\\n')\n : 'The deck is empty — add cards with add_cards.';\n} else if (input.action === 'state') {\n const boxes = { 1: 0, 2: 0, 3: 0 } as Record<number, number>;\n for (const c of deck.cards) boxes[c.box] = (boxes[c.box] ?? 0) + 1;\n summary = deck.cards.length\n ? `${deck.cards.length} card(s) on \"${deck.subject}\": ${deck.dueCardIds.length} due now. Boxes: ${boxes[1]} daily, ${boxes[2]} every-3-days, ${boxes[3]} weekly.`\n : `The deck for \"${deck.subject}\" is empty — offer to write the first cards.`;\n} else if (input.action === 'stage_notes') {\n notes = String(input.notes ?? '').trim().slice(0, 3000);\n if (notes.length < 20) throw new Error('Paste a few sentences of notes to make cards from.');\n status = 'forging';\n summary = `Notes staged for \"${deck.subject}\". ${deck.cards.length} card(s) in the deck so far.`;\n} else {\n throw new Error(\n `Unknown action '${input.action}'. Use state, list_deck, add_cards, record_review, finish_session, or stage_notes.`,\n );\n}\n\ngezel.output({\n total: deck.cards.length,\n due: deck.dueCardIds.length,\n summary,\n missedList,\n status,\n notes,\n});\n"
31
+ },
32
+ "tools": [
33
+ {
34
+ "name": "add_cards",
35
+ "description": "Add flashcards to the deck: a cards array of {front, back} objects, one fact per card, at most 8 at a time. The deck's summary becomes your reply.",
36
+ "script": "deck-store",
37
+ "inputs": {
38
+ "type": "object",
39
+ "properties": {
40
+ "cards": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "object",
44
+ "properties": {
45
+ "front": {
46
+ "type": "string"
47
+ },
48
+ "back": {
49
+ "type": "string"
50
+ }
51
+ },
52
+ "required": [
53
+ "front",
54
+ "back"
55
+ ]
56
+ },
57
+ "description": "The cards to add."
58
+ }
59
+ },
60
+ "required": [
61
+ "cards"
62
+ ]
63
+ },
64
+ "bind": {
65
+ "action": "add_cards"
66
+ },
67
+ "turn": {
68
+ "show": "summary",
69
+ "fallback": "Cards added to the deck."
70
+ }
71
+ },
72
+ {
73
+ "name": "deck_status",
74
+ "description": "See the deck: how many cards, how many due, and the box distribution.",
75
+ "script": "deck-store",
76
+ "inputs": {
77
+ "type": "object",
78
+ "properties": {}
79
+ },
80
+ "bind": {
81
+ "action": "state"
82
+ }
83
+ },
84
+ {
85
+ "name": "list_deck",
86
+ "description": "Read every card in the deck (id, box, front, back).",
87
+ "script": "deck-store",
88
+ "inputs": {
89
+ "type": "object",
90
+ "properties": {}
91
+ },
92
+ "bind": {
93
+ "action": "list_deck"
94
+ }
95
+ },
96
+ {
97
+ "name": "record_review",
98
+ "description": "The review page's per-card verdict.",
99
+ "script": "deck-store",
100
+ "inputs": {
101
+ "type": "object",
102
+ "properties": {
103
+ "cardId": {
104
+ "type": "string"
105
+ },
106
+ "verdict": {
107
+ "type": "string",
108
+ "enum": [
109
+ "got-it",
110
+ "missed"
111
+ ]
112
+ }
113
+ },
114
+ "required": [
115
+ "cardId",
116
+ "verdict"
117
+ ]
118
+ },
119
+ "bind": {
120
+ "action": "record_review"
121
+ }
122
+ },
123
+ {
124
+ "name": "finish_session",
125
+ "description": "The review page's end-of-session summary.",
126
+ "script": "deck-store",
127
+ "inputs": {
128
+ "type": "object",
129
+ "properties": {
130
+ "reviewed": {
131
+ "type": "number"
132
+ },
133
+ "missedIds": {
134
+ "type": "array",
135
+ "items": {
136
+ "type": "string"
137
+ }
138
+ }
139
+ },
140
+ "required": [
141
+ "reviewed"
142
+ ]
143
+ },
144
+ "bind": {
145
+ "action": "finish_session"
146
+ },
147
+ "reaction": {
148
+ "gezel": "study-buddy",
149
+ "prompt": "The user finished a flashcard review session. {{output.summary}}\nMissed cards: {{output.missedList}}\nReply with ONE short encouraging line — specific beats generic. If any cards were missed, offer to add fresh practice cards that come at those same ideas from a new angle (add them with your deck capabilities if the user agrees)."
150
+ }
151
+ },
152
+ {
153
+ "name": "forge_from_notes",
154
+ "description": "The review page's notes-to-cards request.",
155
+ "script": "deck-store",
156
+ "inputs": {
157
+ "type": "object",
158
+ "properties": {
159
+ "notes": {
160
+ "type": "string"
161
+ }
162
+ },
163
+ "required": [
164
+ "notes"
165
+ ]
166
+ },
167
+ "bind": {
168
+ "action": "stage_notes"
169
+ },
170
+ "reaction": {
171
+ "gezel": "study-buddy",
172
+ "hideSeed": true,
173
+ "standalone": true,
174
+ "prompt": "The student pasted notes to turn into flashcards for \"{{subject}}\":\n<<<\n{{output.notes}}\n>>>\nMake 4 to 8 cards that test the key facts in these notes, one fact per card: the front a short question, the back its answer (a name, number or short fact from the notes), for example front \"What produces ATP?\", back \"Mitochondria\". Save them with add_cards.",
175
+ "turn": {
176
+ "tool": "add_cards",
177
+ "when": {
178
+ "op": "equals",
179
+ "field": "status",
180
+ "value": "forging"
181
+ }
182
+ }
183
+ }
184
+ }
185
+ ],
186
+ "craftbooks": [],
187
+ "schedules": [],
188
+ "pages": {
189
+ "entry": "review/index.html",
190
+ "api": 1,
191
+ "reads": [
192
+ {
193
+ "source": "workspace",
194
+ "path": "deck.json"
195
+ }
196
+ ],
197
+ "tools": [
198
+ "record_review",
199
+ "finish_session",
200
+ "forge_from_notes"
201
+ ]
202
+ },
203
+ "workspaceSeed": [
204
+ "deck.json"
205
+ ],
206
+ "minGezelVersion": "1.26280"
207
+ }
@@ -0,0 +1,6 @@
1
+ - A living deck on {{subject}}: a few sharp new cards whenever they're needed,
2
+ never bulk filler.
3
+ - Due cards get reviewed most days; the page's schedule is the source of truth.
4
+ - Every missed card gets another road in — fresh cards on the same idea from a
5
+ new angle, offered after the session.
6
+ - Encouragement always, shame never: a miss is the system working.