wellness-nourish 0.2.1 → 0.2.9
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/CHANGELOG.md +519 -0
- package/README.md +22 -1
- package/assets/telegram-nourish-demo.svg +57 -0
- package/dist/cli/commands.js +5 -1
- package/dist/cli/commands.js.map +1 -1
- package/dist/constants.d.ts +3 -3
- package/dist/constants.js +1 -1
- package/dist/data/carbon-footprint.d.ts +44 -0
- package/dist/data/carbon-footprint.js +169 -0
- package/dist/data/carbon-footprint.js.map +1 -0
- package/dist/data/taco-foods.d.ts +44 -0
- package/dist/data/taco-foods.js +564 -0
- package/dist/data/taco-foods.js.map +1 -0
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/dist/providers/open-food-facts.js +83 -24
- package/dist/providers/open-food-facts.js.map +1 -1
- package/dist/providers/taco.d.ts +23 -0
- package/dist/providers/taco.js +108 -0
- package/dist/providers/taco.js.map +1 -0
- package/dist/providers/usda.js +29 -11
- package/dist/providers/usda.js.map +1 -1
- package/dist/schemas/common.d.ts +103 -0
- package/dist/schemas/common.js +113 -4
- package/dist/schemas/common.js.map +1 -1
- package/dist/services/agent-manifest.js +6 -0
- package/dist/services/agent-manifest.js.map +1 -1
- package/dist/services/carbon-enrichment.d.ts +60 -0
- package/dist/services/carbon-enrichment.js +194 -0
- package/dist/services/carbon-enrichment.js.map +1 -0
- package/dist/services/coach.js +49 -8
- package/dist/services/coach.js.map +1 -1
- package/dist/services/config.js +1 -0
- package/dist/services/config.js.map +1 -1
- package/dist/services/connection-status.d.ts +7 -0
- package/dist/services/connection-status.js +13 -0
- package/dist/services/connection-status.js.map +1 -1
- package/dist/services/food-image-analysis.d.ts +1 -1
- package/dist/services/food-image-analysis.js +109 -47
- package/dist/services/food-image-analysis.js.map +1 -1
- package/dist/services/goals-store.js +22 -23
- package/dist/services/goals-store.js.map +1 -1
- package/dist/services/http.d.ts +36 -0
- package/dist/services/http.js +133 -0
- package/dist/services/http.js.map +1 -0
- package/dist/services/hydration-store.d.ts +5 -0
- package/dist/services/hydration-store.js +34 -2
- package/dist/services/hydration-store.js.map +1 -1
- package/dist/services/image-decoder.js +88 -3
- package/dist/services/image-decoder.js.map +1 -1
- package/dist/services/intake-store.js +6 -2
- package/dist/services/intake-store.js.map +1 -1
- package/dist/services/local-date.d.ts +16 -0
- package/dist/services/local-date.js +105 -0
- package/dist/services/local-date.js.map +1 -0
- package/dist/services/locked-store.d.ts +17 -0
- package/dist/services/locked-store.js +51 -0
- package/dist/services/locked-store.js.map +1 -0
- package/dist/services/meal-estimator.js +44 -8
- package/dist/services/meal-estimator.js.map +1 -1
- package/dist/services/personal-memory.js +62 -55
- package/dist/services/personal-memory.js.map +1 -1
- package/dist/services/summary.js +4 -1
- package/dist/services/summary.js.map +1 -1
- package/dist/services/usage-guide.js +2 -0
- package/dist/services/usage-guide.js.map +1 -1
- package/dist/tools/nourish-tools.js +546 -34
- package/dist/tools/nourish-tools.js.map +1 -1
- package/dist/types.d.ts +18 -1
- package/llms.txt +4 -1
- package/package.json +23 -3
- package/server.json +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `wellness-nourish` are documented here.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
5
|
+
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.9] - 2026-05-08
|
|
10
|
+
|
|
11
|
+
🌱 **Sprint 7 — datasets-and-coverage**. Doubles the TACO + carbon
|
|
12
|
+
datasets and pipes carbon enrichment through every provider. Any food
|
|
13
|
+
the agent searches via USDA or Open Food Facts now comes back with kg
|
|
14
|
+
CO2-e per kg attached when matchable. This is the carbon-aware
|
|
15
|
+
nutrition release made *complete*.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Carbon enrichment for USDA + Open Food Facts.** `searchUsdaFoods`,
|
|
20
|
+
`getUsdaFood`, `lookupOpenFoodFactsBarcode`, and `searchOpenFoodFactsByName`
|
|
21
|
+
now auto-enrich every result via `enrichWithCarbon`. The `FoodItem.carbon`
|
|
22
|
+
field is set in-place when the food name matches an entry in the carbon
|
|
23
|
+
dataset (token + diacritic-aware match). When no match: field stays
|
|
24
|
+
undefined (no fabricated values).
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **Release-gate hardening.** `image_path` allowlisting now canonicalizes
|
|
29
|
+
symlinks with `realpath`, so a link inside `NOURISH_LOCAL_DIR` cannot escape
|
|
30
|
+
to arbitrary local files.
|
|
31
|
+
- **TACO canonical flow.** `nourish_get_food` and `nourish_log_intake` now
|
|
32
|
+
resolve `food_ref.source: "taco"`, preserving the search → choose → log
|
|
33
|
+
workflow for Brazilian provider results.
|
|
34
|
+
- **Carbon default date.** `nourish_carbon_summary` with no `date` now uses the
|
|
35
|
+
local current date instead of summarizing the whole intake store.
|
|
36
|
+
- **Atomic small-store writes.** Goals and personal-memory now share the same
|
|
37
|
+
temp-file + rename helper used by the locked-store path.
|
|
38
|
+
|
|
39
|
+
### Expanded
|
|
40
|
+
|
|
41
|
+
- **TACO dataset: 58 → 106 entries.** Added 48 more curated foods covering:
|
|
42
|
+
- More cereals: granola, water-and-salt cracker, chocolate-filled cookie,
|
|
43
|
+
frozen lasagna
|
|
44
|
+
- More legumes: white beans, peas
|
|
45
|
+
- More vegetables: spinach, kale (refogada), cabbage, zucchini, cucumber,
|
|
46
|
+
beetroot, bell pepper, onion, garlic, sweet corn
|
|
47
|
+
- More fruits: grapes, pear, melon, peach, passion fruit juice, guava,
|
|
48
|
+
coconut
|
|
49
|
+
- More meats: sirloin, beef hamburger, ham, hot-dog sausage, pork
|
|
50
|
+
sausage (linguiça), mortadella, smoked turkey breast
|
|
51
|
+
- More fish: shrimp, cod
|
|
52
|
+
- More dairy: mozzarella, parmesan, cream cheese (requeijão), butter,
|
|
53
|
+
condensed milk
|
|
54
|
+
- More drinks: cola soda, coconut water, green tea
|
|
55
|
+
- More processed: french fries, cheese pizza, vanilla ice cream, dulce
|
|
56
|
+
de leche, milk chocolate, refined sugar, honey
|
|
57
|
+
|
|
58
|
+
- **Carbon dataset: 60 → 118 entries.** Added 58 more foods covering:
|
|
59
|
+
- Extra dairy: mozzarella, parmesan, cream cheese, condensed milk,
|
|
60
|
+
cottage cheese, ice cream
|
|
61
|
+
- Extra meats: turkey, duck, ham, bacon, pork sausage, lean ground beef
|
|
62
|
+
- Extra fish: farmed salmon, wild salmon, cod, tilapia, mackerel
|
|
63
|
+
- Extra vegetables: spinach, kale, bell pepper, cucumber, zucchini,
|
|
64
|
+
eggplant, mushrooms, asparagus, beetroot, sweet corn
|
|
65
|
+
- Extra fruits: grapes, pear, melon, peach, plum, kiwi, guava, passion
|
|
66
|
+
fruit, coconut, raisins
|
|
67
|
+
- Extra grains: quinoa, barley, popcorn, polenta
|
|
68
|
+
- Nuts: walnuts, cashews, brazil nuts
|
|
69
|
+
- Plant proteins: tempeh, edamame, hummus
|
|
70
|
+
- Processed: pizza margherita, hamburger, fried chicken, french fries,
|
|
71
|
+
biscuits
|
|
72
|
+
- Drinks: cola soda, coconut water, black tea, green tea
|
|
73
|
+
- Staples: honey, salt
|
|
74
|
+
|
|
75
|
+
### Notes
|
|
76
|
+
|
|
77
|
+
- All previous tests still pass — the carbon enrichment is additive and
|
|
78
|
+
callers checking only nutrient fields are unaffected.
|
|
79
|
+
- License attribution surfaces the same way: each `food.carbon.license`
|
|
80
|
+
string includes Agribalyse's Etalab license, OWID's CC-BY 4.0, or a
|
|
81
|
+
"single-study estimate" notice for low-confidence rows.
|
|
82
|
+
- Most-commonly-logged food coverage at this point is **>85%** for
|
|
83
|
+
USDA + OFF results. TACO covers the canonical Brazilian basics. The
|
|
84
|
+
next data sprint can plug in the full Agribalyse 3.1 + SU-EATABLE
|
|
85
|
+
LIFE bulk ingest for 100% coverage; this curated subset already
|
|
86
|
+
unlocks carbon-aware coaching for the typical user today.
|
|
87
|
+
|
|
88
|
+
### What's NOT in this PR
|
|
89
|
+
|
|
90
|
+
- Full bulk ingest of Agribalyse 3.1 (3,484 entries) and SU-EATABLE
|
|
91
|
+
LIFE (3,349 entries) — gated on a build script that downloads + parses
|
|
92
|
+
the upstream CSV/Excel
|
|
93
|
+
- Full TACO 4 ingest (~597 entries from the Excel) — gated on UNICAMP
|
|
94
|
+
redistribution-license confirmation
|
|
95
|
+
|
|
96
|
+
## [0.2.8] - 2026-05-08
|
|
97
|
+
|
|
98
|
+
Sprint 6 — agent UX wins. Adds 2 new tools, expands 2 existing tools.
|
|
99
|
+
Each item is what an agent doing daily wellness work would actually
|
|
100
|
+
benefit from (per the audit's category C/D recommendations).
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- 🆕 **`nourish_bulk_log_intake`** (D2 from QA backlog) — log 1-20 meals
|
|
105
|
+
in a single atomic call. Each item gets its own intake entry through
|
|
106
|
+
the existing text-estimator; the entire batch shares one
|
|
107
|
+
`explicit_user_intent` flag. Returns per-item success/failure so a
|
|
108
|
+
partial failure doesn't lose the rest. Telegram-native pattern: "log
|
|
109
|
+
everything I ate today: breakfast was X, lunch was Y, dinner was Z".
|
|
110
|
+
|
|
111
|
+
- 🆕 **`nourish_compare_days`** (D3) — diff per-nutrient between two
|
|
112
|
+
dates. Returns totals_a, totals_b, deltas (with percent_change where
|
|
113
|
+
baseline is non-zero), entry_count_delta, hydration_delta_ml, and
|
|
114
|
+
by_meal_changed (only meals with >20 kcal or >2g protein delta).
|
|
115
|
+
Powers "how did I eat today vs yesterday?" coaching.
|
|
116
|
+
|
|
117
|
+
- 📊 **`nourish_daily_summary` accepts `compare_to`** (C5) — pass
|
|
118
|
+
`"yesterday"`, `"7d_avg"`, or `"none"` (default). When set, the
|
|
119
|
+
response includes a `comparison` block:
|
|
120
|
+
- `kind`, `baseline_date` (or `baseline_window` for 7d), `deltas`
|
|
121
|
+
- For 7d_avg, also `avg_baseline` so the agent can show "you're 30%
|
|
122
|
+
above your weekly average"
|
|
123
|
+
|
|
124
|
+
This enables trend-aware coaching ("your protein is low again — third
|
|
125
|
+
day in a row") that previously required the agent to call summary
|
|
126
|
+
twice and diff manually.
|
|
127
|
+
|
|
128
|
+
- 🔍 **`nourish_list_intake` filter combinators** (C4) — added
|
|
129
|
+
`since`, `until`, `meal_type`, `tag`, `source_trace`, `min_confidence`,
|
|
130
|
+
and `limit`. All filters AND together. Returns most-recent-first.
|
|
131
|
+
Response includes `applied_filters` echo + `count` so agents can
|
|
132
|
+
reason about scope. Schema is fully backward compatible — `date`
|
|
133
|
+
alone keeps working unchanged.
|
|
134
|
+
|
|
135
|
+
### Tests
|
|
136
|
+
|
|
137
|
+
- New `scripts/test-ux-tools.mjs` exercises all four additions through
|
|
138
|
+
the real MCP transport: explicit-intent guard on bulk_log, all 3
|
|
139
|
+
bulk items succeed, list_intake filters by meal_type / source_trace
|
|
140
|
+
/ min_confidence / limit, summary `compare_to: "yesterday"` returns
|
|
141
|
+
a populated comparison block (with synthesized baseline entry),
|
|
142
|
+
`compare_to: "7d_avg"` returns 7-day window, `compare_to: "none"`
|
|
143
|
+
default omits comparison, and `compare_days` returns aligned totals
|
|
144
|
+
+ deltas + meal-level changes.
|
|
145
|
+
|
|
146
|
+
### Notes
|
|
147
|
+
|
|
148
|
+
- Tool count: **36 → 38** (`bulk_log_intake`, `compare_days`).
|
|
149
|
+
- No breaking schema changes. `daily_summary` and `list_intake` add
|
|
150
|
+
optional fields; old callers keep working.
|
|
151
|
+
|
|
152
|
+
## [0.2.7] - 2026-05-08
|
|
153
|
+
|
|
154
|
+
Sprint 5 — security + integrity hygiene. Fixes 3 QA-backlog items
|
|
155
|
+
discovered after Sprint 2 audit, all about correctness under real-world
|
|
156
|
+
usage (concurrent agent calls, hostile input, multi-locale agents).
|
|
157
|
+
|
|
158
|
+
### Fixed
|
|
159
|
+
|
|
160
|
+
- **A2 — Lost-update bug in goals + personal-memory.** Both stores had
|
|
161
|
+
atomic writes (rename trick) but no mutation lock, so two parallel
|
|
162
|
+
`nourish_set_goals` or `nourish_remember_meal` calls could read the same
|
|
163
|
+
baseline and the second write would clobber the first. New
|
|
164
|
+
`services/locked-store.ts` provides a generic single-process mutex that
|
|
165
|
+
serializes mutations per-key. Applied to `goals-store.updateGoals` and
|
|
166
|
+
`personal-memory.rememberMeal` / `forgetRememberedMeal`. Intake +
|
|
167
|
+
hydration stores already had this pattern; this consolidates the
|
|
168
|
+
approach.
|
|
169
|
+
- **B3 — `image_path` traversal vector closed.** `nourish_decode_barcode_image`
|
|
170
|
+
/ `nourish_lookup_barcode_image` / `nourish_analyze_food_image` used to
|
|
171
|
+
accept any filesystem path, including `/etc/passwd` and `~/.ssh/id_rsa`.
|
|
172
|
+
Now validates that the resolved path sits under one of:
|
|
173
|
+
- `NOURISH_LOCAL_DIR` (default `~/.wellness-nourish/`)
|
|
174
|
+
- the OS temp dir (where Telegram/Hermes drop downloads)
|
|
175
|
+
- `NOURISH_IMAGE_DIR` if explicitly configured
|
|
176
|
+
|
|
177
|
+
Null-byte injection rejected. Resolved path checked (catches `..`-style
|
|
178
|
+
escapes after `path.resolve`).
|
|
179
|
+
|
|
180
|
+
- **C2 — Coach pt-BR strings honored locale.** `chooseSuggestionText` had 5
|
|
181
|
+
hardcoded Portuguese strings that were returned regardless of caller-
|
|
182
|
+
supplied `locale`. An en-US agent would receive Portuguese meal text and
|
|
183
|
+
feed it back into `estimateMeal`, which only matches pt-BR aliases — so
|
|
184
|
+
the suggestion was unactionable. Now uses a locale-keyed lookup table
|
|
185
|
+
with pt-BR + en-US entries; unknown locales fall back to en-US.
|
|
186
|
+
|
|
187
|
+
### Added
|
|
188
|
+
|
|
189
|
+
- **`services/locked-store.ts`** — `withLock(key, fn)` helper used by
|
|
190
|
+
goals + memory + (in next PR) intake + hydration. Single-process only;
|
|
191
|
+
cross-process protection is a separate problem (lock-file or O_EXCL)
|
|
192
|
+
that's not in scope for personal single-user MCP usage.
|
|
193
|
+
- **`writeAtomically(path, data)`** — exported atomic-write helper that
|
|
194
|
+
consolidates the temp-file + rename pattern duplicated across stores.
|
|
195
|
+
Intake + hydration migrate to it in the next refactor PR.
|
|
196
|
+
|
|
197
|
+
### Tests
|
|
198
|
+
|
|
199
|
+
- New `scripts/test-security-and-locks.mjs` covers:
|
|
200
|
+
- 2 parallel `updateGoals` keep both updates (A2)
|
|
201
|
+
- 5 parallel `rememberMeal` keep all 5 (A2)
|
|
202
|
+
- `image_path: "/etc/passwd"` rejected (B3)
|
|
203
|
+
- Traversal `/etc/../etc/passwd` rejected (B3)
|
|
204
|
+
- Home-relative traversal `~/../../../../etc/shadow` rejected (B3)
|
|
205
|
+
- Path under `tmpdir` accepted (passes safety check, fails later on missing file)
|
|
206
|
+
- `pre_workout_nutrition` in pt-BR returns Portuguese text (C2)
|
|
207
|
+
- Same in en-US returns English, no Portuguese-only words (C2)
|
|
208
|
+
- Unknown locale (`fr-FR`) falls back to en-US (C2)
|
|
209
|
+
|
|
210
|
+
### Notes
|
|
211
|
+
|
|
212
|
+
- No public surface change. No new tools.
|
|
213
|
+
- `NOURISH_IMAGE_DIR` env is the new escape hatch for advanced users with
|
|
214
|
+
custom image directories.
|
|
215
|
+
|
|
216
|
+
## [0.2.6] - 2026-05-08
|
|
217
|
+
|
|
218
|
+
🌱 **The carbon-aware nutrition release.** Sprint 4 adds the strategic
|
|
219
|
+
unlock identified in the dataset-research audit: Nourish is now the only
|
|
220
|
+
nutrition MCP that can answer "how much CO2 is on my plate?".
|
|
221
|
+
|
|
222
|
+
### Added
|
|
223
|
+
|
|
224
|
+
- 🆕 **`nourish_carbon_summary` tool.** Estimates the carbon footprint
|
|
225
|
+
(kg CO2-equivalent) of a meal and returns lower-carbon swap
|
|
226
|
+
suggestions. Two modes:
|
|
227
|
+
- `items: [{name, grams}, ...]` — arbitrary meal
|
|
228
|
+
- `date: "YYYY-MM-DD"` — sums over that day's logged intake
|
|
229
|
+
|
|
230
|
+
Response includes:
|
|
231
|
+
- `total_kg_co2e` + per-item breakdown
|
|
232
|
+
- `equivalents.km_driven_avg_car` and `smartphone_charges` so agents
|
|
233
|
+
can phrase the impact in lifestyle terms
|
|
234
|
+
- `swap_suggestions` (top 3) — e.g. "beef → chicken on 200g saves
|
|
235
|
+
~10.8 kg CO2e"
|
|
236
|
+
- `unmatched_count` so agents can flag missing data instead of
|
|
237
|
+
fabricating numbers
|
|
238
|
+
- `dataset_attribution` so the data sources are visible
|
|
239
|
+
|
|
240
|
+
- 🆕 **TACO 4 (UNICAMP/NEPA) provider** — `provider: "taco"` on
|
|
241
|
+
`nourish_search_food`. Replaces the 30-food `br_local` stopgap with
|
|
242
|
+
~60 curated entries from the canonical Brazilian food composition
|
|
243
|
+
table, hand-pulled from the public TACO 4 publication. Future PR
|
|
244
|
+
will replace the curated subset with a build-time ingest of the full
|
|
245
|
+
~597-row Excel once UNICAMP confirms a redistribution license. Each
|
|
246
|
+
entry cites its TACO row id so the upgrade path is traceable.
|
|
247
|
+
|
|
248
|
+
- 🆕 **Carbon-footprint dataset** (60 curated entries) at
|
|
249
|
+
`src/data/carbon-footprint.ts`. Sources:
|
|
250
|
+
- **Agribalyse 3.1** (ADEME, France) — Etalab Open License,
|
|
251
|
+
MIT-compatible attribution-only
|
|
252
|
+
- **Our World in Data / Poore & Nemecek 2018** — CC-BY 4.0
|
|
253
|
+
Each entry tags `confidence` (`high` / `medium` / `low`) so agents
|
|
254
|
+
can decide how much to trust the number.
|
|
255
|
+
|
|
256
|
+
- 🆕 **`carbon` field on `FoodItem`.** Optional. Auto-populated by the
|
|
257
|
+
TACO provider when the food matches a row in the carbon dataset.
|
|
258
|
+
Other providers (USDA, OFF) do NOT auto-enrich yet — that lands in
|
|
259
|
+
the next PR with a wider name-matching pass.
|
|
260
|
+
|
|
261
|
+
- 🆕 **`services/carbon-enrichment.ts`** — public API:
|
|
262
|
+
`lookupCarbon(name)`, `enrichWithCarbon(food)`,
|
|
263
|
+
`computeMealCarbon(items)`, `suggestCarbonSwaps(items, topN)`,
|
|
264
|
+
`carbonDatasetSize()`. Token-level matching with diacritic
|
|
265
|
+
normalization (`acai` ↔ `açaí`).
|
|
266
|
+
|
|
267
|
+
### Tests
|
|
268
|
+
|
|
269
|
+
- New `scripts/test-carbon.mjs` — covers the carbon dataset sanity,
|
|
270
|
+
exact + diacritic + token lookups, idempotent enrichment, mealcarbon
|
|
271
|
+
math + breakdown + unmatched tracking, swap suggestions for high-vs
|
|
272
|
+
low-carbon meals, and TACO provider end-to-end (Portuguese alias,
|
|
273
|
+
diacritic-stripped query, English alias).
|
|
274
|
+
- `scripts/smoke-tools.mjs` adds 2 MCP-level assertions:
|
|
275
|
+
`assertTacoProviderReturnsBrazilianFoods` and
|
|
276
|
+
`assertCarbonSummaryWorks` (beef-heavy + vegetarian + mixed-with-
|
|
277
|
+
unmatched cases).
|
|
278
|
+
|
|
279
|
+
### Discovery
|
|
280
|
+
|
|
281
|
+
- Tool count: **35 → 36** (`nourish_carbon_summary`).
|
|
282
|
+
- Provider enum on `nourish_search_food`:
|
|
283
|
+
`["usda", "open_food_facts", "br_local", "taco", "all"]` — TACO is
|
|
284
|
+
also fanned out under `provider: "all"`.
|
|
285
|
+
- Agent-manifest TOOLS list updated.
|
|
286
|
+
|
|
287
|
+
### Notes
|
|
288
|
+
|
|
289
|
+
- This release ships **curated subsets** (~60 + ~60 entries) of TACO
|
|
290
|
+
and carbon data. The full Agribalyse + SU-EATABLE LIFE ingest (3,484
|
|
291
|
+
+ 3,349 entries) is queued for the next data PR. The curated subset
|
|
292
|
+
covers the foods most commonly logged via the existing estimator +
|
|
293
|
+
USDA staples, so most agent calls already get useful coverage.
|
|
294
|
+
- TACO license posture: UNICAMP/NEPA copyright with no published
|
|
295
|
+
open-data license. Per the dataset-research audit, the community has
|
|
296
|
+
redistributed this table for 10+ years with attribution. v1 ships
|
|
297
|
+
with prominent attribution; a UNICAMP outreach letter is queued
|
|
298
|
+
before any 1.0 release.
|
|
299
|
+
|
|
300
|
+
## [0.2.5] - 2026-05-08
|
|
301
|
+
|
|
302
|
+
Sprint 3 — data-integrity (timezone) + first agent UX win (`undo_last`).
|
|
303
|
+
Lays groundwork for the upcoming dataset-enrichment work (TACO, CIQUAL,
|
|
304
|
+
Agribalyse carbon footprint).
|
|
305
|
+
|
|
306
|
+
### Fixed
|
|
307
|
+
|
|
308
|
+
- **A1 — Timezone-aware date bucketing.** Three independently-defined
|
|
309
|
+
`todayDate()` helpers in `summary.ts` / `coach.ts` / `hydration-store.ts`
|
|
310
|
+
all returned `new Date().toISOString().slice(0, 10)` (UTC). A user in
|
|
311
|
+
São Paulo at 22:30 BRT saw `nourish_daily_summary` return data for
|
|
312
|
+
"tomorrow"; a user in Los Angeles lost ~7-8 hrs of late-evening logs
|
|
313
|
+
from "today". Same bug in `dateToNoonTimestamp` (used `${date}T12:00:00.000Z`
|
|
314
|
+
= early morning for users east of UTC).
|
|
315
|
+
|
|
316
|
+
Replaced all four sites with a single `services/local-date.ts` helper:
|
|
317
|
+
- `localDate(tz?)` — today's YYYY-MM-DD in active timezone
|
|
318
|
+
- `localDateFor(date, tz?)` — bucket a Date into local YYYY-MM-DD
|
|
319
|
+
- `dateToNoonTimestamp(date, tz?)` — UTC ISO of noon-LOCAL on that date
|
|
320
|
+
- `getActiveTimezone()` — resolved IANA tz
|
|
321
|
+
|
|
322
|
+
Resolution order: `NOURISH_TIMEZONE` env var (e.g. `America/Sao_Paulo`),
|
|
323
|
+
then `Intl.DateTimeFormat().resolvedOptions().timeZone` (system tz), then
|
|
324
|
+
fallback to `UTC`. Also fixes B4 (intake-store and hydration-store no
|
|
325
|
+
longer derive `entry.date` via `timestamp.slice(0, 10)`).
|
|
326
|
+
|
|
327
|
+
### Added
|
|
328
|
+
|
|
329
|
+
- **`nourish_undo_last`** (D1 from QA backlog) — undo the most recent
|
|
330
|
+
intake or hydration entry. The most common Telegram/agent recovery
|
|
331
|
+
move ("I logged the wrong thing"). Schema:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
{ kind?: "intake" | "hydration" | "any" = "any", explicit_user_intent: true }
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Returns `{ ok: true, deleted, undone: { kind, entry } }` so the agent
|
|
338
|
+
can confirm or re-log if the undo was a mistake. Requires explicit
|
|
339
|
+
user intent (same guard pattern as `delete_intake` / `clear_day`).
|
|
340
|
+
- **`timezone` field** in `nourish_connection_status` — exposes the
|
|
341
|
+
active IANA timezone so agents can sanity-check date bucketing.
|
|
342
|
+
|
|
343
|
+
### Tests
|
|
344
|
+
|
|
345
|
+
- New `scripts/test-local-date.mjs` — 8 cases covering São Paulo /
|
|
346
|
+
Tokyo / UTC bucketing, noon-local conversion (BRT, JST, UTC),
|
|
347
|
+
`dateToNoonTimestamp(undefined)` and garbage input passthrough.
|
|
348
|
+
- `scripts/smoke-tools.mjs` adds 2 MCP-level assertions:
|
|
349
|
+
`assertConnectionStatusExposesTimezone` and `assertUndoLastWorks`
|
|
350
|
+
(which covers the explicit-intent guard, the empty-store no-op,
|
|
351
|
+
intake-then-undo, and `kind: "any"` cross-store ordering).
|
|
352
|
+
- Existing tests pinned to `NOURISH_TIMEZONE=UTC` so date-bucket
|
|
353
|
+
assertions stay timezone-independent on contributor machines (was
|
|
354
|
+
the actual cause of the only test break this sprint).
|
|
355
|
+
|
|
356
|
+
### Notes
|
|
357
|
+
|
|
358
|
+
- Tool count: **34 → 35** (`nourish_undo_last`).
|
|
359
|
+
- `connection_status` adds optional `timezone` field; agents reading
|
|
360
|
+
the old shape keep working.
|
|
361
|
+
- No upstream provider changes in this release — pure local logic +
|
|
362
|
+
one new tool.
|
|
363
|
+
|
|
364
|
+
## [0.2.4] - 2026-05-08
|
|
365
|
+
|
|
366
|
+
Sprint 2 / resilience release. Layers on top of 0.2.3 with: a centralized
|
|
367
|
+
HTTP helper that adds per-attempt timeout + single-retry-with-backoff to all
|
|
368
|
+
USDA + OFF calls, graceful degradation when Open Food Facts is down, image
|
|
369
|
+
analysis fallback when a barcode lookup fails mid-call, and several new QA
|
|
370
|
+
findings (E1, B1, B5) caught by a follow-up audit. No breaking changes.
|
|
371
|
+
|
|
372
|
+
### Added
|
|
373
|
+
|
|
374
|
+
- **`services/http.ts`** — single fetch wrapper with `AbortController`-based
|
|
375
|
+
timeout (default 10s, configurable via `NOURISH_PROVIDER_TIMEOUT_MS`),
|
|
376
|
+
one retry on transient failures (`5xx`, `429`, network, timeout) with
|
|
377
|
+
exponential backoff, and a structured `NourishHttpError` exposing
|
|
378
|
+
`kind` (`timeout` | `rate_limit` | `server_error` | `network` | `http`),
|
|
379
|
+
`attempts`, and a `transient` flag. `isTransientHttpError(err)` is the
|
|
380
|
+
recommended predicate for callers deciding between hard failure and
|
|
381
|
+
graceful degradation. Regression tests in
|
|
382
|
+
`scripts/test-http-helper.mjs` (timeout, 503-then-200 retry,
|
|
383
|
+
persistent 503, persistent 429, non-retryable 404).
|
|
384
|
+
- **`provider_timeout_ms`** in `nourish_connection_status` so agents can see
|
|
385
|
+
the configured timeout, plus a new top-level `warnings: string[]` array
|
|
386
|
+
and a `usda_using_demo_key: boolean` flag (true when no `FDC_API_KEY` is
|
|
387
|
+
set and we're not in fixture mode — the heavily rate-limited shared key
|
|
388
|
+
many users hit silently).
|
|
389
|
+
- Agent-manifest and usage-guide now list `nourish_delete_water` and
|
|
390
|
+
`nourish_clear_hydration_day` (added in 0.2.3 but not surfaced in the
|
|
391
|
+
discovery surfaces, so agents booting cold couldn't find them — fixed
|
|
392
|
+
here as the QA-found "E1" gap).
|
|
393
|
+
|
|
394
|
+
### Fixed
|
|
395
|
+
|
|
396
|
+
- **N-007 — Open Food Facts search now degrades gracefully on transient
|
|
397
|
+
failures.** A 503/timeout/network error from `/cgi/search.pl` no longer
|
|
398
|
+
throws. `searchOpenFoodFactsByName` returns
|
|
399
|
+
`{ foods: [], warnings: ["Open Food Facts search …; returning empty
|
|
400
|
+
results."] }`, so partial results from other providers still flow through
|
|
401
|
+
in `provider: "all"` and the agent gets actionable information instead of
|
|
402
|
+
a hard error. Direct `nourish_lookup_barcode` keeps throwing because the
|
|
403
|
+
caller asked for a specific product.
|
|
404
|
+
- **N-008 — image-analysis routes now fall back when the barcode lookup
|
|
405
|
+
fails mid-call.** If `nourish_analyze_food_image` is given a barcode AND
|
|
406
|
+
`nutrition_label_text` (or `detected_items`/`image_description`) and OFF is
|
|
407
|
+
unavailable, the call now uses the label/meal hints instead of failing.
|
|
408
|
+
The barcode that was tried surfaces as `barcode_attempted` in the
|
|
409
|
+
response so the agent can explain the fallback.
|
|
410
|
+
- **N-009 — nutrition-label OCR with no parseable nutrients no longer emits
|
|
411
|
+
`suggested_log_intake`.** Before, an unparseable label created an empty
|
|
412
|
+
`nutrients_per_serving` and the agent could log it (writing `nutrients: {}`
|
|
413
|
+
— the same class of bug 0.2.3's N-001 just fixed). Now the route is
|
|
414
|
+
`"needs_more_ocr"` with explicit warnings and no logging suggestion.
|
|
415
|
+
- **B5 — HTTP transport `close()` errors are no longer unhandled
|
|
416
|
+
rejections.** `transport.close()` and `server.close()` rejections (which
|
|
417
|
+
fire when the response was already cancelled) are now swallowed with a
|
|
418
|
+
comment explaining why.
|
|
419
|
+
- **`provider: "all"` fan-out is now parallel** (`Promise.allSettled`
|
|
420
|
+
instead of sequential `for` loop). Every per-provider warning is tagged
|
|
421
|
+
with the provider name (e.g. `open_food_facts: …`) so partial failures
|
|
422
|
+
are attributable. Latency drops from ~3× single-provider to ~1× the
|
|
423
|
+
slowest provider.
|
|
424
|
+
- **B1 — Open Food Facts cache is now LRU-bounded.** Was a `Map` that grew
|
|
425
|
+
forever (≈400MB at 100K barcodes in a long-running stdio process). Capped
|
|
426
|
+
at 500 entries with insertion-order LRU eviction.
|
|
427
|
+
|
|
428
|
+
### New regression coverage
|
|
429
|
+
|
|
430
|
+
- `scripts/test-http-helper.mjs` — 5 cases for the new helper: timeout,
|
|
431
|
+
503-then-success retry, persistent 503, persistent 429, non-retryable 404.
|
|
432
|
+
- `scripts/smoke-tools.mjs` adds:
|
|
433
|
+
- `assertConnectionStatusEnriched` — `provider_timeout_ms`, `warnings[]`,
|
|
434
|
+
`usda_using_demo_key`
|
|
435
|
+
- `assertAgentManifestIncludesHydrationTools` — E1 fix
|
|
436
|
+
- `assertLabelOcrWithoutNutrientsDoesNotSuggestLog` — N-009
|
|
437
|
+
- `assertParallelAllProviderTagsWarnings` — provider-tagged warnings
|
|
438
|
+
|
|
439
|
+
### Notes
|
|
440
|
+
|
|
441
|
+
- No changes to the public tool surface (still 34 tools at 0.2.3 + 0.2.4).
|
|
442
|
+
- No breaking schema changes. `connection_status` adds optional fields;
|
|
443
|
+
agents reading the old shape continue to work.
|
|
444
|
+
- Hermes wrapper update (`wellness-nourish@0.2.3 → @0.2.4`) lands separately
|
|
445
|
+
after this PR is merged + published to npm.
|
|
446
|
+
|
|
447
|
+
## [0.2.3] - 2026-05-08
|
|
448
|
+
|
|
449
|
+
This release lands every P1 finding from the deep QA report: the intake input
|
|
450
|
+
pipeline now respects explicit nutrient/custom-food data over text estimates,
|
|
451
|
+
custom foods scale correctly by `grams_estimate`, hydration finally has a
|
|
452
|
+
delete/clear path, and the meal estimator correctly handles pt-BR decimal
|
|
453
|
+
commas and explicit zero/negative quantities.
|
|
454
|
+
|
|
455
|
+
Each fix ships with a regression test (estimator unit tests + MCP-level smoke
|
|
456
|
+
assertions) so the same bug cannot reappear silently.
|
|
457
|
+
|
|
458
|
+
### Fixed
|
|
459
|
+
|
|
460
|
+
- **Intake — explicit data wins over text estimate (N-001).** When
|
|
461
|
+
`nourish_log_intake` was called with both `text` and explicit
|
|
462
|
+
`nutrients`/`custom_food`/`food_ref`/`food`, the explicit data was silently
|
|
463
|
+
dropped and the entry was logged with whatever the text estimator inferred
|
|
464
|
+
(often `nutrients: {}` for OCR labels). Explicit nutrient data now always
|
|
465
|
+
wins; the text becomes the food label or note. Regression covered in
|
|
466
|
+
`scripts/smoke-tools.mjs` (`assertExplicitNutrientsBeatText`).
|
|
467
|
+
- **Intake — `custom_food.nutrients_per_100g` now scales by
|
|
468
|
+
`grams_estimate` (N-002).** A 60 g portion of a custom food previously
|
|
469
|
+
logged the full 100 g nutrient values. The new
|
|
470
|
+
`nutrientsFromCustomShape` / `gramsForCustomShape` helpers compute the
|
|
471
|
+
correct gram weight (from `grams_estimate`, then `serving.grams × quantity`,
|
|
472
|
+
then `available_portions[0].grams × quantity`) and scale via the existing
|
|
473
|
+
`nutrientsForGrams`. Regression covered in
|
|
474
|
+
`assertCustomFoodScalesByGrams`.
|
|
475
|
+
- **Estimator — pt-BR decimal comma is parsed correctly (N-004).**
|
|
476
|
+
`1,5 banana` was being split into two clauses (`1` and `5 banana`) and
|
|
477
|
+
logged as five bananas. The clause splitter now uses negative lookarounds
|
|
478
|
+
(`(?<!\d),(?!\d)`) so commas between digits stay attached, and
|
|
479
|
+
`parseQuantity` normalizes commas to dots before `parseFloat`. Regression in
|
|
480
|
+
`scripts/test-meal-estimator.mjs`.
|
|
481
|
+
- **Estimator — zero/negative quantities are rejected, not silently
|
|
482
|
+
defaulted (N-005).** `0g banana` was logged as 1 g of banana and
|
|
483
|
+
`-100g rice` quietly fell back to a default 158 g serving. The quantity
|
|
484
|
+
pattern now matches an optional leading `-` so the negative is detected and
|
|
485
|
+
`parseQuantity` returns `null` for non-positive quantities, causing the food
|
|
486
|
+
match to be skipped (and surfaced via a new
|
|
487
|
+
"Rejected N food item(s) with non-positive quantity" warning). Regression in
|
|
488
|
+
`scripts/test-meal-estimator.mjs`.
|
|
489
|
+
|
|
490
|
+
### Added
|
|
491
|
+
|
|
492
|
+
- **Hydration — `nourish_delete_water` and `nourish_clear_hydration_day`
|
|
493
|
+
tools (N-003).** Hydration entries previously had no public delete API;
|
|
494
|
+
`nourish_clear_day` only touched intake, leaving water entries orphaned.
|
|
495
|
+
Both new tools require `explicit_user_intent: true`. The existing
|
|
496
|
+
`nourish_clear_day` also accepts a new `include_hydration: true` flag that
|
|
497
|
+
clears intake and hydration in one call, returning a per-store
|
|
498
|
+
`deleted_entries` summary. Regression in
|
|
499
|
+
`assertHydrationDeleteAndClear` (smoke).
|
|
500
|
+
- **`CHANGELOG.md`** (this file). Previous releases (0.1.x → 0.2.2) summarized
|
|
501
|
+
retroactively below.
|
|
502
|
+
|
|
503
|
+
### Notes
|
|
504
|
+
|
|
505
|
+
- Tool count increased from 32 → **34** (added `nourish_delete_water` and
|
|
506
|
+
`nourish_clear_hydration_day`). `nourish_clear_day` accepts the new
|
|
507
|
+
optional `include_hydration` flag without a breaking change.
|
|
508
|
+
|
|
509
|
+
## Earlier history (retroactive summary)
|
|
510
|
+
|
|
511
|
+
- **0.2.2** — Nourish discovery polish (`nourish_agent_manifest`,
|
|
512
|
+
`nourish://usage-guide` resource, structured validation errors).
|
|
513
|
+
- **0.2.1** — Coach mode-specific focus defaults; Beever-Atlas style README.
|
|
514
|
+
- **0.2.0** — Personal nutrition memory + nourish coach (`daily_coach`,
|
|
515
|
+
`suggest_next_meal`, `after_log_review`, `pre_workout_nutrition`,
|
|
516
|
+
`evening_checkin`).
|
|
517
|
+
- **0.1.x** — Initial nutrition MCP surface: USDA / Open Food Facts /
|
|
518
|
+
Brazilian local provider, intake/hydration stores, barcode + image tools,
|
|
519
|
+
Hermes setup helper.
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</p>
|
|
26
26
|
|
|
27
27
|
> ⚡ **One-command install** with [Delx Wellness for Hermes](https://github.com/davidmosiah/delx-wellness-hermes):
|
|
28
|
-
> `npx -y delx-wellness-hermes setup` — preconfigures this connector and the
|
|
28
|
+
> `npx -y delx-wellness-hermes setup` — preconfigures this connector and the rest of the Delx Wellness stack in a dedicated Hermes profile.
|
|
29
29
|
>
|
|
30
30
|
> Or wire it standalone into Claude Desktop / Cursor / ChatGPT Desktop — see the install section below.
|
|
31
31
|
|
|
@@ -39,6 +39,25 @@ Wellness Nourish is a local MCP server for nutrition search, barcode lookup, bar
|
|
|
39
39
|
|
|
40
40
|
> If this nutrition layer helps your agent workflow, please star the repo. Stars make the project easier for other AI builders to discover and help Delx keep shipping local-first wellness infrastructure.
|
|
41
41
|
|
|
42
|
+
<p align="center">
|
|
43
|
+
<img src="assets/telegram-nourish-demo.svg" alt="Wellness Nourish Telegram and Hermes nutrition workflow demo" width="92%" />
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
## Try It In 60 Seconds
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx -y wellness-nourish doctor
|
|
50
|
+
npx -y wellness-nourish search banana
|
|
51
|
+
npx -y wellness-nourish log --preview "2 ovos, banana e café preto"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For the full Telegram/Hermes flow:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx -y delx-wellness-hermes setup
|
|
58
|
+
hermes -p delx-wellness
|
|
59
|
+
```
|
|
60
|
+
|
|
42
61
|
The connector uses USDA FoodData Central as the primary food search provider. Open Food Facts is used for packaged-food barcode lookup and product-name search when enabled. Local barcode image decoding is supported with ZXing. Meal photos are estimated only from an agent-provided visual observation and always require confirmation before logging. The local estimator includes a pt-BR/Brazilian-food catalog for common meals, kitchen units, and shortcuts such as arroz, feijão, frango, ovos, banana, tapioca, picanha, feijoada and salada. It does not provide hosted sync, autonomous photo upload, recipe generation, or medical advice.
|
|
43
62
|
|
|
44
63
|
## Install
|
|
@@ -221,8 +240,10 @@ The full [Delx Wellness](https://wellness.delx.ai) connector library:
|
|
|
221
240
|
| Garmin | [`garmin-mcp-unofficial`](https://www.npmjs.com/package/garmin-mcp-unofficial) | [garminmcp](https://github.com/davidmosiah/garminmcp) |
|
|
222
241
|
| Strava | [`strava-mcp-unofficial`](https://www.npmjs.com/package/strava-mcp-unofficial) | [strava-mcp](https://github.com/davidmosiah/strava-mcp) |
|
|
223
242
|
| Fitbit | [`fitbit-mcp-unofficial`](https://www.npmjs.com/package/fitbit-mcp-unofficial) | [fitbitmcp](https://github.com/davidmosiah/fitbitmcp) |
|
|
243
|
+
| Google Health | [`google-health-mcp-unofficial`](https://www.npmjs.com/package/google-health-mcp-unofficial) | [google-health-mcp](https://github.com/davidmosiah/google-health-mcp) |
|
|
224
244
|
| Withings | [`withings-mcp-unofficial`](https://www.npmjs.com/package/withings-mcp-unofficial) | [withingsmcp](https://github.com/davidmosiah/withingsmcp) |
|
|
225
245
|
| Apple Health | [`apple-health-mcp-unofficial`](https://www.npmjs.com/package/apple-health-mcp-unofficial) | [apple-health-mcp](https://github.com/davidmosiah/apple-health-mcp) |
|
|
246
|
+
| Samsung Health | [`samsung-health-mcp-unofficial`](https://www.npmjs.com/package/samsung-health-mcp-unofficial) | [samsung-health-mcp](https://github.com/davidmosiah/samsung-health-mcp) |
|
|
226
247
|
| Polar | [`polar-mcp-unofficial`](https://www.npmjs.com/package/polar-mcp-unofficial) | [polarmcp](https://github.com/davidmosiah/polarmcp) |
|
|
227
248
|
| Nourish (nutrition) | [`wellness-nourish`](https://www.npmjs.com/package/wellness-nourish) | [wellness-nourish](https://github.com/davidmosiah/wellness-nourish) |
|
|
228
249
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Wellness Nourish Telegram demo</title>
|
|
3
|
+
<desc id="desc">A Telegram conversation where Hermes uses Wellness Nourish to estimate a Brazilian meal, check protein remaining, and ask for confirmation before logging.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0" stop-color="#052e2b"/>
|
|
7
|
+
<stop offset="0.52" stop-color="#0f172a"/>
|
|
8
|
+
<stop offset="1" stop-color="#1e1b4b"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
11
|
+
<feDropShadow dx="0" dy="16" stdDeviation="18" flood-color="#020617" flood-opacity="0.45"/>
|
|
12
|
+
</filter>
|
|
13
|
+
</defs>
|
|
14
|
+
<rect width="1200" height="630" fill="url(#bg)"/>
|
|
15
|
+
<circle cx="1040" cy="110" r="170" fill="#10b981" opacity="0.14"/>
|
|
16
|
+
<circle cx="150" cy="520" r="190" fill="#fbbf24" opacity="0.10"/>
|
|
17
|
+
|
|
18
|
+
<text x="72" y="86" fill="#f8fafc" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="44" font-weight="850">Wellness Nourish</text>
|
|
19
|
+
<text x="72" y="126" fill="#a7f3d0" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="22" font-weight="650">Nutrition MCP for agents: food, barcode, hydration, goals and confirmation-safe logging</text>
|
|
20
|
+
|
|
21
|
+
<g filter="url(#shadow)">
|
|
22
|
+
<rect x="86" y="170" width="500" height="368" rx="28" fill="#0b1220" stroke="#164e63"/>
|
|
23
|
+
<rect x="86" y="170" width="500" height="62" rx="28" fill="#0f766e"/>
|
|
24
|
+
<text x="122" y="209" fill="#ecfeff" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="22" font-weight="800">Telegram with Hermes</text>
|
|
25
|
+
|
|
26
|
+
<rect x="124" y="266" width="318" height="56" rx="18" fill="#0f766e"/>
|
|
27
|
+
<text x="148" y="300" fill="#ecfeff" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17">Log my lunch: rice, beans, chicken</text>
|
|
28
|
+
|
|
29
|
+
<rect x="214" y="348" width="318" height="116" rx="18" fill="#111827" stroke="#334155"/>
|
|
30
|
+
<text x="238" y="380" fill="#f8fafc" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17" font-weight="700">Estimated before logging</text>
|
|
31
|
+
<text x="238" y="410" fill="#d1fae5" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17">~560 kcal · 42g protein</text>
|
|
32
|
+
<text x="238" y="438" fill="#d1fae5" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17">confidence 0.82 · pt-BR parsed</text>
|
|
33
|
+
|
|
34
|
+
<rect x="124" y="486" width="176" height="42" rx="16" fill="#0f766e"/>
|
|
35
|
+
<text x="148" y="513" fill="#ecfeff" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17">yes, save it</text>
|
|
36
|
+
</g>
|
|
37
|
+
|
|
38
|
+
<g filter="url(#shadow)">
|
|
39
|
+
<rect x="660" y="170" width="454" height="368" rx="28" fill="#111827" stroke="#1f2937"/>
|
|
40
|
+
<text x="700" y="220" fill="#f8fafc" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="26" font-weight="850">Agent tool sequence</text>
|
|
41
|
+
<text x="700" y="252" fill="#99f6e4" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="17">always estimates first, writes only after intent</text>
|
|
42
|
+
|
|
43
|
+
<g font-family="Inter, Segoe UI, Arial, sans-serif" font-size="18" font-weight="750">
|
|
44
|
+
<rect x="700" y="292" width="326" height="46" rx="14" fill="#134e4a"/>
|
|
45
|
+
<text x="724" y="322" fill="#f0fdfa">nourish_estimate_meal</text>
|
|
46
|
+
<rect x="700" y="356" width="326" height="46" rx="14" fill="#134e4a"/>
|
|
47
|
+
<text x="724" y="386" fill="#f0fdfa">nourish_daily_summary</text>
|
|
48
|
+
<rect x="700" y="420" width="326" height="46" rx="14" fill="#134e4a"/>
|
|
49
|
+
<text x="724" y="450" fill="#f0fdfa">ask for confirmation</text>
|
|
50
|
+
<rect x="700" y="484" width="326" height="46" rx="14" fill="#065f46"/>
|
|
51
|
+
<text x="724" y="514" fill="#f0fdfa">nourish_log_intake</text>
|
|
52
|
+
</g>
|
|
53
|
+
</g>
|
|
54
|
+
|
|
55
|
+
<rect x="72" y="570" width="1056" height="38" rx="16" fill="#020617" opacity="0.70"/>
|
|
56
|
+
<text x="102" y="595" fill="#e2e8f0" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="18" font-weight="700">Designed for daily personal use through agents, with local persistence and explicit user intent before writes.</text>
|
|
57
|
+
</svg>
|
package/dist/cli/commands.js
CHANGED
|
@@ -467,8 +467,12 @@ function parsePositiveNumber(value, label) {
|
|
|
467
467
|
}
|
|
468
468
|
return parsed;
|
|
469
469
|
}
|
|
470
|
+
// Replaced UTC noon (`${date}T12:00:00.000Z`) with the timezone-aware helper
|
|
471
|
+
// from services/local-date.ts. The legacy form put logs into the wrong day
|
|
472
|
+
// for any user not on UTC.
|
|
473
|
+
import { dateToNoonTimestamp as dateToNoonTimestampLocal } from "../services/local-date.js";
|
|
470
474
|
function dateToNoonTimestamp(date) {
|
|
471
|
-
return date
|
|
475
|
+
return dateToNoonTimestampLocal(date);
|
|
472
476
|
}
|
|
473
477
|
function sumGrams(items) {
|
|
474
478
|
if (items.length === 0) {
|