weather-for-grown-ups 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/ARCHITECTURE.md +295 -0
  2. package/AREA_DISTRIBUTION.md +66 -0
  3. package/AREA_SUMMARY.md +88 -0
  4. package/CATALOG_SEARCH.md +61 -0
  5. package/DIAGNOSTIC_TIME_SERIES.md +146 -0
  6. package/GEFS_DIAGNOSTIC_TIME_SERIES.md +139 -0
  7. package/GEFS_ENSEMBLE.md +241 -0
  8. package/GEFS_FIELD_BUNDLES.md +161 -0
  9. package/GEFS_MULTI_POINT.md +121 -0
  10. package/GEFS_MULTI_POINT_TIME_SERIES.md +113 -0
  11. package/GEFS_PROFILE_DIAGNOSTICS.md +107 -0
  12. package/GEFS_RUN_COMPARISON.md +117 -0
  13. package/GEFS_TRANSECT.md +64 -0
  14. package/GFS_GEFS_COMPARISON.md +104 -0
  15. package/INSTALL.md +156 -0
  16. package/LICENSE +21 -0
  17. package/LIVE_SMOKE.md +165 -0
  18. package/METEOROLOGY_VALIDATION.md +80 -0
  19. package/README.md +468 -0
  20. package/RUN_COMPARISON.md +91 -0
  21. package/TESTING.md +68 -0
  22. package/TRANSECT.md +61 -0
  23. package/dist/cache/file-rate-limiter.d.ts +13 -0
  24. package/dist/cache/file-rate-limiter.js +73 -0
  25. package/dist/cache/file-rate-limiter.js.map +1 -0
  26. package/dist/cache/gefs-s3-subset-cache.d.ts +40 -0
  27. package/dist/cache/gefs-s3-subset-cache.js +146 -0
  28. package/dist/cache/gefs-s3-subset-cache.js.map +1 -0
  29. package/dist/cache/nomads-cache.d.ts +11 -0
  30. package/dist/cache/nomads-cache.js +47 -0
  31. package/dist/cache/nomads-cache.js.map +1 -0
  32. package/dist/cache/s3-subset-cache.d.ts +11 -0
  33. package/dist/cache/s3-subset-cache.js +112 -0
  34. package/dist/cache/s3-subset-cache.js.map +1 -0
  35. package/dist/catalog/catalog.d.ts +98 -0
  36. package/dist/catalog/catalog.js +81 -0
  37. package/dist/catalog/catalog.js.map +1 -0
  38. package/dist/catalog/gefs-catalog.d.ts +84 -0
  39. package/dist/catalog/gefs-catalog.js +112 -0
  40. package/dist/catalog/gefs-catalog.js.map +1 -0
  41. package/dist/catalog/gefs-fields.d.ts +32 -0
  42. package/dist/catalog/gefs-fields.js +75 -0
  43. package/dist/catalog/gefs-fields.js.map +1 -0
  44. package/dist/catalog/gefs-search.d.ts +2 -0
  45. package/dist/catalog/gefs-search.js +189 -0
  46. package/dist/catalog/gefs-search.js.map +1 -0
  47. package/dist/catalog/gefs.d.ts +16 -0
  48. package/dist/catalog/gefs.js +70 -0
  49. package/dist/catalog/gefs.js.map +1 -0
  50. package/dist/catalog/layer-diagnostics.d.ts +14 -0
  51. package/dist/catalog/layer-diagnostics.js +49 -0
  52. package/dist/catalog/layer-diagnostics.js.map +1 -0
  53. package/dist/catalog/models.d.ts +16 -0
  54. package/dist/catalog/models.js +66 -0
  55. package/dist/catalog/models.js.map +1 -0
  56. package/dist/catalog/non-isobaric-fields.d.ts +57 -0
  57. package/dist/catalog/non-isobaric-fields.js +230 -0
  58. package/dist/catalog/non-isobaric-fields.js.map +1 -0
  59. package/dist/catalog/parcel-diagnostics.d.ts +14 -0
  60. package/dist/catalog/parcel-diagnostics.js +47 -0
  61. package/dist/catalog/parcel-diagnostics.js.map +1 -0
  62. package/dist/catalog/pressure-levels.d.ts +2 -0
  63. package/dist/catalog/pressure-levels.js +10 -0
  64. package/dist/catalog/pressure-levels.js.map +1 -0
  65. package/dist/catalog/profile-diagnostics.d.ts +14 -0
  66. package/dist/catalog/profile-diagnostics.js +38 -0
  67. package/dist/catalog/profile-diagnostics.js.map +1 -0
  68. package/dist/catalog/search.d.ts +2 -0
  69. package/dist/catalog/search.js +190 -0
  70. package/dist/catalog/search.js.map +1 -0
  71. package/dist/catalog/variables.d.ts +30 -0
  72. package/dist/catalog/variables.js +85 -0
  73. package/dist/catalog/variables.js.map +1 -0
  74. package/dist/cli/area-command.d.ts +2 -0
  75. package/dist/cli/area-command.js +132 -0
  76. package/dist/cli/area-command.js.map +1 -0
  77. package/dist/cli/catalog-command.d.ts +2 -0
  78. package/dist/cli/catalog-command.js +157 -0
  79. package/dist/cli/catalog-command.js.map +1 -0
  80. package/dist/cli/diagnostic-commands.d.ts +2 -0
  81. package/dist/cli/diagnostic-commands.js +425 -0
  82. package/dist/cli/diagnostic-commands.js.map +1 -0
  83. package/dist/cli/ensemble-command.d.ts +3 -0
  84. package/dist/cli/ensemble-command.js +169 -0
  85. package/dist/cli/ensemble-command.js.map +1 -0
  86. package/dist/cli/gefs-bundle-command.d.ts +2 -0
  87. package/dist/cli/gefs-bundle-command.js +141 -0
  88. package/dist/cli/gefs-bundle-command.js.map +1 -0
  89. package/dist/cli/gefs-parcel-command.d.ts +2 -0
  90. package/dist/cli/gefs-parcel-command.js +47 -0
  91. package/dist/cli/gefs-parcel-command.js.map +1 -0
  92. package/dist/cli/gefs-parcel-timeseries-command.d.ts +2 -0
  93. package/dist/cli/gefs-parcel-timeseries-command.js +59 -0
  94. package/dist/cli/gefs-parcel-timeseries-command.js.map +1 -0
  95. package/dist/cli/gefs-points-bundle-command.d.ts +2 -0
  96. package/dist/cli/gefs-points-bundle-command.js +111 -0
  97. package/dist/cli/gefs-points-bundle-command.js.map +1 -0
  98. package/dist/cli/model-comparison-command.d.ts +2 -0
  99. package/dist/cli/model-comparison-command.js +45 -0
  100. package/dist/cli/model-comparison-command.js.map +1 -0
  101. package/dist/cli/point-commands.d.ts +2 -0
  102. package/dist/cli/point-commands.js +546 -0
  103. package/dist/cli/point-commands.js.map +1 -0
  104. package/dist/cli/program.d.ts +2 -0
  105. package/dist/cli/program.js +31 -0
  106. package/dist/cli/program.js.map +1 -0
  107. package/dist/cli/shared.d.ts +32 -0
  108. package/dist/cli/shared.js +94 -0
  109. package/dist/cli/shared.js.map +1 -0
  110. package/dist/cli/transect-command.d.ts +2 -0
  111. package/dist/cli/transect-command.js +113 -0
  112. package/dist/cli/transect-command.js.map +1 -0
  113. package/dist/cli.d.ts +2 -0
  114. package/dist/cli.js +4 -0
  115. package/dist/cli.js.map +1 -0
  116. package/dist/core/area-distribution.d.ts +44 -0
  117. package/dist/core/area-distribution.js +96 -0
  118. package/dist/core/area-distribution.js.map +1 -0
  119. package/dist/core/area-summary.d.ts +43 -0
  120. package/dist/core/area-summary.js +243 -0
  121. package/dist/core/area-summary.js.map +1 -0
  122. package/dist/core/atmospheric-batch-points-service.d.ts +23 -0
  123. package/dist/core/atmospheric-batch-points-service.js +19 -0
  124. package/dist/core/atmospheric-batch-points-service.js.map +1 -0
  125. package/dist/core/atmospheric-diagnostic-timeseries-service.d.ts +20 -0
  126. package/dist/core/atmospheric-diagnostic-timeseries-service.js +19 -0
  127. package/dist/core/atmospheric-diagnostic-timeseries-service.js.map +1 -0
  128. package/dist/core/atmospheric-layer-diagnostics-service.d.ts +20 -0
  129. package/dist/core/atmospheric-layer-diagnostics-service.js +19 -0
  130. package/dist/core/atmospheric-layer-diagnostics-service.js.map +1 -0
  131. package/dist/core/atmospheric-profile-diagnostics-service.d.ts +20 -0
  132. package/dist/core/atmospheric-profile-diagnostics-service.js +19 -0
  133. package/dist/core/atmospheric-profile-diagnostics-service.js.map +1 -0
  134. package/dist/core/atmospheric-profile-service.d.ts +20 -0
  135. package/dist/core/atmospheric-profile-service.js +19 -0
  136. package/dist/core/atmospheric-profile-service.js.map +1 -0
  137. package/dist/core/atmospheric-profile.d.ts +21 -0
  138. package/dist/core/atmospheric-profile.js +83 -0
  139. package/dist/core/atmospheric-profile.js.map +1 -0
  140. package/dist/core/atmospheric-run-comparison-service.d.ts +23 -0
  141. package/dist/core/atmospheric-run-comparison-service.js +19 -0
  142. package/dist/core/atmospheric-run-comparison-service.js.map +1 -0
  143. package/dist/core/atmospheric-timeseries-service.d.ts +20 -0
  144. package/dist/core/atmospheric-timeseries-service.js +19 -0
  145. package/dist/core/atmospheric-timeseries-service.js.map +1 -0
  146. package/dist/core/batch-points.d.ts +19 -0
  147. package/dist/core/batch-points.js +81 -0
  148. package/dist/core/batch-points.js.map +1 -0
  149. package/dist/core/concurrency.d.ts +1 -0
  150. package/dist/core/concurrency.js +23 -0
  151. package/dist/core/concurrency.js.map +1 -0
  152. package/dist/core/diagnostic-time-series.d.ts +31 -0
  153. package/dist/core/diagnostic-time-series.js +205 -0
  154. package/dist/core/diagnostic-time-series.js.map +1 -0
  155. package/dist/core/ensemble-statistics.d.ts +30 -0
  156. package/dist/core/ensemble-statistics.js +68 -0
  157. package/dist/core/ensemble-statistics.js.map +1 -0
  158. package/dist/core/forecast-hour.d.ts +7 -0
  159. package/dist/core/forecast-hour.js +51 -0
  160. package/dist/core/forecast-hour.js.map +1 -0
  161. package/dist/core/gefs-area-summary.d.ts +30 -0
  162. package/dist/core/gefs-area-summary.js +227 -0
  163. package/dist/core/gefs-area-summary.js.map +1 -0
  164. package/dist/core/gefs-batch-points.d.ts +27 -0
  165. package/dist/core/gefs-batch-points.js +143 -0
  166. package/dist/core/gefs-batch-points.js.map +1 -0
  167. package/dist/core/gefs-bundle-decoder.d.ts +100 -0
  168. package/dist/core/gefs-bundle-decoder.js +228 -0
  169. package/dist/core/gefs-bundle-decoder.js.map +1 -0
  170. package/dist/core/gefs-bundle-timeseries.d.ts +19 -0
  171. package/dist/core/gefs-bundle-timeseries.js +107 -0
  172. package/dist/core/gefs-bundle-timeseries.js.map +1 -0
  173. package/dist/core/gefs-diagnostic-timeseries.d.ts +31 -0
  174. package/dist/core/gefs-diagnostic-timeseries.js +169 -0
  175. package/dist/core/gefs-diagnostic-timeseries.js.map +1 -0
  176. package/dist/core/gefs-ensemble-profile.d.ts +21 -0
  177. package/dist/core/gefs-ensemble-profile.js +152 -0
  178. package/dist/core/gefs-ensemble-profile.js.map +1 -0
  179. package/dist/core/gefs-ensemble-timeseries.d.ts +19 -0
  180. package/dist/core/gefs-ensemble-timeseries.js +83 -0
  181. package/dist/core/gefs-ensemble-timeseries.js.map +1 -0
  182. package/dist/core/gefs-ensemble.d.ts +29 -0
  183. package/dist/core/gefs-ensemble.js +185 -0
  184. package/dist/core/gefs-ensemble.js.map +1 -0
  185. package/dist/core/gefs-latest-run.d.ts +21 -0
  186. package/dist/core/gefs-latest-run.js +67 -0
  187. package/dist/core/gefs-latest-run.js.map +1 -0
  188. package/dist/core/gefs-layer-diagnostics.d.ts +13 -0
  189. package/dist/core/gefs-layer-diagnostics.js +87 -0
  190. package/dist/core/gefs-layer-diagnostics.js.map +1 -0
  191. package/dist/core/gefs-member-bundle.d.ts +25 -0
  192. package/dist/core/gefs-member-bundle.js +94 -0
  193. package/dist/core/gefs-member-bundle.js.map +1 -0
  194. package/dist/core/gefs-parcel-diagnostics.d.ts +21 -0
  195. package/dist/core/gefs-parcel-diagnostics.js +224 -0
  196. package/dist/core/gefs-parcel-diagnostics.js.map +1 -0
  197. package/dist/core/gefs-points-bundle-timeseries.d.ts +25 -0
  198. package/dist/core/gefs-points-bundle-timeseries.js +161 -0
  199. package/dist/core/gefs-points-bundle-timeseries.js.map +1 -0
  200. package/dist/core/gefs-points-bundle.d.ts +28 -0
  201. package/dist/core/gefs-points-bundle.js +114 -0
  202. package/dist/core/gefs-points-bundle.js.map +1 -0
  203. package/dist/core/gefs-points-timeseries.d.ts +27 -0
  204. package/dist/core/gefs-points-timeseries.js +123 -0
  205. package/dist/core/gefs-points-timeseries.js.map +1 -0
  206. package/dist/core/gefs-profile-derivation.d.ts +4 -0
  207. package/dist/core/gefs-profile-derivation.js +38 -0
  208. package/dist/core/gefs-profile-derivation.js.map +1 -0
  209. package/dist/core/gefs-profile-diagnostics.d.ts +13 -0
  210. package/dist/core/gefs-profile-diagnostics.js +152 -0
  211. package/dist/core/gefs-profile-diagnostics.js.map +1 -0
  212. package/dist/core/gefs-run-comparison.d.ts +26 -0
  213. package/dist/core/gefs-run-comparison.js +141 -0
  214. package/dist/core/gefs-run-comparison.js.map +1 -0
  215. package/dist/core/gefs-time.d.ts +9 -0
  216. package/dist/core/gefs-time.js +64 -0
  217. package/dist/core/gefs-time.js.map +1 -0
  218. package/dist/core/gefs-transect.d.ts +19 -0
  219. package/dist/core/gefs-transect.js +71 -0
  220. package/dist/core/gefs-transect.js.map +1 -0
  221. package/dist/core/gfs-gefs-aligned-run.d.ts +20 -0
  222. package/dist/core/gfs-gefs-aligned-run.js +40 -0
  223. package/dist/core/gfs-gefs-aligned-run.js.map +1 -0
  224. package/dist/core/gfs-gefs-comparison.d.ts +23 -0
  225. package/dist/core/gfs-gefs-comparison.js +125 -0
  226. package/dist/core/gfs-gefs-comparison.js.map +1 -0
  227. package/dist/core/latest-run.d.ts +30 -0
  228. package/dist/core/latest-run.js +132 -0
  229. package/dist/core/latest-run.js.map +1 -0
  230. package/dist/core/layer-diagnostics.d.ts +13 -0
  231. package/dist/core/layer-diagnostics.js +36 -0
  232. package/dist/core/layer-diagnostics.js.map +1 -0
  233. package/dist/core/parcel-diagnostics.d.ts +13 -0
  234. package/dist/core/parcel-diagnostics.js +69 -0
  235. package/dist/core/parcel-diagnostics.js.map +1 -0
  236. package/dist/core/points-time-series.d.ts +27 -0
  237. package/dist/core/points-time-series.js +116 -0
  238. package/dist/core/points-time-series.js.map +1 -0
  239. package/dist/core/pressure-diagnostics.d.ts +16 -0
  240. package/dist/core/pressure-diagnostics.js +79 -0
  241. package/dist/core/pressure-diagnostics.js.map +1 -0
  242. package/dist/core/profile-diagnostics.d.ts +13 -0
  243. package/dist/core/profile-diagnostics.js +38 -0
  244. package/dist/core/profile-diagnostics.js.map +1 -0
  245. package/dist/core/profile.d.ts +28 -0
  246. package/dist/core/profile.js +275 -0
  247. package/dist/core/profile.js.map +1 -0
  248. package/dist/core/run-comparison.d.ts +75 -0
  249. package/dist/core/run-comparison.js +189 -0
  250. package/dist/core/run-comparison.js.map +1 -0
  251. package/dist/core/time-series.d.ts +19 -0
  252. package/dist/core/time-series.js +89 -0
  253. package/dist/core/time-series.js.map +1 -0
  254. package/dist/core/transect.d.ts +37 -0
  255. package/dist/core/transect.js +115 -0
  256. package/dist/core/transect.js.map +1 -0
  257. package/dist/core/types.d.ts +256 -0
  258. package/dist/core/types.js +2 -0
  259. package/dist/core/types.js.map +1 -0
  260. package/dist/derived/humidity.d.ts +9 -0
  261. package/dist/derived/humidity.js +28 -0
  262. package/dist/derived/humidity.js.map +1 -0
  263. package/dist/derived/layer-diagnostics.d.ts +13 -0
  264. package/dist/derived/layer-diagnostics.js +35 -0
  265. package/dist/derived/layer-diagnostics.js.map +1 -0
  266. package/dist/derived/parcel-diagnostics.d.ts +53 -0
  267. package/dist/derived/parcel-diagnostics.js +342 -0
  268. package/dist/derived/parcel-diagnostics.js.map +1 -0
  269. package/dist/derived/profile-diagnostics.d.ts +30 -0
  270. package/dist/derived/profile-diagnostics.js +62 -0
  271. package/dist/derived/profile-diagnostics.js.map +1 -0
  272. package/dist/derived/profile-features.d.ts +46 -0
  273. package/dist/derived/profile-features.js +123 -0
  274. package/dist/derived/profile-features.js.map +1 -0
  275. package/dist/derived/thermodynamics.d.ts +57 -0
  276. package/dist/derived/thermodynamics.js +210 -0
  277. package/dist/derived/thermodynamics.js.map +1 -0
  278. package/dist/derived/wind.d.ts +5 -0
  279. package/dist/derived/wind.js +9 -0
  280. package/dist/derived/wind.js.map +1 -0
  281. package/dist/entry.d.ts +2 -0
  282. package/dist/entry.js +18 -0
  283. package/dist/entry.js.map +1 -0
  284. package/dist/grib/gribberish-runtime.d.ts +39 -0
  285. package/dist/grib/gribberish-runtime.js +328 -0
  286. package/dist/grib/gribberish-runtime.js.map +1 -0
  287. package/dist/grib/index.d.ts +25 -0
  288. package/dist/grib/index.js +96 -0
  289. package/dist/grib/index.js.map +1 -0
  290. package/dist/grib/wgrib2-grid.d.ts +30 -0
  291. package/dist/grib/wgrib2-grid.js +124 -0
  292. package/dist/grib/wgrib2-grid.js.map +1 -0
  293. package/dist/grib/wgrib2-stats.d.ts +54 -0
  294. package/dist/grib/wgrib2-stats.js +173 -0
  295. package/dist/grib/wgrib2-stats.js.map +1 -0
  296. package/dist/grib/wgrib2.d.ts +13 -0
  297. package/dist/grib/wgrib2.js +122 -0
  298. package/dist/grib/wgrib2.js.map +1 -0
  299. package/dist/mcp-area-tool.d.ts +115 -0
  300. package/dist/mcp-area-tool.js +17 -0
  301. package/dist/mcp-area-tool.js.map +1 -0
  302. package/dist/mcp-cli.d.ts +2 -0
  303. package/dist/mcp-cli.js +3 -0
  304. package/dist/mcp-cli.js.map +1 -0
  305. package/dist/mcp-extended-server.d.ts +6 -0
  306. package/dist/mcp-extended-server.js +70 -0
  307. package/dist/mcp-extended-server.js.map +1 -0
  308. package/dist/mcp-gefs-area-tool.d.ts +197 -0
  309. package/dist/mcp-gefs-area-tool.js +17 -0
  310. package/dist/mcp-gefs-area-tool.js.map +1 -0
  311. package/dist/mcp-gefs-bundle-tool.d.ts +281 -0
  312. package/dist/mcp-gefs-bundle-tool.js +24 -0
  313. package/dist/mcp-gefs-bundle-tool.js.map +1 -0
  314. package/dist/mcp-gefs-catalog-tool.d.ts +135 -0
  315. package/dist/mcp-gefs-catalog-tool.js +26 -0
  316. package/dist/mcp-gefs-catalog-tool.js.map +1 -0
  317. package/dist/mcp-gefs-points-bundle-timeseries-tool.d.ts +146 -0
  318. package/dist/mcp-gefs-points-bundle-timeseries-tool.js +17 -0
  319. package/dist/mcp-gefs-points-bundle-timeseries-tool.js.map +1 -0
  320. package/dist/mcp-gefs-points-bundle-tool.d.ts +144 -0
  321. package/dist/mcp-gefs-points-bundle-tool.js +17 -0
  322. package/dist/mcp-gefs-points-bundle-tool.js.map +1 -0
  323. package/dist/mcp-gefs-points-timeseries-tool.d.ts +81 -0
  324. package/dist/mcp-gefs-points-timeseries-tool.js +17 -0
  325. package/dist/mcp-gefs-points-timeseries-tool.js.map +1 -0
  326. package/dist/mcp-gefs-tool.d.ts +1444 -0
  327. package/dist/mcp-gefs-tool.js +52 -0
  328. package/dist/mcp-gefs-tool.js.map +1 -0
  329. package/dist/mcp-gefs-transect-tool.d.ts +156 -0
  330. package/dist/mcp-gefs-transect-tool.js +17 -0
  331. package/dist/mcp-gefs-transect-tool.js.map +1 -0
  332. package/dist/mcp-http-server.d.ts +13 -0
  333. package/dist/mcp-http-server.js +145 -0
  334. package/dist/mcp-http-server.js.map +1 -0
  335. package/dist/mcp-http.d.ts +2 -0
  336. package/dist/mcp-http.js +22 -0
  337. package/dist/mcp-http.js.map +1 -0
  338. package/dist/mcp-model-comparison-tool.d.ts +90 -0
  339. package/dist/mcp-model-comparison-tool.js +17 -0
  340. package/dist/mcp-model-comparison-tool.js.map +1 -0
  341. package/dist/mcp-server.d.ts +2 -0
  342. package/dist/mcp-server.js +261 -0
  343. package/dist/mcp-server.js.map +1 -0
  344. package/dist/mcp-tool.d.ts +1160 -0
  345. package/dist/mcp-tool.js +128 -0
  346. package/dist/mcp-tool.js.map +1 -0
  347. package/dist/mcp-transect-tool.d.ts +79 -0
  348. package/dist/mcp-transect-tool.js +17 -0
  349. package/dist/mcp-transect-tool.js.map +1 -0
  350. package/dist/mcp.d.ts +1 -0
  351. package/dist/mcp.js +4 -0
  352. package/dist/mcp.js.map +1 -0
  353. package/dist/schema/area-summary-result.d.ts +234 -0
  354. package/dist/schema/area-summary-result.js +39 -0
  355. package/dist/schema/area-summary-result.js.map +1 -0
  356. package/dist/schema/area-summary.d.ts +113 -0
  357. package/dist/schema/area-summary.js +23 -0
  358. package/dist/schema/area-summary.js.map +1 -0
  359. package/dist/schema/atmospheric-diagnostic-timeseries.d.ts +711 -0
  360. package/dist/schema/atmospheric-diagnostic-timeseries.js +19 -0
  361. package/dist/schema/atmospheric-diagnostic-timeseries.js.map +1 -0
  362. package/dist/schema/atmospheric-layer-diagnostics.d.ts +285 -0
  363. package/dist/schema/atmospheric-layer-diagnostics.js +19 -0
  364. package/dist/schema/atmospheric-layer-diagnostics.js.map +1 -0
  365. package/dist/schema/atmospheric-profile-diagnostics.d.ts +463 -0
  366. package/dist/schema/atmospheric-profile-diagnostics.js +19 -0
  367. package/dist/schema/atmospheric-profile-diagnostics.js.map +1 -0
  368. package/dist/schema/atmospheric-profile.d.ts +495 -0
  369. package/dist/schema/atmospheric-profile.js +20 -0
  370. package/dist/schema/atmospheric-profile.js.map +1 -0
  371. package/dist/schema/atmospheric-timeseries.d.ts +460 -0
  372. package/dist/schema/atmospheric-timeseries.js +19 -0
  373. package/dist/schema/atmospheric-timeseries.js.map +1 -0
  374. package/dist/schema/catalog-search.d.ts +134 -0
  375. package/dist/schema/catalog-search.js +50 -0
  376. package/dist/schema/catalog-search.js.map +1 -0
  377. package/dist/schema/diagnostic-time-series-result.d.ts +345 -0
  378. package/dist/schema/diagnostic-time-series-result.js +60 -0
  379. package/dist/schema/diagnostic-time-series-result.js.map +1 -0
  380. package/dist/schema/diagnostic-time-series.d.ts +66 -0
  381. package/dist/schema/diagnostic-time-series.js +68 -0
  382. package/dist/schema/diagnostic-time-series.js.map +1 -0
  383. package/dist/schema/gefs-area-summary.d.ts +384 -0
  384. package/dist/schema/gefs-area-summary.js +138 -0
  385. package/dist/schema/gefs-area-summary.js.map +1 -0
  386. package/dist/schema/gefs-batch-points.d.ts +221 -0
  387. package/dist/schema/gefs-batch-points.js +83 -0
  388. package/dist/schema/gefs-batch-points.js.map +1 -0
  389. package/dist/schema/gefs-bundle-timeseries.d.ts +359 -0
  390. package/dist/schema/gefs-bundle-timeseries.js +59 -0
  391. package/dist/schema/gefs-bundle-timeseries.js.map +1 -0
  392. package/dist/schema/gefs-diagnostic-timeseries.d.ts +869 -0
  393. package/dist/schema/gefs-diagnostic-timeseries.js +140 -0
  394. package/dist/schema/gefs-diagnostic-timeseries.js.map +1 -0
  395. package/dist/schema/gefs-ensemble-profile.d.ts +242 -0
  396. package/dist/schema/gefs-ensemble-profile.js +94 -0
  397. package/dist/schema/gefs-ensemble-profile.js.map +1 -0
  398. package/dist/schema/gefs-ensemble-timeseries.d.ts +192 -0
  399. package/dist/schema/gefs-ensemble-timeseries.js +94 -0
  400. package/dist/schema/gefs-ensemble-timeseries.js.map +1 -0
  401. package/dist/schema/gefs-ensemble.d.ts +247 -0
  402. package/dist/schema/gefs-ensemble.js +129 -0
  403. package/dist/schema/gefs-ensemble.js.map +1 -0
  404. package/dist/schema/gefs-layer-diagnostics.d.ts +202 -0
  405. package/dist/schema/gefs-layer-diagnostics.js +113 -0
  406. package/dist/schema/gefs-layer-diagnostics.js.map +1 -0
  407. package/dist/schema/gefs-member-bundle.d.ts +505 -0
  408. package/dist/schema/gefs-member-bundle.js +163 -0
  409. package/dist/schema/gefs-member-bundle.js.map +1 -0
  410. package/dist/schema/gefs-parcel-diagnostics.d.ts +399 -0
  411. package/dist/schema/gefs-parcel-diagnostics.js +93 -0
  412. package/dist/schema/gefs-parcel-diagnostics.js.map +1 -0
  413. package/dist/schema/gefs-points-bundle-timeseries.d.ts +366 -0
  414. package/dist/schema/gefs-points-bundle-timeseries.js +59 -0
  415. package/dist/schema/gefs-points-bundle-timeseries.js.map +1 -0
  416. package/dist/schema/gefs-points-bundle.d.ts +391 -0
  417. package/dist/schema/gefs-points-bundle.js +53 -0
  418. package/dist/schema/gefs-points-bundle.js.map +1 -0
  419. package/dist/schema/gefs-points-timeseries.d.ts +200 -0
  420. package/dist/schema/gefs-points-timeseries.js +103 -0
  421. package/dist/schema/gefs-points-timeseries.js.map +1 -0
  422. package/dist/schema/gefs-profile-diagnostics.d.ts +498 -0
  423. package/dist/schema/gefs-profile-diagnostics.js +126 -0
  424. package/dist/schema/gefs-profile-diagnostics.js.map +1 -0
  425. package/dist/schema/gefs-run-comparison.d.ts +189 -0
  426. package/dist/schema/gefs-run-comparison.js +109 -0
  427. package/dist/schema/gefs-run-comparison.js.map +1 -0
  428. package/dist/schema/gefs-transect.d.ts +407 -0
  429. package/dist/schema/gefs-transect.js +50 -0
  430. package/dist/schema/gefs-transect.js.map +1 -0
  431. package/dist/schema/gfs-gefs-comparison.d.ts +173 -0
  432. package/dist/schema/gfs-gefs-comparison.js +95 -0
  433. package/dist/schema/gfs-gefs-comparison.js.map +1 -0
  434. package/dist/schema/query.d.ts +796 -0
  435. package/dist/schema/query.js +225 -0
  436. package/dist/schema/query.js.map +1 -0
  437. package/dist/schema/result.d.ts +1560 -0
  438. package/dist/schema/result.js +300 -0
  439. package/dist/schema/result.js.map +1 -0
  440. package/dist/schema/run-comparison-result.d.ts +622 -0
  441. package/dist/schema/run-comparison-result.js +71 -0
  442. package/dist/schema/run-comparison-result.js.map +1 -0
  443. package/dist/schema/transect-result.d.ts +122 -0
  444. package/dist/schema/transect-result.js +30 -0
  445. package/dist/schema/transect-result.js.map +1 -0
  446. package/dist/schema/transect.d.ts +41 -0
  447. package/dist/schema/transect.js +22 -0
  448. package/dist/schema/transect.js.map +1 -0
  449. package/dist/sources/gefs-s3.d.ts +13 -0
  450. package/dist/sources/gefs-s3.js +39 -0
  451. package/dist/sources/gefs-s3.js.map +1 -0
  452. package/dist/sources/gfs-s3.d.ts +21 -0
  453. package/dist/sources/gfs-s3.js +63 -0
  454. package/dist/sources/gfs-s3.js.map +1 -0
  455. package/dist/sources/nomads.d.ts +24 -0
  456. package/dist/sources/nomads.js +53 -0
  457. package/dist/sources/nomads.js.map +1 -0
  458. package/dist/sources/profile-source.d.ts +19 -0
  459. package/dist/sources/profile-source.js +26 -0
  460. package/dist/sources/profile-source.js.map +1 -0
  461. package/dist/sources/types.d.ts +24 -0
  462. package/dist/sources/types.js +2 -0
  463. package/dist/sources/types.js.map +1 -0
  464. package/package.json +78 -0
@@ -0,0 +1,295 @@
1
+ # Architecture
2
+
3
+ Weather for Grown Ups is primarily a **numerical-weather-model data-access product**, not a forecasting or interpretation layer.
4
+
5
+ ```text
6
+ NOAA GFS / GEFS
7
+
8
+ model-specific catalogs, run semantics and source adapters
9
+
10
+ normalized atmospheric states / profile adapters
11
+
12
+ shared meteorological kernels and composition primitives
13
+
14
+ deterministic result OR ensemble member aggregation
15
+
16
+ model-discriminated shared contracts
17
+
18
+ CLI / MCP stdio / MCP Streamable HTTP
19
+
20
+ agent interpretation
21
+ ```
22
+
23
+ ## Core principles
24
+
25
+ The core owns:
26
+
27
+ - explicit model identity, model capabilities and product semantics;
28
+ - canonical variable/field/member names;
29
+ - run, valid-time, forecast-hour and native-cadence semantics;
30
+ - authoritative published pressure levels and explicit non-isobaric vertical semantics;
31
+ - instantaneous / accumulation / average temporal semantics where applicable;
32
+ - query planning and dependency expansion;
33
+ - upstream access, pacing and immutable-slice caching;
34
+ - GRIB decoder abstraction;
35
+ - normalized typed atmospheric values and provenance;
36
+ - model-independent physical transforms;
37
+ - spatial/temporal compositions;
38
+ - explicit ensemble statistics and aggregation semantics.
39
+
40
+ The core is the product. CLI and MCP are adapters over it, not separate implementations.
41
+
42
+ The key rule is:
43
+
44
+ > **Unify operations and physics; preserve model semantics.**
45
+
46
+ A common operation does not imply identical source inventory or identical result shape. GFS and GEFS can both implement `profile`, `timeseries`, or `layer_diagnostics`, while deterministic GFS returns one state and GEFS returns a member distribution.
47
+
48
+ ## Model capability boundary
49
+
50
+ `src/catalog/models.ts` is the explicit capability registry for atmospheric model adapters. It records model kind, grid spacing, forecast horizon and supported operations.
51
+
52
+ This prevents two bad failure modes:
53
+
54
+ 1. mechanically copying every GFS endpoint into a GEFS namespace even when ensemble semantics should differ;
55
+ 2. pretending GEFS supports a GFS operation when the required upstream fields are not actually available.
56
+
57
+ Unsupported capabilities are data in the model catalog, not accidental runtime surprises. Adding a future model such as ICON or ECMWF/AIFS should mean implementing model-specific inventory/source/run adapters behind stable atmospheric operation boundaries rather than creating a new API tree from scratch.
58
+
59
+ ## Normalized pressure-profile boundary
60
+
61
+ `ProfileLevel` is the normalized pressure-state representation consumed by deterministic meteorology.
62
+
63
+ `src/core/atmospheric-profile.ts` defines a model-independent `AtmosphericProfileSnapshot` around those levels. Deterministic GFS already naturally produces this shape. GEFS member profiles are adapted into it member-by-member:
64
+
65
+ ```text
66
+ GFS raw/profile service ───────────────┐
67
+ ├─> normalized ProfileLevel[] ─> shared physics
68
+ GEFS ensemble profile ─> member split ┘
69
+ ```
70
+
71
+ The adapter maps GEFS normalized raw values such as temperature, U/V wind and geopotential height to the same typed level fields used by deterministic diagnostics. The physical layer therefore does not need to know whether a level originated in GFS or one GEFS member.
72
+
73
+ This boundary is intentionally below ensemble aggregation. A nonlinear meteorological diagnostic is evaluated on each ensemble member's atmospheric state first; WFG does not calculate diagnostics from the ensemble-mean profile unless a future operation explicitly defines that different quantity.
74
+
75
+ ## Shared diagnostic kernels
76
+
77
+ Low-level formulas already live under `src/derived/`. `src/core/pressure-diagnostics.ts` now owns model-independent orchestration over normalized pressure levels for:
78
+
79
+ - environmental temperature lapse rate;
80
+ - vector wind shear;
81
+ - potential-temperature gradient;
82
+ - freezing-level crossings;
83
+ - sampled temperature-inversion layers.
84
+
85
+ The deterministic GFS `LayerDiagnosticsService` and `ProfileDiagnosticsService` call these kernels rather than owning parallel formula/traversal logic.
86
+
87
+ GEFS layer diagnostics call the **same** layer kernel once per requested member. Because geopotential heights differ between members, physical layer depth is member-specific too. Only after each member's calculation is complete are diagnostic outputs and depth summarized across the ensemble.
88
+
89
+ Parcel mechanics remain model-independent under `derived/parcel-diagnostics.ts`, but the current GEFS product contract does not yet provide the complete dependency parity needed to expose the GFS parcel operation. The model capability catalog therefore marks parcel diagnostics unsupported for GEFS instead of fabricating parity.
90
+
91
+ ## Shared ensemble statistics
92
+
93
+ `src/core/ensemble-statistics.ts` is the one distribution implementation used by raw scalar GEFS fields, profile cells and diagnostic outputs.
94
+
95
+ It defines:
96
+
97
+ - arithmetic mean;
98
+ - population standard deviation;
99
+ - min/max;
100
+ - caller-selected quantiles using linear interpolation over sorted members;
101
+ - raw >= threshold member fractions.
102
+
103
+ Threshold fractions retain the interpretation marker `raw_member_fraction_not_calibrated_probability`. Calibration, model weighting, climatological correction and decision-specific interpretation remain outside this primitive.
104
+
105
+ Centralizing these mechanics ensures that “p50” or “population spread” means the same thing whether the distribution contains raw temperature, wind shear, layer depth, or another future member-derived quantity.
106
+
107
+ ## Unified operation dispatch
108
+
109
+ The canonical internal operation contracts are model-discriminated unions rather than flattened result objects.
110
+
111
+ Examples:
112
+
113
+ ```text
114
+ AtmosphericProfileRequest
115
+ ├─ model=gfs_0p25 + GFS profile query
116
+ └─ model=gefs_0p50 + GEFS ensemble-profile query
117
+
118
+ AtmosphericProfileResult
119
+ ├─ deterministic GFS profile
120
+ └─ GEFS profile distributions/member data
121
+ ```
122
+
123
+ Equivalent dispatch boundaries exist for time series and layer diagnostics.
124
+
125
+ Services:
126
+
127
+ - `AtmosphericProfileService`
128
+ - `AtmosphericTimeSeriesService`
129
+ - `AtmosphericLayerDiagnosticsService`
130
+
131
+ select the model adapter and validate the returned model-specific schema. They intentionally do **not** coerce results into a lowest-common-denominator `value` object.
132
+
133
+ ## Deterministic GFS 0.25°
134
+
135
+ ### Catalog-driven derivation
136
+
137
+ Derived variables declare raw GFS dependencies in the shared catalog. Query planning expands those dependencies before source access, validates raw completeness, and only then computes the requested value locally. This keeps NOAA access minimal and makes the same derivation automatically available to every compatible GFS composition.
138
+
139
+ Moist thermodynamic variables remain ordinary per-level derived variables. Wet-bulb temperature and equivalent potential temperature both depend on temperature plus specific humidity, with pressure supplied by the isobaric coordinate. Equivalent potential temperature uses the Bolton (1980) formulation. Wet-bulb temperature is a deterministic same-pressure adiabatic-saturation enthalpy solve.
140
+
141
+ ### Vertical diagnostics
142
+
143
+ Pressure-layer diagnostics explicitly name a lower-altitude/higher-pressure surface and an upper-altitude/lower-pressure surface. One minimal two-level profile supplies endpoints. Height-normalized quantities use geopotential-height difference rather than treating pressure difference as geometric depth.
144
+
145
+ Whole-profile diagnostics are explicit about sampling. The caller chooses published pressure levels. WFG fetches the dependency union once, returns sampled levels, and derives freezing-level crossings or inversion structure locally. It does not imply unresolved vertical structure between pressure levels never requested.
146
+
147
+ ### Parcel diagnostics
148
+
149
+ Parcel choice is part of the physics, so WFG has no ambiguous generic CAPE tool. Callers explicitly select `surface_2m`, `mixed_layer_100hpa`, or `most_unstable_300hpa`.
150
+
151
+ One profile request obtains pressure-level temperature, specific humidity and geopotential height together with required surface/2 m fields. The surface parcel initializes from GFS surface pressure/geopotential height with 2 m temperature and humidity. The mixed-layer parcel uses pressure-weighted mean potential temperature and mixing ratio over the exact lowest 100 hPa. The most-unstable parcel selects the sampled state with maximum Bolton equivalent potential temperature in the lowest 300 hPa.
152
+
153
+ Ascent is dry adiabatic to the Bolton LCL and pseudo-adiabatic above it using deterministic numerical integration in log pressure. Environmental values are interpolated in log pressure. Buoyancy compares parcel and environmental **virtual temperature**; zero-buoyancy crossings are inserted before pressure-coordinate CAPE/CIN integration. Raw environmental levels and the complete parcel path remain in the single-time result for auditability.
154
+
155
+ ### Spatial and temporal composition
156
+
157
+ `BatchPointsService` is the efficient same-time/multi-location primitive. It resolves one GFS run, downloads/reuses one selected-message AWS slice, then samples all requested coordinates locally.
158
+
159
+ `TransectService` composes that primitive: it generates evenly spaced great-circle coordinates, delegates one batch request, and attaches along-track distance.
160
+
161
+ `PointsTimeSeriesService` composes batch requests across native GFS forecast steps, reusing one selected-message slice per step. `TimeSeriesService` is the single-point field equivalent. `RunComparisonService` holds valid time constant and compares consecutive six-hour cycles with deterministic delta rules.
162
+
163
+ `DiagnosticTimeSeriesService` composes existing single-time layer, whole-profile and parcel services across the native GFS time axis. Query-aware `latest` is resolved once against the complete valid-time range and exact raw dependencies; every step then receives that explicit cycle so a series cannot drift between runs.
164
+
165
+ Area summaries deliberately use a different path. A bounded NOMADS subset is decoded locally and reduced to statistics; the raw grid is never returned.
166
+
167
+ ## GEFS 0.5° ensemble foundation
168
+
169
+ GEFS remains a separate **model adapter**, not a boolean/member option inside GFS source code.
170
+
171
+ ### Separate inventory and run semantics
172
+
173
+ `src/catalog/gefs.ts` declares supported control/perturbed members and the current `pgrb2a` variable/pressure combinations. GEFS schemas validate the requested Cartesian selection before source access.
174
+
175
+ `GefsLatestRunResolver` is query-aware over member availability. Starting from the newest six-hour cycle that could precede the requested valid time, it walks older cycles until every selected member has the required forecast file. Range resolution fixes one cycle across complete ensemble time-series intervals.
176
+
177
+ The current WFG GEFS contract uses native three-hour output from `f000` through `f384`. Upstream changes/extensions are adopted only by explicit contract changes and tests.
178
+
179
+ ### Member-aware source access
180
+
181
+ `GefsS3SubsetCache` addresses immutable member-specific GEFS objects in NOAA AWS Open Data. For profile/diagnostic selections it:
182
+
183
+ 1. fetches and caches the member `.idx` inventory;
184
+ 2. selects all requested GRIB variable/pressure messages;
185
+ 3. downloads only those byte ranges;
186
+ 4. stitches and caches one immutable multi-message subset for that selection/member;
187
+ 5. decodes the point once with the shared `Wgrib2Decoder`.
188
+
189
+ Range downloads are bounded within each member and member processing itself is bounded-concurrent. Repeated operations naturally reuse immutable profile slices.
190
+
191
+ ### Ensemble profile and diagnostics
192
+
193
+ `GefsEnsembleProfileService` produces raw normalized member profiles plus cell-wise distributions. Member profiles are omitted from public responses by default but can be requested for audit/composition.
194
+
195
+ `GefsLayerDiagnosticsService` is an example of the intended ensemble composition pattern:
196
+
197
+ ```text
198
+ requested layer diagnostic
199
+
200
+ expand shared diagnostic dependencies
201
+
202
+ fetch one GEFS profile slice per member
203
+
204
+ adapt each member to normalized ProfileLevel[]
205
+
206
+ run same pressure-diagnostic kernel per member
207
+
208
+ summarize member diagnostic outputs and layer depths
209
+ ```
210
+
211
+ This is the template for future ensemble diagnostics.
212
+
213
+ ## Public surfaces
214
+
215
+ CLI and MCP remain equal public surfaces over the same core, but they optimize for different callers.
216
+
217
+ ### CLI
218
+
219
+ The CLI is operation-oriented. Shared operations use `--model gfs|gefs`:
220
+
221
+ - `profile`
222
+ - `timeseries`
223
+ - `layer`
224
+
225
+ GFS remains the default model for backward compatibility. Existing `ensemble-profile` and `ensemble-timeseries` commands remain explicit GEFS aliases but route through the same unified dispatchers rather than separate business logic.
226
+
227
+ Other commands remain model-specific until the capability registry says otherwise.
228
+
229
+ ### MCP
230
+
231
+ MCP intentionally retains explicit wrappers such as:
232
+
233
+ - `get_gfs_profile`
234
+ - `get_gefs_ensemble_profile`
235
+ - `get_gfs_layer_diagnostics`
236
+ - `get_gefs_layer_diagnostics`
237
+
238
+ This keeps schemas small and obvious for agents, especially smaller models, while those wrappers delegate to shared underlying profile/diagnostic primitives. A unified core does not require one giant polymorphic MCP tool.
239
+
240
+ Both MCP transports instantiate the same tool set:
241
+
242
+ - **stdio** for local process-spawned clients;
243
+ - **Streamable HTTP** for hosted/remote clients.
244
+
245
+ The HTTP launcher is transport/infrastructure code only. It adds `/mcp`, `/healthz`, safe loopback defaults and Host/Origin protection; it does not define a separate atmospheric API.
246
+
247
+ ## Shared contracts
248
+
249
+ Zod schemas define public query/result shapes. Shared operation schemas are discriminated unions over model-specific schemas, preserving exact semantics and validation rules for each model.
250
+
251
+ A new model or operation is incomplete until:
252
+
253
+ 1. capability metadata is correct;
254
+ 2. model-specific inventory/source/run validation exists;
255
+ 3. normalized state adaptation is defined where physical kernels are reused;
256
+ 4. result semantics are explicit;
257
+ 5. CLI and MCP adapters are updated where the operation is public;
258
+ 6. deterministic tests and appropriate real-upstream smoke coverage exist.
259
+
260
+ ## Source strategy
261
+
262
+ ### NOMADS Grib Filter
263
+
264
+ Use NOMADS where geographic subsetting materially reduces transfer: deterministic GFS single-point requests and bounded areas. All physical requests share the same cross-process courtesy limiter and cache boundary.
265
+
266
+ ### NOAA AWS Open Data — deterministic GFS
267
+
268
+ Use GFS AWS `.idx` inventories and HTTP byte ranges where selected messages can be reused across locations or forecast steps: batch points, transects, field time series, diagnostic time series, multi-point time series, run comparison and aligned GFS-vs-GEFS work.
269
+
270
+ ### NOAA AWS Open Data — GEFS
271
+
272
+ Use member-specific GEFS AWS `pgrb2a` objects and `.idx` byte ranges. Each requested member contributes a selected-message slice; sampling/diagnostics are bounded-concurrent and aggregation happens locally.
273
+
274
+ AWS paths do not use the NOMADS courtesy limiter because they do not call the NOMADS scripted-filter service.
275
+
276
+ ## GRIB strategy
277
+
278
+ WFG does not implement a GRIB2 parser in TypeScript. `Wgrib2Decoder` is a narrow adapter around NOAA's `wgrib2` executable. GFS and GEFS source layers produce minimal GRIB subsets; the rest of the codebase deals with typed meteorological values rather than GRIB internals.
279
+
280
+ Docker is therefore the reproducible distribution boundary: the production image pins Node.js and `wgrib2`; the npm package remains lightweight for environments that already provide the decoder.
281
+
282
+ ## Rate limiting
283
+
284
+ Every physical NOMADS request goes through `FileRateLimiter`. An atomically-created lock directory coordinates independent CLI and MCP processes on one machine. The completion timestamp is persisted separately, making the default 11-second cooldown apply across process lifetimes.
285
+
286
+ A future hosted multi-replica deployment can replace this implementation with Redis/Postgres behind the same limiter boundary without changing meteorological services.
287
+
288
+ ## Core does not own
289
+
290
+ - activity-specific weather scores;
291
+ - subjective forecast interpretation;
292
+ - calibrated probabilities unless an explicit calibration layer is designed and validated;
293
+ - domain-specific safety judgments.
294
+
295
+ Those belong to the consuming agent or a specialized application.
@@ -0,0 +1,66 @@
1
+ # Rich bounded-area statistics
2
+
3
+ The default `wfg area` / MCP `summarize_gfs_area` response remains the existing bounded min, max, and unweighted grid-point mean. Rich distribution statistics are opt-in and never return the raw GFS grid.
4
+
5
+ ## Optional outputs
6
+
7
+ A caller may request:
8
+
9
+ - percentiles in the range 0-100;
10
+ - fractions of defined grid cells greater than or equal to (`gte`) or less than or equal to (`lte`) one or more thresholds;
11
+ - representative min/max grid coordinates plus the number of cells tied at each extremum.
12
+
13
+ All calculations use **defined GFS grid cells only** and are unweighted by cell area, matching the existing area mean semantics.
14
+
15
+ Thresholds and returned percentile/extrema values use WFG's **normalized public output unit**. For example, a `temperature` / `temperature_2m` threshold of `15` means 15 °C, not 15 K.
16
+
17
+ Percentiles use linear interpolation over the sorted defined grid values with zero-based position `(p / 100) * (n - 1)`. The response names this method explicitly as `linear_interpolation_sorted_defined_grid_points`.
18
+
19
+ For extrema with ties, WFG returns the first deterministic grid point in wgrib2's output order together with `tiedGridPoints`; it does not imply that the extremum is spatially unique.
20
+
21
+ ## CLI
22
+
23
+ ```bash
24
+ wfg area \
25
+ --west 12 --east 18 \
26
+ --south 48 --north 51 \
27
+ --valid 2026-08-24T12:00:00Z \
28
+ --var temperature \
29
+ --level 850 \
30
+ --percentiles 10,50,90 \
31
+ --gte 15 \
32
+ --lte 0 \
33
+ --extrema-locations \
34
+ --json
35
+ ```
36
+
37
+ `--gte` and `--lte` are repeatable.
38
+
39
+ The same options work for raw non-isobaric fields such as `low_cloud_cover_average`. Non-isobaric vertical and temporal semantics remain exact.
40
+
41
+ ## MCP
42
+
43
+ `summarize_gfs_area` accepts optional structured fields:
44
+
45
+ ```json
46
+ {
47
+ "percentiles": [10, 50, 90],
48
+ "thresholds": [
49
+ { "operator": "gte", "value": 15 },
50
+ { "operator": "lte", "value": 0 }
51
+ ],
52
+ "includeExtremaLocations": true
53
+ }
54
+ ```
55
+
56
+ Up to 20 percentiles and 20 thresholds may be requested in one call. Duplicate percentiles are rejected.
57
+
58
+ ## Execution path
59
+
60
+ Ordinary area calls continue to use the fast local `wgrib2 -stats` path.
61
+
62
+ Only when rich statistics are requested does WFG materialize the already geographically bounded field with `wgrib2 -spread -`. The NOMADS request is still cropped first and the existing `maxGridPoints` guard still applies before any network access. For non-isobaric fields, WFG first requires one exact GRIB record matching variable, vertical semantics, and instantaneous/accumulation/average semantics.
63
+
64
+ The bounded grid values exist only inside the local calculation. WFG returns aggregates and optional extrema coordinates, not the grid itself.
65
+
66
+ The NOMADS cache and shared 11-second courtesy limiter are unchanged.
@@ -0,0 +1,88 @@
1
+ # Bounded area summaries
2
+
3
+ WFG exposes bounded geographic statistics without returning the raw GFS grid to the agent. The same primitive is available as CLI `wfg area` and MCP `summarize_gfs_area`.
4
+
5
+ The result contains the number of defined grid cells plus minimum, maximum, and an **unweighted GFS grid-point mean**. The default request guard is 50,000 estimated 0.25° grid points.
6
+
7
+ ## Pressure-level field
8
+
9
+ ```bash
10
+ wfg area \
11
+ --west 12 --east 18 \
12
+ --south 48 --north 51 \
13
+ --valid 2026-08-24T12:00:00Z \
14
+ --var temperature \
15
+ --level 850 \
16
+ --json
17
+ ```
18
+
19
+ Pressure summaries accept one raw pressure-level variable and one published GFS pressure surface. Derived pressure variables are intentionally not accepted by the area primitive yet.
20
+
21
+ ## Non-isobaric field
22
+
23
+ ```bash
24
+ wfg area \
25
+ --west 12 --east 18 \
26
+ --south 48 --north 51 \
27
+ --valid 2026-08-24T12:00:00Z \
28
+ --field low_cloud_cover_average \
29
+ --json
30
+ ```
31
+
32
+ A field request accepts one **raw** non-isobaric catalog field. This includes surface, height-above-ground, named-layer, named-level, column, instantaneous, accumulation, and forecast-window-average products.
33
+
34
+ The result includes:
35
+
36
+ - the catalog field ID
37
+ - exact public vertical semantics (`surface`, height AGL, named layer, or named level)
38
+ - exact temporal semantics
39
+ - normalized output field name and unit
40
+ - bounded-area statistics
41
+
42
+ For accumulation and average products, temporal metadata includes the GFS forecast-hour interval and absolute UTC start/end times.
43
+
44
+ ## Exact GRIB-record selection
45
+
46
+ NOMADS Grib Filter selects variables and vertical levels, but one filtered response can still contain more than one temporal product for the same code and level. For example, low cloud cover can have both an instantaneous record and a forecast-window-average record.
47
+
48
+ WFG therefore does not run statistics blindly over the filtered file. For non-isobaric area requests it first inspects the local `wgrib2 -s` inventory and requires exactly one record matching:
49
+
50
+ 1. GFS variable code
51
+ 2. exact GRIB vertical level text
52
+ 3. temporal semantics: instantaneous, accumulation, or average
53
+
54
+ Only that record is passed to `wgrib2 -stats`. Missing and ambiguous matches fail explicitly.
55
+
56
+ This is the same fail-closed philosophy used by point/profile field extraction: an instantaneous request must never silently become an average request simply because both share a variable code and named layer.
57
+
58
+ ## Unit normalization
59
+
60
+ Statistics are normalized to the field catalog's public output unit after aggregation.
61
+
62
+ Temperature records published in kelvin and exposed by WFG in degrees Celsius are shifted by 273.15 for mean/min/max. Numerically equivalent conversions such as precipitation `kg/m²` to liquid-water-equivalent `mm` preserve the numeric values.
63
+
64
+ ## Why derived wind is not supported yet
65
+
66
+ Derived `wind_10m`, `wind_20m`, and similar fields are intentionally rejected for area summaries.
67
+
68
+ Computing an area mean U and mean V and then deriving wind speed/direction is **not** equivalent to deriving wind at every grid cell and then aggregating those derived values. The latter requires a grid-aware derived-statistics path rather than the scalar `wgrib2 -stats` adapter used here.
69
+
70
+ Until that path exists, WFG fails rather than returning a mathematically misleading area wind statistic.
71
+
72
+ ## Data access and pacing
73
+
74
+ Area summaries use NOAA NOMADS because Grib Filter can crop the geographic region before transfer. Every physical download uses the same `NomadsCache` and cross-process `FileRateLimiter` as other NOMADS access.
75
+
76
+ The default cooldown remains **11 seconds after a request completes**, deliberately conservative relative to NOAA's 10-second scripted-request guidance. Cache hits do not consume a limiter slot.
77
+
78
+ ## Geographic limits
79
+
80
+ Current area constraints:
81
+
82
+ - bbox coordinates must be within normal latitude/longitude bounds
83
+ - west must be strictly less than east
84
+ - south must be strictly less than north
85
+ - antimeridian-crossing boxes are not supported yet
86
+ - the default estimated-grid guard is 50,000 cells, raiseable explicitly within the schema maximum
87
+
88
+ The mean is an unweighted grid-point mean, not an area-weighted spherical mean.
@@ -0,0 +1,61 @@
1
+ # GFS catalog search
2
+
3
+ WFG keeps the complete atmospheric catalog available through CLI `wfg catalog` and MCP `get_gfs_catalog`, but agents often need only a small subset of that surface. Catalog search provides a bounded, deterministic discovery path without changing the underlying catalog definitions.
4
+
5
+ ## CLI
6
+
7
+ With no search/filter options, `wfg catalog` keeps the existing full-catalog behavior.
8
+
9
+ ```bash
10
+ wfg catalog
11
+ wfg catalog --json
12
+ ```
13
+
14
+ Supplying any search/filter option switches the command to compact search/browse mode:
15
+
16
+ ```bash
17
+ wfg catalog \
18
+ --search "low cloud cover" \
19
+ --sections fields \
20
+ --temporal average \
21
+ --limit 10 \
22
+ --json
23
+ ```
24
+
25
+ Available filters:
26
+
27
+ - `--search <text>` — tokenized case-insensitive search across IDs, descriptions, dependencies, output names/descriptions/units, GFS codes, source units, and vertical/temporal semantics.
28
+ - `--sections <list>` — comma-separated subset of `variables`, `fields`, `layer_diagnostics`, `profile_diagnostics`, `parcel_definitions`.
29
+ - `--classification <raw|derived>` — raw NOAA-backed definitions versus deterministic derived definitions/diagnostics.
30
+ - `--temporal <instantaneous|accumulation|average>` — exact non-isobaric temporal semantics.
31
+ - `--limit <number>` — returned matches, 1-100. Compact search defaults to 30.
32
+
33
+ ## MCP
34
+
35
+ MCP exposes the same shared search core as `search_gfs_catalog`. `get_gfs_catalog` remains unchanged for clients that want the full catalog.
36
+
37
+ Example structured input:
38
+
39
+ ```json
40
+ {
41
+ "search": "wet bulb",
42
+ "sections": ["variables"],
43
+ "classification": "derived",
44
+ "limit": 10
45
+ }
46
+ ```
47
+
48
+ ## Ranking and matching
49
+
50
+ Search normalizes case, whitespace, underscores, hyphens, and diacritics. Every search token must occur somewhere in the searchable representation of an entry; WFG does not broaden a zero-result query.
51
+
52
+ Ranking is deterministic. Exact IDs receive the strongest score, followed by ID prefixes/substrings and then token matches across IDs, descriptions, and structured metadata. Ties are resolved by stable section order and then ID.
53
+
54
+ The result includes:
55
+
56
+ - the resolved query and filters;
57
+ - `totalMatches` before limiting;
58
+ - `truncated` when more matches exist;
59
+ - flat matches with section, ID, raw/derived classification, concrete kind, description, vertical semantics, optional temporal semantics/GFS metadata/dependencies, normalized outputs, and score.
60
+
61
+ Search is local and deterministic. It performs no NOAA request and does not touch the NOMADS courtesy limiter.
@@ -0,0 +1,146 @@
1
+ # Diagnostic time series
2
+
3
+ WFG can evaluate its existing deterministic diagnostics across the native NOAA GFS 0.25° forecast timeline. This is a composition layer over the single-time diagnostic services, not a second meteorology implementation.
4
+
5
+ ## Why this exists
6
+
7
+ Single-time diagnostic tools are ideal for inspecting one forecast instant in detail. Agents often need a different question shape:
8
+
9
+ - when does CAPE peak?
10
+ - how does LCL evolve through the afternoon?
11
+ - when do sampled freezing-level crossings move upward or downward?
12
+ - when do sampled inversion layers appear or disappear?
13
+ - how does lapse rate or vector wind shear evolve between two fixed pressure surfaces?
14
+
15
+ `diagnostic-timeseries` / `get_gfs_diagnostic_timeseries` answer those questions in one bounded request.
16
+
17
+ ## One diagnostic family per query
18
+
19
+ The `diagnostic` selection is a discriminated union with three kinds.
20
+
21
+ ### Layer
22
+
23
+ ```json
24
+ {
25
+ "kind": "layer",
26
+ "lowerPressureHpa": 850,
27
+ "upperPressureHpa": 700,
28
+ "diagnostics": ["temperature_lapse_rate", "wind_shear"]
29
+ }
30
+ ```
31
+
32
+ The pressure surfaces stay fixed for the full series.
33
+
34
+ ### Whole profile
35
+
36
+ ```json
37
+ {
38
+ "kind": "profile",
39
+ "pressureLevelsHpa": [1000, 925, 850, 700, 500],
40
+ "diagnostics": ["freezing_level_crossings", "temperature_inversion_layers"]
41
+ }
42
+ ```
43
+
44
+ The supplied published pressure levels define vertical sampling resolution at every time step. WFG does not imply structure between levels the caller did not request.
45
+
46
+ ### Parcel
47
+
48
+ ```json
49
+ {
50
+ "kind": "parcel",
51
+ "pressureLevelsHpa": [1000, 975, 950, 925, 900, 850, 800, 750, 700, 650, 600, 550, 500, 450, 400, 350, 300, 250, 200],
52
+ "parcel": "surface_2m"
53
+ }
54
+ ```
55
+
56
+ The explicit parcel definition and sampled environmental pressure levels stay fixed for the full series.
57
+
58
+ ## Time semantics
59
+
60
+ The caller supplies an inclusive `startTime` and `endTime`. WFG returns every native GFS output falling inside that range:
61
+
62
+ - hourly through forecast hour 120;
63
+ - every three hours after forecast hour 120 through f384.
64
+
65
+ `maxSteps` bounds the response before any diagnostic data access begins. The default is the same bounded value used by ordinary point time series.
66
+
67
+ ## Run selection
68
+
69
+ The entire series uses one GFS model cycle.
70
+
71
+ - `latest` resolves the newest cycle whose already-published data can satisfy the complete requested time range **and the exact raw dependencies required by the chosen diagnostic**;
72
+ - `latest_complete` resolves the newest cycle published through f384;
73
+ - an explicit 00Z/06Z/12Z/18Z run timestamp is reproducible.
74
+
75
+ After resolution, the explicit run timestamp is passed to every underlying single-time diagnostic call. The model cycle therefore cannot drift halfway through a series.
76
+
77
+ ## Data access
78
+
79
+ S3 is the default because diagnostic time series require several forecast files and NOAA AWS Open Data is the efficient multi-time path. WFG selects only the required GRIB messages from each forecast file and uses the normal immutable slice cache.
80
+
81
+ `source: "nomads"` remains available explicitly. Every physical NOMADS request still passes through the shared file-backed 11-second courtesy limiter; diagnostic time series have no bypass.
82
+
83
+ ## Compact parcel steps
84
+
85
+ The single-time `parcel` / `get_gfs_parcel_diagnostics` surface returns the complete parcel path and raw environmental profile for auditability.
86
+
87
+ Repeating a full parcel path at every forecast step can make a time-series response unnecessarily large. Parcel time-series steps therefore keep:
88
+
89
+ - parcel starting state;
90
+ - LCL;
91
+ - optional LFC and EL;
92
+ - CAPE and CIN;
93
+ - CAPE/CIN top semantics;
94
+
95
+ but omit the repeated `parcelPath` array. Use the single-time parcel tool when a particular step needs the full audit trail.
96
+
97
+ ## CLI
98
+
99
+ Layer diagnostics:
100
+
101
+ ```bash
102
+ wfg diagnostic-timeseries \
103
+ --kind layer \
104
+ --lat 50.08 --lon 14.43 \
105
+ --start 2026-08-24T09:00:00Z \
106
+ --end 2026-08-24T18:00:00Z \
107
+ --lower 850 --upper 700 \
108
+ --diagnostics temperature_lapse_rate,wind_shear \
109
+ --json
110
+ ```
111
+
112
+ Parcel diagnostics:
113
+
114
+ ```bash
115
+ wfg diagnostic-timeseries \
116
+ --kind parcel \
117
+ --lat 50.08 --lon 14.43 \
118
+ --start 2026-08-24T09:00:00Z \
119
+ --end 2026-08-24T18:00:00Z \
120
+ --levels 1000,975,950,925,900,850,800,750,700,650,600,550,500,450,400,350,300,250,200 \
121
+ --parcel surface_2m \
122
+ --json
123
+ ```
124
+
125
+ ## MCP
126
+
127
+ Tool: `get_gfs_diagnostic_timeseries`
128
+
129
+ Example input:
130
+
131
+ ```json
132
+ {
133
+ "latitude": 50.08,
134
+ "longitude": 14.43,
135
+ "run": "latest",
136
+ "startTime": "2026-08-24T09:00:00Z",
137
+ "endTime": "2026-08-24T18:00:00Z",
138
+ "diagnostic": {
139
+ "kind": "profile",
140
+ "pressureLevelsHpa": [1000, 925, 850, 700, 500],
141
+ "diagnostics": ["freezing_level_crossings", "temperature_inversion_layers"]
142
+ }
143
+ }
144
+ ```
145
+
146
+ The CLI and MCP adapters call the same core service and validate the same result contract.