scitex 2.14.0__py3-none-any.whl → 2.15.2__py3-none-any.whl

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 (300) hide show
  1. scitex/__init__.py +71 -17
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +210 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/template.py +24 -0
  17. scitex/_mcp_tools/writer.py +21 -204
  18. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  19. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  20. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  21. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  22. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  23. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  24. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  25. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  26. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  27. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  28. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
  29. scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
  30. scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
  31. scitex/audio/README.md +40 -36
  32. scitex/audio/__init__.py +129 -61
  33. scitex/audio/_branding.py +185 -0
  34. scitex/audio/_mcp/__init__.py +32 -0
  35. scitex/audio/_mcp/handlers.py +59 -6
  36. scitex/audio/_mcp/speak_handlers.py +238 -0
  37. scitex/audio/_relay.py +225 -0
  38. scitex/audio/_tts.py +18 -10
  39. scitex/audio/engines/base.py +17 -10
  40. scitex/audio/engines/elevenlabs_engine.py +7 -2
  41. scitex/audio/mcp_server.py +228 -75
  42. scitex/canvas/README.md +1 -1
  43. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  44. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  45. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  46. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  47. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  48. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  49. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  50. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  51. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  52. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  53. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  54. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  55. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  56. scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
  57. scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
  58. scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
  59. scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
  60. scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
  61. scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
  62. scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
  63. scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
  64. scitex/canvas/editor/flask_editor/_core.py +25 -1684
  65. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  66. scitex/cli/__init__.py +38 -43
  67. scitex/cli/audio.py +76 -27
  68. scitex/cli/capture.py +13 -20
  69. scitex/cli/introspect.py +481 -0
  70. scitex/cli/main.py +200 -109
  71. scitex/cli/mcp.py +60 -34
  72. scitex/cli/plt.py +357 -0
  73. scitex/cli/repro.py +15 -8
  74. scitex/cli/resource.py +15 -8
  75. scitex/cli/scholar/__init__.py +23 -8
  76. scitex/cli/scholar/_crossref_scitex.py +296 -0
  77. scitex/cli/scholar/_fetch.py +25 -3
  78. scitex/cli/social.py +314 -0
  79. scitex/cli/stats.py +15 -8
  80. scitex/cli/template.py +129 -12
  81. scitex/cli/tex.py +15 -8
  82. scitex/cli/writer.py +132 -8
  83. scitex/cloud/__init__.py +41 -2
  84. scitex/config/README.md +1 -1
  85. scitex/config/__init__.py +16 -2
  86. scitex/config/_env_registry.py +256 -0
  87. scitex/context/__init__.py +22 -0
  88. scitex/dev/__init__.py +20 -1
  89. scitex/diagram/__init__.py +42 -19
  90. scitex/diagram/mcp_server.py +13 -125
  91. scitex/gen/__init__.py +50 -14
  92. scitex/gen/_list_packages.py +4 -4
  93. scitex/introspect/__init__.py +82 -0
  94. scitex/introspect/_call_graph.py +303 -0
  95. scitex/introspect/_class_hierarchy.py +163 -0
  96. scitex/introspect/_core.py +41 -0
  97. scitex/introspect/_docstring.py +131 -0
  98. scitex/introspect/_examples.py +113 -0
  99. scitex/introspect/_imports.py +271 -0
  100. scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
  101. scitex/introspect/_mcp/__init__.py +41 -0
  102. scitex/introspect/_mcp/handlers.py +233 -0
  103. scitex/introspect/_members.py +155 -0
  104. scitex/introspect/_resolve.py +89 -0
  105. scitex/introspect/_signature.py +131 -0
  106. scitex/introspect/_source.py +80 -0
  107. scitex/introspect/_type_hints.py +172 -0
  108. scitex/io/_save.py +1 -2
  109. scitex/io/bundle/README.md +1 -1
  110. scitex/logging/_formatters.py +19 -9
  111. scitex/mcp_server.py +98 -5
  112. scitex/os/__init__.py +4 -0
  113. scitex/{gen → os}/_check_host.py +4 -5
  114. scitex/plt/__init__.py +245 -550
  115. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  116. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  117. scitex/plt/gallery/README.md +1 -1
  118. scitex/plt/utils/_hitmap/__init__.py +82 -0
  119. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  120. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  121. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  122. scitex/plt/utils/_hitmap/_constants.py +40 -0
  123. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  124. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  125. scitex/plt/utils/_hitmap/_query.py +113 -0
  126. scitex/plt/utils/_hitmap.py +46 -1616
  127. scitex/plt/utils/_metadata/__init__.py +80 -0
  128. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  129. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  130. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  131. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  132. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  133. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  134. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  135. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  136. scitex/plt/utils/_metadata/_csv.py +416 -0
  137. scitex/plt/utils/_metadata/_detect.py +225 -0
  138. scitex/plt/utils/_metadata/_legend.py +127 -0
  139. scitex/plt/utils/_metadata/_rounding.py +117 -0
  140. scitex/plt/utils/_metadata/_verification.py +202 -0
  141. scitex/schema/README.md +1 -1
  142. scitex/scholar/__init__.py +8 -0
  143. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  144. scitex/scholar/core/Scholar.py +63 -1700
  145. scitex/scholar/core/_mixins/__init__.py +36 -0
  146. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  147. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  148. scitex/scholar/core/_mixins/_loaders.py +103 -0
  149. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  150. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  151. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  152. scitex/scholar/core/_mixins/_savers.py +69 -0
  153. scitex/scholar/core/_mixins/_search.py +103 -0
  154. scitex/scholar/core/_mixins/_services.py +88 -0
  155. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  156. scitex/scholar/crossref_scitex.py +367 -0
  157. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  158. scitex/scholar/examples/00_run_all.sh +120 -0
  159. scitex/scholar/jobs/_executors.py +27 -3
  160. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  161. scitex/scholar/pdf_download/_cli.py +154 -0
  162. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  163. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  164. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  165. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  166. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  167. scitex/scholar/pipelines/_single_steps.py +71 -36
  168. scitex/scholar/storage/_LibraryManager.py +97 -1695
  169. scitex/scholar/storage/_mixins/__init__.py +30 -0
  170. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  171. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  172. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  173. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  174. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  175. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  176. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  177. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  178. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  179. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  180. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  181. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  182. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  183. scitex/security/README.md +3 -3
  184. scitex/session/README.md +1 -1
  185. scitex/session/__init__.py +26 -7
  186. scitex/session/_decorator.py +1 -1
  187. scitex/sh/README.md +1 -1
  188. scitex/sh/__init__.py +7 -4
  189. scitex/social/__init__.py +155 -0
  190. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  191. scitex/stats/_mcp/_handlers/__init__.py +31 -0
  192. scitex/stats/_mcp/_handlers/_corrections.py +113 -0
  193. scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
  194. scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
  195. scitex/stats/_mcp/_handlers/_format.py +94 -0
  196. scitex/stats/_mcp/_handlers/_normality.py +110 -0
  197. scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
  198. scitex/stats/_mcp/_handlers/_power.py +247 -0
  199. scitex/stats/_mcp/_handlers/_recommend.py +102 -0
  200. scitex/stats/_mcp/_handlers/_run_test.py +279 -0
  201. scitex/stats/_mcp/_handlers/_stars.py +48 -0
  202. scitex/stats/_mcp/handlers.py +19 -1171
  203. scitex/stats/auto/_stat_style.py +175 -0
  204. scitex/stats/auto/_style_definitions.py +411 -0
  205. scitex/stats/auto/_styles.py +22 -620
  206. scitex/stats/descriptive/__init__.py +11 -8
  207. scitex/stats/descriptive/_ci.py +39 -0
  208. scitex/stats/power/_power.py +15 -4
  209. scitex/str/__init__.py +2 -1
  210. scitex/str/_title_case.py +63 -0
  211. scitex/template/README.md +1 -1
  212. scitex/template/__init__.py +25 -10
  213. scitex/template/_code_templates.py +147 -0
  214. scitex/template/_mcp/handlers.py +81 -0
  215. scitex/template/_mcp/tool_schemas.py +55 -0
  216. scitex/template/_templates/__init__.py +51 -0
  217. scitex/template/_templates/audio.py +233 -0
  218. scitex/template/_templates/canvas.py +312 -0
  219. scitex/template/_templates/capture.py +268 -0
  220. scitex/template/_templates/config.py +43 -0
  221. scitex/template/_templates/diagram.py +294 -0
  222. scitex/template/_templates/io.py +107 -0
  223. scitex/template/_templates/module.py +53 -0
  224. scitex/template/_templates/plt.py +202 -0
  225. scitex/template/_templates/scholar.py +267 -0
  226. scitex/template/_templates/session.py +130 -0
  227. scitex/template/_templates/session_minimal.py +43 -0
  228. scitex/template/_templates/session_plot.py +67 -0
  229. scitex/template/_templates/session_stats.py +77 -0
  230. scitex/template/_templates/stats.py +323 -0
  231. scitex/template/_templates/writer.py +296 -0
  232. scitex/template/clone_writer_directory.py +5 -5
  233. scitex/ui/_backends/_email.py +10 -2
  234. scitex/ui/_backends/_webhook.py +5 -1
  235. scitex/web/_search_pubmed.py +10 -6
  236. scitex/writer/README.md +1 -1
  237. scitex/writer/_mcp/handlers.py +11 -744
  238. scitex/writer/_mcp/tool_schemas.py +5 -335
  239. scitex-2.15.2.dist-info/METADATA +648 -0
  240. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
  241. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  242. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  243. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  244. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  245. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  246. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  247. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  248. scitex/diagram/_compile.py +0 -312
  249. scitex/diagram/_diagram.py +0 -355
  250. scitex/diagram/_mcp/__init__.py +0 -4
  251. scitex/diagram/_mcp/handlers.py +0 -400
  252. scitex/diagram/_mcp/tool_schemas.py +0 -157
  253. scitex/diagram/_presets.py +0 -173
  254. scitex/diagram/_schema.py +0 -182
  255. scitex/diagram/_split.py +0 -278
  256. scitex/gen/_ci.py +0 -12
  257. scitex/gen/_title_case.py +0 -89
  258. scitex/plt/_mcp/__init__.py +0 -4
  259. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  260. scitex/plt/_mcp/_handlers_figure.py +0 -195
  261. scitex/plt/_mcp/_handlers_plot.py +0 -252
  262. scitex/plt/_mcp/_handlers_style.py +0 -219
  263. scitex/plt/_mcp/handlers.py +0 -74
  264. scitex/plt/_mcp/tool_schemas.py +0 -497
  265. scitex/plt/mcp_server.py +0 -231
  266. scitex/scholar/data/.gitkeep +0 -0
  267. scitex/scholar/data/README.md +0 -44
  268. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  269. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  270. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  271. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  272. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  273. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  274. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  275. scitex/scholar/data/bib_files/pac.bib +0 -698
  276. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  277. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  278. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  279. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  280. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  281. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  282. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  283. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  284. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  285. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  286. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  287. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  288. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  289. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  290. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  291. scitex/scholar/data/impact_factor.db +0 -0
  292. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  293. scitex/scholar/examples/dev.py +0 -38
  294. scitex-2.14.0.dist-info/METADATA +0 -1238
  295. /scitex/{gen → context}/_detect_environment.py +0 -0
  296. /scitex/{gen → context}/_get_notebook_path.py +0 -0
  297. /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
  298. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
  299. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
  300. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
scitex/cli/plt.py ADDED
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-25
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/cli/plt.py
4
+
5
+ """
6
+ SciTeX CLI - Plot Commands
7
+
8
+ Thin wrapper around figrecipe CLI for reproducible matplotlib figures.
9
+ All commands delegate to figrecipe for reproducibility.
10
+ """
11
+
12
+ import subprocess
13
+ import sys
14
+
15
+ import click
16
+
17
+
18
+ def _run_figrecipe(*args) -> int:
19
+ """Run figrecipe CLI command."""
20
+ cmd = [sys.executable, "-m", "figrecipe"]
21
+ cmd.extend(args)
22
+
23
+ result = subprocess.run(cmd)
24
+ return result.returncode
25
+
26
+
27
+ def _check_figrecipe() -> bool:
28
+ """Check if figrecipe is available."""
29
+ try:
30
+ import figrecipe # noqa: F401
31
+
32
+ return True
33
+ except ImportError:
34
+ return False
35
+
36
+
37
+ @click.group(
38
+ context_settings={"help_option_names": ["-h", "--help"]},
39
+ invoke_without_command=True,
40
+ )
41
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
42
+ @click.pass_context
43
+ def plt(ctx, help_recursive):
44
+ """
45
+ Plot and figure management (powered by figrecipe)
46
+
47
+ \b
48
+ Commands:
49
+ plot - Create figure from YAML/JSON spec
50
+ edit - Launch interactive GUI editor
51
+ compose - Combine multiple figures
52
+ crop - Crop whitespace from images
53
+ reproduce - Reproduce figure from recipe
54
+ validate - Validate recipe reproducibility
55
+ diagram - Create diagrams (flowcharts, etc.)
56
+
57
+ \b
58
+ Examples:
59
+ scitex plt plot spec.yaml -o fig.png
60
+ scitex plt edit
61
+ scitex plt compose a.png b.png -o combined.png
62
+ scitex plt reproduce recipe.yaml
63
+
64
+ \b
65
+ Note: Wraps figrecipe CLI. Run 'figrecipe --help' for full options.
66
+ """
67
+ if not _check_figrecipe():
68
+ click.secho("Error: figrecipe not installed", fg="red", err=True)
69
+ click.echo("\nInstall with: pip install figrecipe")
70
+ ctx.exit(1)
71
+
72
+ if help_recursive:
73
+ from . import print_help_recursive
74
+
75
+ print_help_recursive(ctx, plt)
76
+ ctx.exit(0)
77
+ elif ctx.invoked_subcommand is None:
78
+ click.echo(ctx.get_help())
79
+
80
+
81
+ @plt.command()
82
+ @click.argument("spec_file", type=click.Path(exists=True))
83
+ @click.option("-o", "--output", required=True, help="Output file path")
84
+ @click.option("--dpi", type=int, default=300, help="DPI for raster output")
85
+ @click.option("--no-recipe", is_flag=True, help="Don't save YAML recipe")
86
+ def plot(spec_file, output, dpi, no_recipe):
87
+ """
88
+ Create a figure from a declarative YAML/JSON spec.
89
+
90
+ \b
91
+ Examples:
92
+ scitex plt plot spec.yaml -o fig.png
93
+ scitex plt plot spec.json -o fig.pdf --dpi 600
94
+ """
95
+ args = ["plot", spec_file, "-o", output, "--dpi", str(dpi)]
96
+ if no_recipe:
97
+ args.append("--no-recipe")
98
+ sys.exit(_run_figrecipe(*args))
99
+
100
+
101
+ @plt.command()
102
+ @click.argument("recipe_file", type=click.Path(exists=True), required=False)
103
+ def edit(recipe_file):
104
+ """
105
+ Launch interactive GUI editor.
106
+
107
+ \b
108
+ Examples:
109
+ scitex plt edit # Open blank editor
110
+ scitex plt edit recipe.yaml # Open existing recipe
111
+ """
112
+ args = ["edit"]
113
+ if recipe_file:
114
+ args.append(recipe_file)
115
+ sys.exit(_run_figrecipe(*args))
116
+
117
+
118
+ @plt.command()
119
+ @click.argument("sources", nargs=-1, required=True, type=click.Path(exists=True))
120
+ @click.option("-o", "--output", required=True, help="Output file path")
121
+ @click.option(
122
+ "-l",
123
+ "--layout",
124
+ type=click.Choice(["horizontal", "vertical", "grid"]),
125
+ default="horizontal",
126
+ help="Layout mode",
127
+ )
128
+ @click.option("--gap", type=float, default=5, help="Gap between panels (mm)")
129
+ @click.option("--dpi", type=int, default=300, help="DPI for output")
130
+ @click.option("--no-labels", is_flag=True, help="Don't add panel labels (A, B, C)")
131
+ @click.option("--caption", help="Figure caption")
132
+ def compose(sources, output, layout, gap, dpi, no_labels, caption):
133
+ """
134
+ Compose multiple figures into one.
135
+
136
+ \b
137
+ Examples:
138
+ scitex plt compose a.png b.png -o combined.png
139
+ scitex plt compose *.png -o fig.pdf --layout grid
140
+ scitex plt compose a.yaml b.yaml -o fig.png --gap 10
141
+ """
142
+ args = ["compose"]
143
+ args.extend(sources)
144
+ args.extend(
145
+ ["-o", output, "--layout", layout, "--gap", str(gap), "--dpi", str(dpi)]
146
+ )
147
+ if no_labels:
148
+ args.append("--no-labels")
149
+ if caption:
150
+ args.extend(["--caption", caption])
151
+ sys.exit(_run_figrecipe(*args))
152
+
153
+
154
+ @plt.command()
155
+ @click.argument("input_file", type=click.Path(exists=True))
156
+ @click.option("-o", "--output", help="Output file (default: overwrites input)")
157
+ @click.option("--margin", type=float, default=1, help="Margin to keep (mm)")
158
+ def crop(input_file, output, margin):
159
+ """
160
+ Crop whitespace from an image.
161
+
162
+ \b
163
+ Examples:
164
+ scitex plt crop figure.png
165
+ scitex plt crop figure.png -o cropped.png --margin 2
166
+ """
167
+ args = ["crop", input_file, "--margin", str(margin)]
168
+ if output:
169
+ args.extend(["-o", output])
170
+ sys.exit(_run_figrecipe(*args))
171
+
172
+
173
+ @plt.command()
174
+ @click.argument("recipe_file", type=click.Path(exists=True))
175
+ @click.option("-o", "--output", help="Output file path")
176
+ @click.option(
177
+ "--format", "fmt", type=click.Choice(["png", "pdf", "svg"]), help="Output format"
178
+ )
179
+ @click.option("--dpi", type=int, default=300, help="DPI for raster output")
180
+ def reproduce(recipe_file, output, fmt, dpi):
181
+ """
182
+ Reproduce a figure from a YAML recipe.
183
+
184
+ \b
185
+ Examples:
186
+ scitex plt reproduce recipe.yaml
187
+ scitex plt reproduce recipe.yaml -o new_fig.pdf
188
+ """
189
+ args = ["reproduce", recipe_file, "--dpi", str(dpi)]
190
+ if output:
191
+ args.extend(["-o", output])
192
+ if fmt:
193
+ args.extend(["--format", fmt])
194
+ sys.exit(_run_figrecipe(*args))
195
+
196
+
197
+ @plt.command()
198
+ @click.argument("recipe_file", type=click.Path(exists=True))
199
+ @click.option("--threshold", type=float, default=100, help="MSE threshold")
200
+ def validate(recipe_file, threshold):
201
+ """
202
+ Validate that a recipe reproduces its original figure.
203
+
204
+ \b
205
+ Examples:
206
+ scitex plt validate recipe.yaml
207
+ scitex plt validate recipe.yaml --threshold 50
208
+ """
209
+ args = ["validate", recipe_file, "--threshold", str(threshold)]
210
+ sys.exit(_run_figrecipe(*args))
211
+
212
+
213
+ @plt.command()
214
+ @click.argument("spec_file", type=click.Path(exists=True))
215
+ @click.option("-o", "--output", help="Output file path")
216
+ @click.option(
217
+ "--format", "fmt", type=click.Choice(["mermaid", "graphviz"]), default="mermaid"
218
+ )
219
+ def diagram(spec_file, output, fmt):
220
+ """
221
+ Create diagrams (flowcharts, pipelines, etc.)
222
+
223
+ \b
224
+ Examples:
225
+ scitex plt diagram workflow.yaml
226
+ scitex plt diagram pipeline.yaml -o diagram.png --format graphviz
227
+ """
228
+ args = ["diagram", spec_file, "--format", fmt]
229
+ if output:
230
+ args.extend(["-o", output])
231
+ sys.exit(_run_figrecipe(*args))
232
+
233
+
234
+ @plt.command()
235
+ @click.argument("recipe_file", type=click.Path(exists=True))
236
+ @click.option("-v", "--verbose", is_flag=True, help="Show detailed info")
237
+ def info(recipe_file, verbose):
238
+ """
239
+ Show information about a recipe.
240
+
241
+ \b
242
+ Examples:
243
+ scitex plt info recipe.yaml
244
+ scitex plt info recipe.yaml --verbose
245
+ """
246
+ args = ["info", recipe_file]
247
+ if verbose:
248
+ args.append("--verbose")
249
+ sys.exit(_run_figrecipe(*args))
250
+
251
+
252
+ @plt.command()
253
+ @click.argument("recipe_file", type=click.Path(exists=True))
254
+ @click.option("-o", "--output", help="Output file for extracted data")
255
+ def extract(recipe_file, output):
256
+ """
257
+ Extract plotted data arrays from a recipe.
258
+
259
+ \b
260
+ Examples:
261
+ scitex plt extract recipe.yaml
262
+ scitex plt extract recipe.yaml -o data.json
263
+ """
264
+ args = ["extract", recipe_file]
265
+ if output:
266
+ args.extend(["-o", output])
267
+ sys.exit(_run_figrecipe(*args))
268
+
269
+
270
+ @plt.command()
271
+ @click.argument("action", type=click.Choice(["list", "show", "set"]), default="list")
272
+ @click.argument("style_name", required=False)
273
+ def style(action, style_name):
274
+ """
275
+ Manage figure styles and presets.
276
+
277
+ \b
278
+ Examples:
279
+ scitex plt style list # List available styles
280
+ scitex plt style show nature # Show style details
281
+ scitex plt style set publication # Set default style
282
+ """
283
+ args = ["style", action]
284
+ if style_name:
285
+ args.append(style_name)
286
+ sys.exit(_run_figrecipe(*args))
287
+
288
+
289
+ @plt.command()
290
+ @click.option("--check", is_flag=True, help="Check font availability")
291
+ def fonts(check):
292
+ """
293
+ List or check available fonts.
294
+
295
+ \b
296
+ Examples:
297
+ scitex plt fonts
298
+ scitex plt fonts --check
299
+ """
300
+ args = ["fonts"]
301
+ if check:
302
+ args.append("--check")
303
+ sys.exit(_run_figrecipe(*args))
304
+
305
+
306
+ @plt.command()
307
+ @click.argument("input_file", type=click.Path(exists=True))
308
+ @click.option("-o", "--output", required=True, help="Output file path")
309
+ @click.option("--format", "fmt", help="Output format (inferred from extension)")
310
+ def convert(input_file, output, fmt):
311
+ """
312
+ Convert between figure formats.
313
+
314
+ \b
315
+ Examples:
316
+ scitex plt convert fig.png -o fig.pdf
317
+ scitex plt convert fig.svg -o fig.png
318
+ """
319
+ args = ["convert", input_file, "-o", output]
320
+ if fmt:
321
+ args.extend(["--format", fmt])
322
+ sys.exit(_run_figrecipe(*args))
323
+
324
+
325
+ @plt.command()
326
+ @click.option(
327
+ "-t",
328
+ "--transport",
329
+ type=click.Choice(["stdio", "sse", "http"]),
330
+ default="stdio",
331
+ help="Transport protocol",
332
+ )
333
+ @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE transport")
334
+ @click.option("--port", default=8087, type=int, help="Port for HTTP/SSE transport")
335
+ def serve(transport, host, port):
336
+ """
337
+ Run figrecipe MCP server
338
+
339
+ \b
340
+ Examples:
341
+ scitex plt serve # stdio for Claude Desktop
342
+ scitex plt serve -t http --port 8087
343
+ """
344
+ args = ["mcp", "run", "--transport", transport]
345
+ if transport != "stdio":
346
+ args.extend(["--host", host, "--port", str(port)])
347
+ click.secho(f"Starting figrecipe MCP server ({transport})", fg="cyan")
348
+ click.echo(f" Host: {host}")
349
+ click.echo(f" Port: {port}")
350
+
351
+ sys.exit(_run_figrecipe(*args))
352
+
353
+
354
+ if __name__ == "__main__":
355
+ plt()
356
+
357
+ # EOF
scitex/cli/repro.py CHANGED
@@ -10,8 +10,13 @@ import sys
10
10
  import click
11
11
 
12
12
 
13
- @click.group(context_settings={"help_option_names": ["-h", "--help"]})
14
- def repro():
13
+ @click.group(
14
+ context_settings={"help_option_names": ["-h", "--help"]},
15
+ invoke_without_command=True,
16
+ )
17
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
18
+ @click.pass_context
19
+ def repro(ctx, help_recursive):
15
20
  """
16
21
  Reproducibility utilities
17
22
 
@@ -29,20 +34,22 @@ def repro():
29
34
  scitex repro hash data.npy # Hash array file
30
35
  scitex repro seed 42 # Set random seed
31
36
  """
32
- pass
37
+ if help_recursive:
38
+ _print_help_recursive(ctx)
39
+ ctx.exit(0)
40
+ elif ctx.invoked_subcommand is None:
41
+ click.echo(ctx.get_help())
33
42
 
34
43
 
35
- @repro.command("help-recursive")
36
- @click.pass_context
37
- def help_recursive(ctx):
38
- """Show help for all commands recursively."""
44
+ def _print_help_recursive(ctx):
45
+ """Print help for all commands recursively."""
39
46
  fake_parent = click.Context(click.Group(), info_name="scitex")
40
47
  parent_ctx = click.Context(repro, info_name="repro", parent=fake_parent)
41
48
  click.secho("━━━ scitex repro ━━━", fg="cyan", bold=True)
42
49
  click.echo(repro.get_help(parent_ctx))
43
50
  for name in sorted(repro.list_commands(ctx) or []):
44
51
  cmd = repro.get_command(ctx, name)
45
- if cmd is None or name == "help-recursive":
52
+ if cmd is None:
46
53
  continue
47
54
  click.echo()
48
55
  click.secho(f"━━━ scitex repro {name} ━━━", fg="cyan", bold=True)
scitex/cli/resource.py CHANGED
@@ -10,8 +10,13 @@ import sys
10
10
  import click
11
11
 
12
12
 
13
- @click.group(context_settings={"help_option_names": ["-h", "--help"]})
14
- def resource():
13
+ @click.group(
14
+ context_settings={"help_option_names": ["-h", "--help"]},
15
+ invoke_without_command=True,
16
+ )
17
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
18
+ @click.pass_context
19
+ def resource(ctx, help_recursive):
15
20
  """
16
21
  System resource monitoring
17
22
 
@@ -27,20 +32,22 @@ def resource():
27
32
  scitex resource usage # Current CPU/memory/GPU usage
28
33
  scitex resource monitor --interval 5
29
34
  """
30
- pass
35
+ if help_recursive:
36
+ _print_help_recursive(ctx)
37
+ ctx.exit(0)
38
+ elif ctx.invoked_subcommand is None:
39
+ click.echo(ctx.get_help())
31
40
 
32
41
 
33
- @resource.command("help-recursive")
34
- @click.pass_context
35
- def help_recursive(ctx):
36
- """Show help for all commands recursively."""
42
+ def _print_help_recursive(ctx):
43
+ """Print help for all commands recursively."""
37
44
  fake_parent = click.Context(click.Group(), info_name="scitex")
38
45
  parent_ctx = click.Context(resource, info_name="resource", parent=fake_parent)
39
46
  click.secho("━━━ scitex resource ━━━", fg="cyan", bold=True)
40
47
  click.echo(resource.get_help(parent_ctx))
41
48
  for name in sorted(resource.list_commands(ctx) or []):
42
49
  cmd = resource.get_command(ctx, name)
43
- if cmd is None or name == "help-recursive":
50
+ if cmd is None:
44
51
  continue
45
52
  click.echo()
46
53
  click.secho(f"━━━ scitex resource {name} ━━━", fg="cyan", bold=True)
@@ -15,19 +15,31 @@ Usage:
15
15
  scitex scholar config
16
16
  scitex scholar jobs list
17
17
  scitex scholar jobs status <job_id>
18
+
19
+ CrossRef database (167M+ papers via crossref-local):
20
+ scitex scholar crossref-scitex search "deep learning"
21
+ scitex scholar crossref-scitex get 10.1038/nature12373
22
+ scitex scholar crossref-scitex count "epilepsy seizure"
23
+ scitex scholar crossref-scitex info
18
24
  """
19
25
 
20
26
  from __future__ import annotations
21
27
 
22
28
  import click
23
29
 
30
+ from ._crossref_scitex import crossref_scitex
24
31
  from ._fetch import fetch
25
32
  from ._jobs import jobs
26
33
  from ._library import config, library
27
34
 
28
35
 
29
- @click.group()
30
- def scholar():
36
+ @click.group(
37
+ context_settings={"help_option_names": ["-h", "--help"]},
38
+ invoke_without_command=True,
39
+ )
40
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
41
+ @click.pass_context
42
+ def scholar(ctx, help_recursive):
31
43
  """
32
44
  Scientific paper management
33
45
 
@@ -41,13 +53,15 @@ def scholar():
41
53
  scitex scholar library
42
54
  scitex scholar jobs list
43
55
  """
44
- pass
56
+ if help_recursive:
57
+ _print_help_recursive(ctx)
58
+ ctx.exit(0)
59
+ elif ctx.invoked_subcommand is None:
60
+ click.echo(ctx.get_help())
45
61
 
46
62
 
47
- @scholar.command("help-recursive")
48
- @click.pass_context
49
- def help_recursive(ctx):
50
- """Show help for all commands recursively."""
63
+ def _print_help_recursive(ctx):
64
+ """Print help for all commands recursively."""
51
65
  fake_parent = click.Context(click.Group(), info_name="scitex")
52
66
  parent_ctx = click.Context(scholar, info_name="scholar", parent=fake_parent)
53
67
 
@@ -56,7 +70,7 @@ def help_recursive(ctx):
56
70
 
57
71
  for name in sorted(scholar.list_commands(ctx) or []):
58
72
  cmd = scholar.get_command(ctx, name)
59
- if cmd is None or name == "help-recursive":
73
+ if cmd is None:
60
74
  continue
61
75
  click.echo()
62
76
  click.secho(f"━━━ scitex scholar {name} ━━━", fg="cyan", bold=True)
@@ -79,6 +93,7 @@ def help_recursive(ctx):
79
93
  click.echo(sub_cmd.get_help(sub_sub_ctx))
80
94
 
81
95
 
96
+ scholar.add_command(crossref_scitex)
82
97
  scholar.add_command(fetch)
83
98
  scholar.add_command(library)
84
99
  scholar.add_command(config)