@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,8 @@
1
+ Training toward **{{goal}}**, {{weeklyTarget}} session(s) a week. Log a session on the
2
+ **Output** tab, or just tell your Coach about it in the chat. The Coach celebrates the
3
+ streak and keeps the plan sized to the life you actually have: smaller when the week
4
+ is hard, growing again when it isn't.
5
+
6
+ The Output tab is the training board: the streak, this week against the target, the
7
+ recent sessions, and the weekly recap. The recap's numbers come straight from your
8
+ log; the Coach adds one suggestion for the week ahead.
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "Weekly training recap",
3
+ "description": "This week's training in numbers against last week, plus one suggestion for next week. The numbers come from the training log; the coach adds the suggestion.",
4
+ "steps": [
5
+ {
6
+ "id": "recap",
7
+ "name": "Write the weekly recap",
8
+ "prompt": "Write this week's training recap. First call training_recap for the week's numbers: sessions, minutes, and the change from last week. Then call save_recap once with ONE concrete suggestion for next week, in one sentence, grounded in those numbers. Do not write the recap file yourself; save_recap writes it.",
9
+ "suggestedRole": "coach",
10
+ "advanceWhen": {
11
+ "file": "recaps/latest.json",
12
+ "sniff": "json-valid"
13
+ },
14
+ "gate": {
15
+ "at": "completion",
16
+ "checks": [
17
+ {
18
+ "kind": "sniff",
19
+ "file": "recaps/latest.json",
20
+ "sniff": "json-valid"
21
+ },
22
+ {
23
+ "kind": "contains",
24
+ "file": "recaps/latest.json",
25
+ "pattern": "\"suggestion\":\\s*\"[^\"]{10,}",
26
+ "label": "a suggestion for next week"
27
+ }
28
+ ],
29
+ "onReject": "recap",
30
+ "maxAttempts": 3
31
+ }
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,229 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "version": "1.1.0",
4
+ "releasedAt": "2026-10-07T00:00:00Z",
5
+ "meesterManaged": false,
6
+ "leanProfile": true,
7
+ "params": {
8
+ "type": "object",
9
+ "properties": {
10
+ "goal": {
11
+ "type": "string",
12
+ "title": "What are you training toward?",
13
+ "description": "e.g. 'feel strong again', 'a 10k in October'.",
14
+ "default": "feeling strong and consistent"
15
+ },
16
+ "weeklyTarget": {
17
+ "type": "number",
18
+ "title": "Sessions per week",
19
+ "description": "How many training sessions a week to aim for.",
20
+ "default": 3
21
+ }
22
+ },
23
+ "required": [
24
+ "goal",
25
+ "weeklyTarget"
26
+ ]
27
+ },
28
+ "nameTemplate": "Fitness: {{goal}}",
29
+ "aboutTemplate": "about.md",
30
+ "missionTemplate": "mission.md",
31
+ "gezels": [
32
+ {
33
+ "templateId": "coach",
34
+ "voorman": true
35
+ }
36
+ ],
37
+ "tools": [
38
+ {
39
+ "name": "log_workout",
40
+ "description": "Log a training session the moment it is reported (activity + minutes).",
41
+ "script": "training-store",
42
+ "inputs": {
43
+ "type": "object",
44
+ "properties": {
45
+ "activity": {
46
+ "type": "string"
47
+ },
48
+ "minutes": {
49
+ "type": "number"
50
+ },
51
+ "note": {
52
+ "type": "string"
53
+ }
54
+ },
55
+ "required": [
56
+ "activity"
57
+ ]
58
+ },
59
+ "bind": {
60
+ "action": "log_workout"
61
+ }
62
+ },
63
+ {
64
+ "name": "set_target",
65
+ "description": "Change the weekly session target.",
66
+ "script": "training-store",
67
+ "inputs": {
68
+ "type": "object",
69
+ "properties": {
70
+ "weeklyTarget": {
71
+ "type": "number"
72
+ }
73
+ },
74
+ "required": [
75
+ "weeklyTarget"
76
+ ]
77
+ },
78
+ "bind": {
79
+ "action": "set_target"
80
+ }
81
+ },
82
+ {
83
+ "name": "training_status",
84
+ "description": "Streak, this week against the target, all-time count.",
85
+ "script": "training-store",
86
+ "inputs": {
87
+ "type": "object",
88
+ "properties": {}
89
+ },
90
+ "bind": {
91
+ "action": "status"
92
+ }
93
+ },
94
+ {
95
+ "name": "workout_history",
96
+ "description": "The recent sessions, most recent last.",
97
+ "script": "training-store",
98
+ "inputs": {
99
+ "type": "object",
100
+ "properties": {}
101
+ },
102
+ "bind": {
103
+ "action": "history"
104
+ }
105
+ },
106
+ {
107
+ "name": "training_recap",
108
+ "description": "This week's training against last week: sessions, minutes, and the longest session.",
109
+ "script": "training-store",
110
+ "inputs": {
111
+ "type": "object",
112
+ "properties": {}
113
+ },
114
+ "bind": {
115
+ "action": "recap_numbers"
116
+ }
117
+ },
118
+ {
119
+ "name": "save_recap",
120
+ "description": "Save the weekly recap with ONE concrete suggestion for next week, in one sentence, grounded in this week's numbers. The app adds the numbers.",
121
+ "script": "training-store",
122
+ "inputs": {
123
+ "type": "object",
124
+ "properties": {
125
+ "suggestion": {
126
+ "type": "string",
127
+ "description": "One sentence for next week."
128
+ }
129
+ },
130
+ "required": [
131
+ "suggestion"
132
+ ]
133
+ },
134
+ "bind": {
135
+ "action": "save_recap"
136
+ },
137
+ "turn": {
138
+ "show": "display",
139
+ "fallback": "Recap saved."
140
+ }
141
+ },
142
+ {
143
+ "name": "page_log_workout",
144
+ "description": "Log a session from the dashboard.",
145
+ "script": "training-store",
146
+ "inputs": {
147
+ "type": "object",
148
+ "properties": {
149
+ "activity": {
150
+ "type": "string"
151
+ },
152
+ "minutes": {
153
+ "type": "number"
154
+ },
155
+ "note": {
156
+ "type": "string"
157
+ }
158
+ },
159
+ "required": [
160
+ "activity"
161
+ ]
162
+ },
163
+ "bind": {
164
+ "action": "log_workout"
165
+ }
166
+ },
167
+ {
168
+ "name": "request_recap",
169
+ "description": "Ask the coach for this week's recap from the dashboard.",
170
+ "script": "training-store",
171
+ "inputs": {
172
+ "type": "object",
173
+ "properties": {}
174
+ },
175
+ "bind": {
176
+ "action": "request_recap"
177
+ },
178
+ "reaction": {
179
+ "gezel": "coach",
180
+ "hideSeed": true,
181
+ "standalone": true,
182
+ "prompt": "The person asked for this week's training recap. The numbers, from their log:\n```json\n{{output.recap}}\n```\nCall save_recap once with ONE concrete suggestion for next week, in one sentence, grounded in these numbers. Their goal: {{goal}}.",
183
+ "turn": {
184
+ "tool": "save_recap",
185
+ "when": {
186
+ "op": "equals",
187
+ "field": "status",
188
+ "value": "recap_wanted"
189
+ }
190
+ }
191
+ }
192
+ }
193
+ ],
194
+ "craftbooks": [
195
+ "training-recap"
196
+ ],
197
+ "schedules": [
198
+ {
199
+ "cron": "0 18 * * 0",
200
+ "craftbook": "training-recap",
201
+ "consent": "ask"
202
+ }
203
+ ],
204
+ "pages": {
205
+ "entry": "dashboard/index.html",
206
+ "api": 1,
207
+ "reads": [
208
+ {
209
+ "source": "workspace",
210
+ "path": "training.json"
211
+ },
212
+ {
213
+ "source": "workspace",
214
+ "path": "recaps/latest.json"
215
+ }
216
+ ],
217
+ "tools": [
218
+ "page_log_workout",
219
+ "request_recap"
220
+ ]
221
+ },
222
+ "workspaceSeed": [
223
+ "training.json"
224
+ ],
225
+ "scripts": {
226
+ "training-store": "import { type InferredInput, defineScript, gezel } from '@bendyline/gezel-sdk';\nimport { logStore } from '@bendyline/gezel-sdk/stores';\n\nexport const meta = defineScript({\n name: 'training-store',\n description:\n \"Read and update the training log (training.json). Call action:'status' for streak + this week vs the target; 'log_workout' the moment a session is reported (activity + minutes); 'set_target' to change the weekly session target; 'history' for the recent sessions; 'recap_numbers' for this week against last; 'save_recap' to write the weekly recap with one suggestion.\",\n inputs: {\n action: {\n type: 'string',\n description:\n \"One of 'status', 'log_workout', 'set_target', 'history', 'recap_numbers', 'request_recap', or 'save_recap'.\",\n required: true,\n },\n activity: { type: 'string', description: \"What they did, e.g. 'run', 'gym', 'long walk'.\" },\n minutes: { type: 'number', description: 'How long the session ran, in minutes.' },\n note: { type: 'string', description: 'Anything worth remembering about the session.' },\n weeklyTarget: { type: 'number', description: 'Sessions per week to aim for (set_target).' },\n suggestion: { type: 'string', description: 'For save_recap: one concrete suggestion for next week.' },\n },\n outputs: {\n streakDays: { type: 'number', description: 'Consecutive days with a session, ending at the latest.' },\n thisWeek: { type: 'number', description: 'Sessions logged this week (Monday UTC start).' },\n weeklyTarget: { type: 'number', description: 'The current weekly session target.' },\n summary: { type: 'string', description: 'The training picture.' },\n personalBest: { type: 'boolean', description: 'For log_workout: the longest session on record.' },\n recap: { type: 'json', description: 'This week against last: sessions, minutes, longest session.' },\n display: { type: 'string', description: 'For save_recap: the recap as shown.' },\n status: { type: 'string', description: \"'recap_wanted' after request_recap, else 'ok'.\" },\n },\n requires: ['workspace.read', 'workspace.write'],\n} as const);\n\nconst input = gezel.input as InferredInput<typeof meta>;\n\n// ── fitness-rules-start ──\n// Pure derivations over logStore-shaped workout events: kind 'workout',\n// data {activity, minutes}. Weeks start Monday 00:00 UTC; nowIso is a\n// parameter so the rules stay clock-independent and testable.\n\nfunction weekStartMs(nowIso) {\n var day = new Date(nowIso.slice(0, 10) + 'T00:00:00Z');\n var sinceMonday = (day.getUTCDay() + 6) % 7;\n return day.getTime() - sinceMonday * 86400000;\n}\n\nfunction thisWeekCount(events, nowIso) {\n var start = weekStartMs(nowIso);\n return events.filter(function (e) {\n return Date.parse(e.at) >= start;\n }).length;\n}\n\nfunction trainingLine(stats, weekCount, weeklyTarget) {\n var target = weeklyTarget > 0 ? weeklyTarget : 0;\n var weekPart =\n 'This week: ' +\n weekCount +\n (target > 0 ? ' of ' + target + ' session(s)' : ' session(s)') +\n (target > 0 && weekCount >= target ? ' — target met.' : '.');\n var streakPart =\n stats.total === 0\n ? 'No sessions logged yet.'\n : 'Streak: ' + stats.streakDays + ' day(s); ' + stats.total + ' session(s) all time.';\n return weekPart + ' ' + streakPart;\n}\n\n// The week's numbers against the week before: what a recap reports, so the\n// coach only adds a suggestion. Weeks start Monday UTC, as above.\nfunction weekRecap(events, nowIso, weeklyTarget) {\n var start = weekStartMs(nowIso);\n var prevStart = start - 7 * 86400000;\n function minutesOf(e) {\n var m = e.data && Number(e.data.minutes);\n return isFinite(m) && m > 0 ? m : 0;\n }\n var week = events.filter(function (e) { return Date.parse(e.at) >= start; });\n var prev = events.filter(function (e) {\n var t = Date.parse(e.at);\n return t >= prevStart && t < start;\n });\n var minutes = week.reduce(function (n, e) { return n + minutesOf(e); }, 0);\n var prevMinutes = prev.reduce(function (n, e) { return n + minutesOf(e); }, 0);\n var longest = week.reduce(function (best, e) {\n return !best || minutesOf(e) > minutesOf(best) ? e : best;\n }, null);\n return {\n weekOf: new Date(start).toISOString().slice(0, 10),\n sessions: week.length,\n target: weeklyTarget,\n minutes: minutes,\n sessionsChange: week.length - prev.length,\n minutesChange: minutes - prevMinutes,\n longest: longest\n ? { activity: String((longest.data && longest.data.activity) || 'session'), minutes: minutesOf(longest) }\n : null,\n };\n}\n\nfunction recapLine(recap, suggestion) {\n var change = recap.minutesChange === 0 ? 'same as last week' : (recap.minutesChange > 0 ? '+' : '') + recap.minutesChange + ' min vs last week';\n return (\n 'Week of ' + recap.weekOf + ': ' + recap.sessions + (recap.target > 0 ? ' of ' + recap.target : '') +\n ' session(s), ' + recap.minutes + ' min (' + change + ').' +\n (suggestion ? ' Next week: ' + suggestion : '')\n );\n}\n// ── fitness-rules-end ──\n\nconst training = logStore(gezel.fs, 'training.json');\n\nasync function readWeeklyTarget(): Promise<number> {\n try {\n const parsed = JSON.parse(await gezel.fs.read('training.json')) as { weeklyTarget?: unknown };\n const target = Number(parsed.weeklyTarget);\n return Number.isFinite(target) && target > 0 ? Math.round(target) : 0;\n } catch {\n return 0;\n }\n}\n\nlet personalBest = false;\nlet display = '';\nlet status = 'ok';\nif (input.action === 'log_workout') {\n if (!input.activity) throw new Error(\"log_workout needs 'activity' (what they did).\");\n const minutes = typeof input.minutes === 'number' && input.minutes > 0 ? Math.round(input.minutes) : 0;\n const before = await training.list();\n const longestBefore = before.reduce((n, e) => Math.max(n, Number(e.data?.minutes) || 0), 0);\n personalBest = minutes > 0 && before.length > 0 && minutes > longestBefore;\n await training.append({\n kind: 'workout',\n ...(input.note ? { note: input.note } : {}),\n data: {\n activity: input.activity,\n ...(typeof input.minutes === 'number' && input.minutes > 0\n ? { minutes: Math.round(input.minutes) }\n : {}),\n },\n });\n} else if (input.action === 'set_target') {\n const target = Number(input.weeklyTarget);\n if (!Number.isFinite(target) || target <= 0) {\n throw new Error(\"set_target needs a positive 'weeklyTarget' (sessions per week).\");\n }\n // logStore has no field API; the passenger key flips via a targeted\n // read-modify-write that preserves the rest of the file.\n const raw = JSON.parse(await gezel.fs.read('training.json')) as Record<string, unknown>;\n raw.weeklyTarget = Math.round(target);\n await gezel.fs.write('training.json', `${JSON.stringify(raw, null, 2)}\\n`);\n} else if (input.action === 'request_recap') {\n status = 'recap_wanted';\n} else if (input.action === 'save_recap') {\n const suggestion = String(input.suggestion ?? '').replace(/\\s+/g, ' ').trim().slice(0, 240);\n if (suggestion.length < 10) throw new Error(\"save_recap needs a one-sentence 'suggestion' for next week.\");\n const recap = weekRecap(await training.list(), new Date().toISOString(), await readWeeklyTarget());\n const doc = { ...recap, suggestion, writtenAt: new Date().toISOString() };\n const body = `${JSON.stringify(doc, null, 2)}\\n`;\n await gezel.fs.write(`recaps/${recap.weekOf}.json`, body);\n await gezel.fs.write('recaps/latest.json', body);\n display = recapLine(recap, suggestion);\n} else if (!['status', 'history', 'recap_numbers'].includes(String(input.action))) {\n throw new Error(\n `Unknown action '${input.action}'. Use status, log_workout, set_target, history, recap_numbers, or save_recap.`,\n );\n}\n\nconst events = await training.list();\nconst stats = await training.stats();\nconst weeklyTarget = await readWeeklyTarget();\nconst nowIso = new Date().toISOString();\nconst weekCount = thisWeekCount(events, nowIso);\nconst listing =\n input.action === 'history'\n ? events\n .slice(-12)\n .map(\n (e) =>\n `${e.at.slice(0, 10)} ${String(e.data?.activity ?? 'session')}${typeof e.data?.minutes === 'number' ? ` (${e.data.minutes} min)` : ''}${e.note ? ` — ${e.note}` : ''}`,\n )\n .join('\\n') || 'No sessions yet.'\n : '';\n\nconst recap = weekRecap(events, nowIso, weeklyTarget);\ngezel.output({\n streakDays: stats.streakDays,\n thisWeek: weekCount,\n weeklyTarget,\n summary:\n listing ||\n (input.action === 'recap_numbers' || input.action === 'request_recap'\n ? recapLine(recap, '')\n : trainingLine(stats, weekCount, weeklyTarget)),\n personalBest,\n recap,\n display,\n status,\n});\n"
227
+ },
228
+ "minGezelVersion": "1.26280"
229
+ }
@@ -0,0 +1,7 @@
1
+ - Build a training rhythm that holds: {{weeklyTarget}} session(s) a week toward
2
+ {{goal}}.
3
+ - Every session gets logged the day it happens — the streak and the weekly
4
+ count come from the record, never from memory.
5
+ - Consistency over intensity: a shrunk plan that survives a hard week beats an
6
+ ambitious one that dies in it.
7
+ - Encouragement always, guilt never.