spectro-kernel 0.6.1__tar.gz → 0.6.2__tar.gz

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 (319) hide show
  1. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/.gitignore +5 -0
  2. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/CHANGELOG.md +43 -0
  3. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/PKG-INFO +8 -4
  4. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/README.md +2 -1
  5. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/algorithms.md +1 -1
  6. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/contributing.md +3 -3
  7. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/getting-started.md +1 -1
  8. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/index.md +1 -1
  9. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/reference.md +1 -1
  10. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/discover-the-catalogue.md +1 -1
  11. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/first-spectrum.md +1 -1
  12. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/long-slit-reduction.md +1 -1
  13. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/mcp.md +1 -0
  14. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/why.md +1 -1
  15. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/pyproject.toml +7 -4
  16. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/version.py +1 -1
  17. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/__main__.py +43 -2
  18. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_http_entry.py +67 -2
  19. spectro_kernel-0.6.2/tools/docs/__init__.py +6 -0
  20. spectro_kernel-0.6.2/tools/docs/build.py +82 -0
  21. spectro_kernel-0.6.2/tools/docs/generate.py +25 -0
  22. spectro_kernel-0.6.2/tools/docs/generate_catalogue.py +482 -0
  23. spectro_kernel-0.6.2/tools/docs/generate_llms.py +134 -0
  24. spectro_kernel-0.6.2/tools/docs/netlify.toml +20 -0
  25. spectro_kernel-0.6.1/docs/gen_catalogue.py +0 -124
  26. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/LICENSE +0 -0
  27. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/architecture.md +0 -0
  28. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/data-types.md +0 -0
  29. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/pipelines.md +0 -0
  30. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/bess-dashboard.md +0 -0
  31. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/multi-star-viewer.md +0 -0
  32. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/web-playground.md +0 -0
  33. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  34. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  35. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  36. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  37. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  38. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  39. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  40. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  41. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  42. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  43. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  44. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  45. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  46. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  47. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  48. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  49. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  50. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  51. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  52. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  53. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  54. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  55. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  56. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/aurora-line-monitor.md +0 -0
  57. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/be-star-variability.md +0 -0
  58. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  59. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  60. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  61. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  62. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/your-first-sb2.md +0 -0
  63. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/add-an-algorithm.md +0 -0
  64. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/analyse-a-spectrum.md +0 -0
  65. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/cli.md +0 -0
  66. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/library.md +0 -0
  67. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/__init__.py +0 -0
  68. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/__init__.py +0 -0
  69. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/easyspec.py +0 -0
  70. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/__init__.py +0 -0
  71. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/_common.py +0 -0
  72. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  73. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  74. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
  75. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +0 -0
  76. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  77. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
  78. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
  79. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
  80. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
  81. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +0 -0
  82. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +0 -0
  83. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  84. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
  85. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
  86. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
  87. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
  88. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
  89. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_region.py +0 -0
  90. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
  91. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  92. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
  93. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  94. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
  95. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
  96. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
  97. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
  98. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
  99. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +0 -0
  100. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
  101. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  102. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +0 -0
  103. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
  104. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
  105. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
  106. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +0 -0
  107. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_remote.py +0 -0
  108. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
  109. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +0 -0
  110. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  111. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
  112. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  113. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  114. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
  115. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
  116. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  117. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  118. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/boxcar.py +0 -0
  119. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/detect_trace.py +0 -0
  120. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
  121. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/optimal.py +0 -0
  122. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
  123. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  124. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  125. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
  126. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
  127. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
  128. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  129. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
  130. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +0 -0
  131. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  132. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_line_flux.py +0 -0
  133. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
  134. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  135. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
  136. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/detect.py +0 -0
  137. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
  138. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
  139. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
  140. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
  141. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/vr_ratio.py +0 -0
  142. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
  143. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +0 -0
  144. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +0 -0
  145. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +0 -0
  146. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  147. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
  148. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
  149. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
  150. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
  151. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
  152. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  153. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
  154. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
  155. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
  156. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
  157. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_combine.py +0 -0
  158. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  159. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +0 -0
  160. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
  161. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
  162. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
  163. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
  164. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
  165. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
  166. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
  167. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
  168. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_combine.py +0 -0
  169. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
  170. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
  171. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
  172. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
  173. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  174. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
  175. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
  176. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
  177. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
  178. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/redshift_lines.py +0 -0
  179. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  180. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +0 -0
  181. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
  182. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
  183. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
  184. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  185. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
  186. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
  187. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  188. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
  189. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
  190. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  191. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
  192. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +0 -0
  193. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  194. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  195. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
  196. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
  197. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  198. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  199. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  200. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
  201. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
  202. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  203. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +0 -0
  204. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
  205. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
  206. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
  207. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +0 -0
  208. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
  209. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +0 -0
  210. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/base.py +0 -0
  211. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/cli.py +0 -0
  212. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/embeddings.py +0 -0
  213. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/errors.py +0 -0
  214. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/__init__.py +0 -0
  215. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/ascii.py +0 -0
  216. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/fits.py +0 -0
  217. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/votable.py +0 -0
  218. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/pipeline.py +0 -0
  219. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/__init__.py +0 -0
  220. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  221. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  222. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  223. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  224. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  225. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  226. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  227. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/loader.py +0 -0
  228. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/py.typed +0 -0
  229. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/registry.py +0 -0
  230. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/__init__.py +0 -0
  231. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/catalog.py +0 -0
  232. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/context.py +0 -0
  233. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/enums.py +0 -0
  234. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/history.py +0 -0
  235. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/image.py +0 -0
  236. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/line.py +0 -0
  237. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/spectrum.py +0 -0
  238. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/timeseries.py +0 -0
  239. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/url_safety.py +0 -0
  240. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/__init__.py +0 -0
  241. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/auth.py +0 -0
  242. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/auto_tools.py +0 -0
  243. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/observability.py +0 -0
  244. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/py.typed +0 -0
  245. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/server.py +0 -0
  246. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/session.py +0 -0
  247. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/url_safety.py +0 -0
  248. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/conftest.py +0 -0
  249. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/conftest.py +0 -0
  250. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_known_answers.py +0 -0
  251. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_sun.py +0 -0
  252. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_vega.py +0 -0
  253. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_aperture_photometry.py +0 -0
  254. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_barycentric_bjd.py +0 -0
  255. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_base.py +0 -0
  256. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_catalog_queries_mocked.py +0 -0
  257. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_classify_template_chi2.py +0 -0
  258. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_cli.py +0 -0
  259. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_combine.py +0 -0
  260. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_combine_arithmetic.py +0 -0
  261. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_continuum.py +0 -0
  262. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_corrections.py +0 -0
  263. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_cross_correlate_extension.py +0 -0
  264. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_dark_flat_combine.py +0 -0
  265. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_denoise_2d.py +0 -0
  266. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_detect_lines_blind.py +0 -0
  267. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_detect_trace.py +0 -0
  268. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_disentangle_sb2.py +0 -0
  269. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_doppler_tomogram.py +0 -0
  270. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_apply_staging.py +0 -0
  271. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_sessions.py +0 -0
  272. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_wrappers.py +0 -0
  273. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding.py +0 -0
  274. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_pretrained.py +0 -0
  275. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_remote_lick.py +0 -0
  276. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_tier1.py +0 -0
  277. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_export_fits_bess.py +0 -0
  278. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_export_hdf5.py +0 -0
  279. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_boxcar.py +0 -0
  280. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_optimal.py +0 -0
  281. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_spectrum_sum.py +0 -0
  282. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_flux_calibrate_easyspec.py +0 -0
  283. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_geometry_corrections.py +0 -0
  284. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_io.py +0 -0
  285. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_lamp_atlas_match.py +0 -0
  286. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_line_profiles.py +0 -0
  287. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_lines.py +0 -0
  288. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp.py +0 -0
  289. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_param_guard.py +0 -0
  290. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_production.py +0 -0
  291. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_security.py +0 -0
  292. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_tools_extra.py +0 -0
  293. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_measure_arc_geometry.py +0 -0
  294. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_misc_algorithms.py +0 -0
  295. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_nebular_diagnostics.py +0 -0
  296. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_no_circular_imports.py +0 -0
  297. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_normalize_to_region.py +0 -0
  298. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_output_produces_contract.py +0 -0
  299. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_pipeline.py +0 -0
  300. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_quality.py +0 -0
  301. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_read_sdss.py +0 -0
  302. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_redshift_lines.py +0 -0
  303. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_registry.py +0 -0
  304. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_reidentify.py +0 -0
  305. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_resample_flux_conserving.py +0 -0
  306. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_response_from_standard.py +0 -0
  307. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_rotation_curve.py +0 -0
  308. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_rv_precision_bouchy.py +0 -0
  309. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_sky_lateral_bands.py +0 -0
  310. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_smoothing.py +0 -0
  311. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_timeseries.py +0 -0
  312. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_transforms.py +0 -0
  313. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_types.py +0 -0
  314. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_url_safety_kernel.py +0 -0
  315. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_validate_bess_header.py +0 -0
  316. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_viz.py +0 -0
  317. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_vr_ratio.py +0 -0
  318. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
  319. {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_solar.py +0 -0
@@ -164,6 +164,11 @@ venv.bak/
164
164
  # Rope project settings
165
165
  .ropeproject
166
166
 
167
+ # documentation - generated by tools/docs/generate.py
168
+ docs/algorithms/
169
+ docs/ai/
170
+ docs/llms.txt
171
+
167
172
  # mkdocs documentation
168
173
  /site
169
174
 
@@ -6,6 +6,49 @@ Until `1.0.0` the public API may change between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.6.2] — 2026-09-19
10
+
11
+ A documentation and web release : the docs move to a generated,
12
+ agent-readable site at https://docs.spectrokernel.io/, the landing page is
13
+ rebuilt around the open MCP server, and the server gains CORS so the web
14
+ playground can use it. No algorithm changes.
15
+
16
+ ### MCP server
17
+
18
+ - CORS for browser clients in HTTP mode : `SPECTRO_MCP_CORS_ORIGINS`
19
+ (default : the spectrokernel.io sites ; `*` or `none`), with the
20
+ `Mcp-Session-Id` header exposed, so the web playground on the landing
21
+ page can talk to the public server. Pre-flights are answered before the
22
+ API-key check.
23
+
24
+ ### Documentation — new build pipeline and hosting (no package change)
25
+
26
+ - The site is now built with **Zensical** (the Material for MkDocs team's
27
+ successor engine, reading the unchanged `mkdocs.yml`) and published at
28
+ **https://docs.spectrokernel.io/** by Netlify, which builds it from the
29
+ repository (base directory `tools/docs`, see `tools/docs/netlify.toml`) ;
30
+ pull requests get a deploy preview and GitHub Actions keeps a strict build
31
+ check. `mkdocs build` keeps working as a fallback.
32
+ - The algorithm catalogue is generated *before* the build by
33
+ `tools/docs/generate_catalogue.py` (Zensical runs no MkDocs plugin such as
34
+ `gen-files`) : one page per algorithm under `/algorithms/<category>/<name>/`
35
+ (summary, backend, version, context reads/writes, parameter table, Python /
36
+ CLI / MCP call examples, references, related algorithms), a category index,
37
+ and a navigation tree consumed by `literate-nav`.
38
+ - Machine-readable layer for agents and tooling : `/ai/algorithms.json`
39
+ (validated at build time against `/ai/schemas/algorithms.schema.json`,
40
+ schema version 1, stamped with the package version), a curated `/llms.txt`
41
+ derived from the navigation, and the Markdown source of every page served
42
+ next to its HTML (`<page>/index.md`). Netlify headers set the content types
43
+ and CORS on `/ai/*`.
44
+ - `tools/docs/build.py` reproduces the CI build locally (generators + strict
45
+ build + post-processing) ; `tools/docs/generate.py && zensical serve` for
46
+ live preview. Generated files (`docs/algorithms/`, `docs/ai/`,
47
+ `docs/llms.txt`) are git-ignored. `.readthedocs.yaml` and
48
+ `docs/gen_catalogue.py` are removed ; the `docs` extra now installs
49
+ `zensical`, `mkdocstrings[python]`, `mkdocs-material`,
50
+ `mkdocs-literate-nav`, `jsonschema`.
51
+
9
52
  ## [0.6.1] — 2026-09-19
10
53
 
11
54
  A correctness and hardening release driven by a full audit of the
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: spectro-kernel
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
5
5
  Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
6
- Project-URL: Documentation, https://matthieulel.github.io/spectro-kernel/
6
+ Project-URL: Documentation, https://docs.spectrokernel.io/
7
7
  Project-URL: Source, https://github.com/matthieulel/spectro-kernel
8
8
  Project-URL: Issues, https://github.com/matthieulel/spectro-kernel/issues
9
9
  Project-URL: Changelog, https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md
@@ -54,9 +54,12 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
54
54
  Requires-Dist: pytest>=8.0; extra == 'dev'
55
55
  Requires-Dist: ruff>=0.5; extra == 'dev'
56
56
  Provides-Extra: docs
57
- Requires-Dist: mkdocs-gen-files>=0.5; extra == 'docs'
57
+ Requires-Dist: jsonschema>=4.0; extra == 'docs'
58
+ Requires-Dist: mkdocs-literate-nav>=0.6; extra == 'docs'
58
59
  Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
59
60
  Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
61
+ Requires-Dist: pyyaml>=6.0; extra == 'docs'
62
+ Requires-Dist: zensical>=0.0.62; extra == 'docs'
60
63
  Provides-Extra: embedding-ml
61
64
  Requires-Dist: torch>=2.2; extra == 'embedding-ml'
62
65
  Provides-Extra: embedding-remote
@@ -197,6 +200,7 @@ by environment variable (none of these affect stdio mode) :
197
200
  | `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
198
201
  | `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
199
202
  | `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
203
+ | `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
200
204
  | `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
201
205
  | `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
202
206
  | `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
@@ -234,7 +238,7 @@ from the registry. Build it locally with:
234
238
 
235
239
  ```bash
236
240
  uv pip install -e ".[docs,all]"
237
- mkdocs serve # live preview at http://127.0.0.1:8000
241
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
238
242
  ```
239
243
 
240
244
  Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
@@ -111,6 +111,7 @@ by environment variable (none of these affect stdio mode) :
111
111
  | `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
112
112
  | `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
113
113
  | `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
114
+ | `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
114
115
  | `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
115
116
  | `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
116
117
  | `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
@@ -148,7 +149,7 @@ from the registry. Build it locally with:
148
149
 
149
150
  ```bash
150
151
  uv pip install -e ".[docs,all]"
151
- mkdocs serve # live preview at http://127.0.0.1:8000
152
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
152
153
  ```
153
154
 
154
155
  Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
@@ -103,7 +103,7 @@ principle](../why.md#the-principle-we-do-not-reinvent). Every algorithm declares
103
103
  - its **`references`** - the literature for the method, or the wrapped library.
104
104
 
105
105
  Both are visible in `spectro describe <name>`, in the
106
- [catalogue](../catalogue.md), and in every MCP tool description. Nothing is a black box:
106
+ [catalogue](../algorithms/index.md), and in every MCP tool description. Nothing is a black box:
107
107
  you can always see what an algorithm stands on and where the method comes from.
108
108
 
109
109
  ## Naming conventions
@@ -57,10 +57,10 @@ CI runs the same checks on every pull request; keep `main` green.
57
57
 
58
58
  ```bash
59
59
  uv pip install -e ".[docs,all]"
60
- mkdocs serve # live preview at http://127.0.0.1:8000
61
- mkdocs build # static build into site/
60
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
61
+ python tools/docs/build.py # what CI runs: generators + strict build into site/
62
62
  ```
63
63
 
64
- The [algorithm catalogue](catalogue.md) page is *generated* from the registry at build
64
+ The [algorithm catalogue](algorithms/index.md) page is *generated* from the registry at build
65
65
  time - you never edit it by hand. Add a docstring and metadata to your algorithm and the
66
66
  catalogue page documents it automatically.
@@ -114,4 +114,4 @@ The agent now sees every algorithm as a tool. See [As an MCP server](usage/mcp.m
114
114
  catalogue](tutorials/discover-the-catalogue.md).
115
115
  - Understand the moving parts → [Concepts: architecture](concepts/architecture.md).
116
116
  - A guided, end-to-end analysis → [Tutorial: analyse a spectrum](tutorials/analyse-a-spectrum.md).
117
- - Browse everything available → [Algorithm catalogue](catalogue.md).
117
+ - Browse everything available → [Algorithm catalogue](algorithms/index.md).
@@ -49,7 +49,7 @@ through MCP, you can do from a terminal, and vice-versa.
49
49
  your first analysis in five minutes.
50
50
  - :material-sitemap: **[Concepts](concepts/architecture.md)** - the architecture,
51
51
  the data types, the registry, the pipelines.
52
- - :material-book-open-variant: **[Algorithm catalogue](catalogue.md)** - every
52
+ - :material-book-open-variant: **[Algorithm catalogue](algorithms/index.md)** - every
53
53
  algorithm, auto-generated from the code.
54
54
  - :material-school: **[Tutorials](tutorials/first-spectrum.md)** - from *"I captured a
55
55
  spectrum of a star, now what?"* all the way to writing your own algorithm.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This page is generated from the source docstrings by
4
4
  [mkdocstrings](https://mkdocstrings.github.io/). It documents the public Python API; for
5
- the *algorithms* themselves see the [catalogue](catalogue.md).
5
+ the *algorithms* themselves see the [catalogue](algorithms/index.md).
6
6
 
7
7
  ## Data types
8
8
 
@@ -106,7 +106,7 @@ Over [MCP](../usage/mcp.md), the discovery tools are `list_algorithms` and
106
106
 
107
107
  ## The full reference
108
108
 
109
- The [Algorithm catalogue](../catalogue.md) page lists **every** algorithm with its
109
+ The [Algorithm catalogue](../algorithms/index.md) page lists **every** algorithm with its
110
110
  parameters, provenance and references, grouped by category. It is generated directly
111
111
  from the registry, so it is always in sync with the version you have installed.
112
112
 
@@ -149,4 +149,4 @@ checked, continuum normalised, lines identified. That is a genuine scientific re
149
149
  operation available, and how to inspect each one.
150
150
  - **A fuller, guided analysis** → [Analyse a spectrum](analyse-a-spectrum.md).
151
151
  - **Periodic targets** (Be stars, eclipsing binaries) → the `lomb_scargle` and
152
- `phase_fold` algorithms in the [catalogue](../catalogue.md).
152
+ `phase_fold` algorithms in the [catalogue](../algorithms/index.md).
@@ -167,5 +167,5 @@ Every algorithm above follows the same kernel contract:
167
167
  `ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
168
168
  per-step `metrics` are JSON-serialisable for later auditing.
169
169
 
170
- See [Algorithm catalogue](../catalogue.md) for the full parameter and
170
+ See [Algorithm catalogue](../algorithms/index.md) for the full parameter and
171
171
  reference list of each brick.
@@ -136,6 +136,7 @@ bricks are now fetched through the same guard as `load_spectrum`.
136
136
  | `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
137
137
  | `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window limit per API key (per client IP when auth is off). |
138
138
  | `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For`. |
139
+ | `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
139
140
  | `SPECTRO_MCP_MAX_SESSIONS` | `200` | Live in-memory sessions before LRU eviction. |
140
141
  | `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Largest downloaded FITS accepted (data elements); `0` disables. |
141
142
  | `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget per URL download. |
@@ -89,7 +89,7 @@ Every algorithm makes this visible through its `backend` field:
89
89
  | `plotly` | Renders figures with Plotly. |
90
90
 
91
91
  …and through its `references` field, which cites the literature. You can see both in
92
- `spectro describe <name>`, in the [catalogue](catalogue.md), and in every MCP tool
92
+ `spectro describe <name>`, in the [catalogue](algorithms/index.md), and in every MCP tool
93
93
  description.
94
94
 
95
95
  ### Examples
@@ -64,9 +64,12 @@ dev = [
64
64
  "ruff>=0.5",
65
65
  ]
66
66
  docs = [
67
- "mkdocs-material>=9.5",
68
- "mkdocstrings[python]>=0.26",
69
- "mkdocs-gen-files>=0.5",
67
+ "zensical>=0.0.62", # the site engine (Material for MkDocs successor)
68
+ "mkdocstrings[python]>=0.26", # Python API reference (Griffe), used natively by Zensical
69
+ "mkdocs-material>=9.5", # fallback engine for the same mkdocs.yml
70
+ "mkdocs-literate-nav>=0.6", # fallback engine's nav plugin (Zensical has it built in)
71
+ "jsonschema>=4.0", # validates docs/ai/algorithms.json at build time
72
+ "pyyaml>=6.0",
70
73
  ]
71
74
  all = [
72
75
  "spectro-kernel[catalogs,viz,storage,reduction,mcp,monitoring,embedding-wavelets,embedding-remote]",
@@ -81,7 +84,7 @@ spectro-mcp = "spectro_mcp.__main__:main"
81
84
 
82
85
  [project.urls]
83
86
  Homepage = "https://github.com/matthieulel/spectro-kernel"
84
- Documentation = "https://matthieulel.github.io/spectro-kernel/"
87
+ Documentation = "https://docs.spectrokernel.io/"
85
88
  Source = "https://github.com/matthieulel/spectro-kernel"
86
89
  Issues = "https://github.com/matthieulel/spectro-kernel/issues"
87
90
  Changelog = "https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md"
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for the package version."""
2
2
 
3
- __version__ = "0.6.1"
3
+ __version__ = "0.6.2"
@@ -80,8 +80,10 @@ def _http_api_key() -> str | None:
80
80
  return api_key
81
81
  if _truthy("SPECTRO_MCP_ALLOW_ANONYMOUS"):
82
82
  print(
83
- "spectro-mcp: WARNING — SPECTRO_MCP_ALLOW_ANONYMOUS=1: the HTTP server "
84
- "accepts every request without an API key. Do not expose it publicly.",
83
+ "spectro-mcp: open mode (SPECTRO_MCP_ALLOW_ANONYMOUS=1): no API key "
84
+ "required. Local paths are refused, URLs go through the SSRF guard, "
85
+ "sessions / downloads / memory are capped and requests are rate-limited "
86
+ "per client IP. Set SPECTRO_MCP_API_KEY to close the server.",
85
87
  file=sys.stderr,
86
88
  )
87
89
  return None
@@ -96,6 +98,28 @@ def _http_api_key() -> str | None:
96
98
  raise SystemExit(2)
97
99
 
98
100
 
101
+ DEFAULT_CORS_ORIGINS = (
102
+ "https://spectrokernel.io",
103
+ "https://www.spectrokernel.io",
104
+ "https://docs.spectrokernel.io",
105
+ )
106
+
107
+
108
+ def _cors_origins() -> list[str]:
109
+ """Origins allowed to call the HTTP server from a browser.
110
+
111
+ ``SPECTRO_MCP_CORS_ORIGINS`` is a comma-separated list (``*`` for any
112
+ origin, ``none`` to disable) ; unset means the spectrokernel.io sites.
113
+ """
114
+ raw = os.environ.get("SPECTRO_MCP_CORS_ORIGINS")
115
+ if raw is None:
116
+ return list(DEFAULT_CORS_ORIGINS)
117
+ items = [o.strip() for o in raw.split(",") if o.strip()]
118
+ if not items or items == ["none"]:
119
+ return []
120
+ return items
121
+
122
+
99
123
  def _run_http(server, args, api_key: str | None) -> int:
100
124
  """Serve over HTTP, wiring API-key auth + optional rate-limit middleware.
101
125
 
@@ -124,6 +148,23 @@ def _run_http(server, args, api_key: str | None) -> int:
124
148
  carriers += ", ?api_key= query parameter (scrubbed from access logs)"
125
149
  print(f"spectro-mcp: API-key auth enabled ({carriers}).", file=sys.stderr)
126
150
 
151
+ # Browser clients (the playground on spectrokernel.io) need CORS ; added
152
+ # last so it is the outermost layer and OPTIONS pre-flights are answered
153
+ # before the API-key check (browsers send no custom header on a pre-flight).
154
+ origins = _cors_origins()
155
+ if origins:
156
+ from starlette.middleware.cors import CORSMiddleware
157
+
158
+ app.add_middleware(
159
+ CORSMiddleware,
160
+ allow_origins=origins,
161
+ allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
162
+ allow_headers=["*"],
163
+ expose_headers=["Mcp-Session-Id"],
164
+ max_age=600,
165
+ )
166
+ print(f"spectro-mcp: CORS enabled for {', '.join(origins)}.", file=sys.stderr)
167
+
127
168
  uvicorn_kwargs: dict = {"log_config": None}
128
169
  if _truthy("SPECTRO_MCP_TRUSTED_PROXY"):
129
170
  # Behind a load balancer (DigitalOcean App Platform, nginx, …) the
@@ -97,9 +97,12 @@ def test_http_with_key_puts_auth_outermost(fake_boot, clean_env):
97
97
  clean_env.setenv("SPECTRO_MCP_API_KEY", "secret")
98
98
  clean_env.setenv("SPECTRO_MCP_RATE_PER_MINUTE", "30")
99
99
  assert entry.main(["--http"]) == 0
100
+ from starlette.middleware.cors import CORSMiddleware
101
+
100
102
  classes = [m.cls for m in fake_boot["app"].user_middleware]
101
- # Starlette: index 0 is the outermost layer.
102
- assert classes == [ApiKeyMiddleware, RateLimitMiddleware]
103
+ # Starlette: index 0 is the outermost layer. CORS sits outside auth so
104
+ # browser pre-flights are answered; auth stays outside the rate limiter.
105
+ assert classes == [CORSMiddleware, ApiKeyMiddleware, RateLimitMiddleware]
103
106
  assert fake_boot["uvicorn_kwargs"].get("proxy_headers") is None
104
107
  assert fake_boot["build_kwargs"]["mask_error_details"] is True
105
108
 
@@ -137,3 +140,65 @@ def test_stdio_mode_needs_no_key(clean_env, monkeypatch):
137
140
  assert record["ran"] is True
138
141
  assert "mask_error_details" not in record["build_kwargs"]
139
142
  assert os.environ.get("SPECTRO_MCP_DENY_LOCAL_PATHS") is None
143
+
144
+
145
+ # --- CORS ------------------------------------------------------------------ #
146
+ def test_cors_origins_default_and_overrides(monkeypatch):
147
+ from spectro_mcp.__main__ import DEFAULT_CORS_ORIGINS, _cors_origins
148
+
149
+ monkeypatch.delenv("SPECTRO_MCP_CORS_ORIGINS", raising=False)
150
+ assert _cors_origins() == list(DEFAULT_CORS_ORIGINS)
151
+ monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "https://a.example, https://b.example")
152
+ assert _cors_origins() == ["https://a.example", "https://b.example"]
153
+ monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "*")
154
+ assert _cors_origins() == ["*"]
155
+ monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "none")
156
+ assert _cors_origins() == []
157
+
158
+
159
+ def test_cors_preflight_is_answered_before_auth():
160
+ """A browser pre-flight carries no X-API-Key: CORS must sit outside auth."""
161
+ from starlette.applications import Starlette
162
+ from starlette.middleware.cors import CORSMiddleware
163
+ from starlette.responses import PlainTextResponse
164
+ from starlette.routing import Route
165
+ from starlette.testclient import TestClient
166
+
167
+ async def ok(_request):
168
+ return PlainTextResponse("ok")
169
+
170
+ app = Starlette(routes=[Route("/mcp", ok, methods=["POST"])])
171
+ # Same registration order as _run_http: auth first, CORS last (outermost).
172
+ app.add_middleware(ApiKeyMiddleware, expected="secret")
173
+ app.add_middleware(
174
+ CORSMiddleware,
175
+ allow_origins=["https://spectrokernel.io"],
176
+ allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
177
+ allow_headers=["*"],
178
+ expose_headers=["Mcp-Session-Id"],
179
+ )
180
+ client = TestClient(app)
181
+ pre = client.options(
182
+ "/mcp",
183
+ headers={
184
+ "Origin": "https://spectrokernel.io",
185
+ "Access-Control-Request-Method": "POST",
186
+ "Access-Control-Request-Headers": "content-type,mcp-session-id",
187
+ },
188
+ )
189
+ assert pre.status_code == 200
190
+ assert pre.headers["access-control-allow-origin"] == "https://spectrokernel.io"
191
+ assert "mcp-session-id" in pre.headers["access-control-allow-headers"].lower()
192
+ # Real request from an allowed origin: auth still applies, headers exposed.
193
+ ok_headers = {"Origin": "https://spectrokernel.io", "X-API-Key": "secret"}
194
+ resp = client.post("/mcp", headers=ok_headers)
195
+ assert resp.status_code == 200
196
+ assert resp.headers["access-control-allow-origin"] == "https://spectrokernel.io"
197
+ assert "Mcp-Session-Id" in resp.headers["access-control-expose-headers"]
198
+ assert client.post("/mcp", headers={"Origin": "https://spectrokernel.io"}).status_code == 401
199
+ # Disallowed origin gets no CORS grant.
200
+ other = client.options(
201
+ "/mcp",
202
+ headers={"Origin": "https://evil.example", "Access-Control-Request-Method": "POST"},
203
+ )
204
+ assert "access-control-allow-origin" not in other.headers
@@ -0,0 +1,6 @@
1
+ """Documentation tooling: generators that turn the live registry into pages and JSON.
2
+
3
+ Run ``python tools/docs/build.py`` for a full build, or ``python tools/docs/generate.py``
4
+ before ``zensical serve``. Everything written under ``docs/algorithms/``, ``docs/ai/``
5
+ and ``docs/llms.txt`` is generated and git-ignored.
6
+ """
@@ -0,0 +1,82 @@
1
+ """Build the documentation site the way CI does.
2
+
3
+ python tools/docs/build.py # generate + zensical build --strict + post-process
4
+ python tools/docs/build.py --no-strict
5
+
6
+ Steps :
7
+
8
+ 1. run the generators (``generate.py``) so every generated page exists before the
9
+ engine looks for it - Zensical does not run MkDocs plugins such as gen-files ;
10
+ 2. ``zensical build --strict`` (warnings are errors, exactly like CI) ;
11
+ 3. post-process ``site/`` for the hosting platform : publish the Markdown source of
12
+ every page next to its HTML (``<page>/index.md``) so agents can fetch it, and write
13
+ the Netlify ``_headers`` file (content types, CORS on the JSON artefacts).
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import shutil
19
+ import subprocess
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ ROOT = Path(__file__).resolve().parents[2]
24
+ DOCS = ROOT / "docs"
25
+ SITE = ROOT / "site"
26
+
27
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
28
+ from generate import generate_all # noqa: E402
29
+
30
+ HEADERS = """\
31
+ # Generated by tools/docs/build.py - Netlify header rules for docs.spectrokernel.io
32
+ /*
33
+ X-Content-Type-Options: nosniff
34
+ Referrer-Policy: strict-origin-when-cross-origin
35
+ /*.md
36
+ Content-Type: text/markdown; charset=utf-8
37
+ /llms.txt
38
+ Content-Type: text/plain; charset=utf-8
39
+ /ai/*
40
+ Access-Control-Allow-Origin: *
41
+ Cache-Control: public, max-age=300
42
+ """
43
+
44
+
45
+ def _zensical() -> str:
46
+ candidate = Path(sys.executable).with_name("zensical")
47
+ return str(candidate) if candidate.exists() else (shutil.which("zensical") or "zensical")
48
+
49
+
50
+ def publish_markdown_sources() -> int:
51
+ """Copy each ``docs/**/*.md`` page to ``site/<url>/index.md``."""
52
+ count = 0
53
+ for src in DOCS.rglob("*.md"):
54
+ rel = src.relative_to(DOCS)
55
+ if rel.name.startswith("."):
56
+ continue
57
+ if rel.name == "index.md":
58
+ target = SITE / rel.parent / "index.md"
59
+ else:
60
+ target = SITE / rel.with_suffix("") / "index.md"
61
+ target.parent.mkdir(parents=True, exist_ok=True)
62
+ shutil.copyfile(src, target)
63
+ count += 1
64
+ return count
65
+
66
+
67
+ def main(argv: list[str]) -> int:
68
+ strict = "--no-strict" not in argv
69
+ generate_all()
70
+ cmd = [_zensical(), "build"] + (["--strict"] if strict else [])
71
+ print("build:", " ".join(cmd))
72
+ result = subprocess.run(cmd, cwd=ROOT)
73
+ if result.returncode != 0:
74
+ return result.returncode
75
+ n = publish_markdown_sources()
76
+ (SITE / "_headers").write_text(HEADERS, encoding="utf-8")
77
+ print(f"build: {n} Markdown sources published next to their pages, _headers written")
78
+ return 0
79
+
80
+
81
+ if __name__ == "__main__":
82
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,25 @@
1
+ """Run every documentation generator (catalogue pages + JSON, llms.txt).
2
+
3
+ python tools/docs/generate.py && zensical serve
4
+
5
+ The build script (``tools/docs/build.py``) calls this before building.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
14
+
15
+ import generate_catalogue # noqa: E402
16
+ import generate_llms # noqa: E402
17
+
18
+
19
+ def generate_all() -> None:
20
+ generate_catalogue.generate()
21
+ generate_llms.generate()
22
+
23
+
24
+ if __name__ == "__main__":
25
+ generate_all()