scitex 2.14.0__py3-none-any.whl → 2.15.1__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 (218) hide show
  1. scitex/__init__.py +47 -0
  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 +191 -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/writer.py +21 -204
  17. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  18. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  19. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  20. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  21. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  22. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  23. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  24. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  25. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  26. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  27. scitex/audio/README.md +40 -36
  28. scitex/audio/__init__.py +127 -59
  29. scitex/audio/_branding.py +185 -0
  30. scitex/audio/_mcp/__init__.py +32 -0
  31. scitex/audio/_mcp/handlers.py +59 -6
  32. scitex/audio/_mcp/speak_handlers.py +238 -0
  33. scitex/audio/_relay.py +225 -0
  34. scitex/audio/engines/elevenlabs_engine.py +6 -1
  35. scitex/audio/mcp_server.py +228 -75
  36. scitex/canvas/README.md +1 -1
  37. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  38. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  39. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  40. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  41. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  42. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  43. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  44. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  45. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  46. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  47. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  48. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  49. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  50. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  51. scitex/cli/__init__.py +38 -43
  52. scitex/cli/audio.py +76 -27
  53. scitex/cli/capture.py +13 -20
  54. scitex/cli/introspect.py +443 -0
  55. scitex/cli/main.py +198 -109
  56. scitex/cli/mcp.py +60 -34
  57. scitex/cli/scholar/__init__.py +8 -0
  58. scitex/cli/scholar/_crossref_scitex.py +296 -0
  59. scitex/cli/scholar/_fetch.py +25 -3
  60. scitex/cli/social.py +314 -0
  61. scitex/cli/writer.py +117 -0
  62. scitex/config/README.md +1 -1
  63. scitex/config/__init__.py +16 -2
  64. scitex/config/_env_registry.py +191 -0
  65. scitex/diagram/__init__.py +42 -19
  66. scitex/diagram/mcp_server.py +13 -125
  67. scitex/introspect/__init__.py +75 -0
  68. scitex/introspect/_call_graph.py +303 -0
  69. scitex/introspect/_class_hierarchy.py +163 -0
  70. scitex/introspect/_core.py +42 -0
  71. scitex/introspect/_docstring.py +131 -0
  72. scitex/introspect/_examples.py +113 -0
  73. scitex/introspect/_imports.py +271 -0
  74. scitex/introspect/_mcp/__init__.py +37 -0
  75. scitex/introspect/_mcp/handlers.py +208 -0
  76. scitex/introspect/_members.py +151 -0
  77. scitex/introspect/_resolve.py +89 -0
  78. scitex/introspect/_signature.py +131 -0
  79. scitex/introspect/_source.py +80 -0
  80. scitex/introspect/_type_hints.py +172 -0
  81. scitex/io/bundle/README.md +1 -1
  82. scitex/mcp_server.py +98 -5
  83. scitex/plt/__init__.py +248 -550
  84. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  85. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  86. scitex/plt/gallery/README.md +1 -1
  87. scitex/plt/utils/_hitmap/__init__.py +82 -0
  88. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  89. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  90. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  91. scitex/plt/utils/_hitmap/_constants.py +40 -0
  92. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  93. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  94. scitex/plt/utils/_hitmap/_query.py +113 -0
  95. scitex/plt/utils/_hitmap.py +46 -1616
  96. scitex/plt/utils/_metadata/__init__.py +80 -0
  97. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  98. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  99. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  100. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  101. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  102. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  103. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  104. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  105. scitex/plt/utils/_metadata/_csv.py +416 -0
  106. scitex/plt/utils/_metadata/_detect.py +225 -0
  107. scitex/plt/utils/_metadata/_legend.py +127 -0
  108. scitex/plt/utils/_metadata/_rounding.py +117 -0
  109. scitex/plt/utils/_metadata/_verification.py +202 -0
  110. scitex/schema/README.md +1 -1
  111. scitex/scholar/__init__.py +8 -0
  112. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  113. scitex/scholar/core/Scholar.py +63 -1700
  114. scitex/scholar/core/_mixins/__init__.py +36 -0
  115. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  116. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  117. scitex/scholar/core/_mixins/_loaders.py +103 -0
  118. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  119. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  120. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  121. scitex/scholar/core/_mixins/_savers.py +69 -0
  122. scitex/scholar/core/_mixins/_search.py +103 -0
  123. scitex/scholar/core/_mixins/_services.py +88 -0
  124. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  125. scitex/scholar/crossref_scitex.py +367 -0
  126. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  127. scitex/scholar/examples/00_run_all.sh +120 -0
  128. scitex/scholar/jobs/_executors.py +27 -3
  129. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  130. scitex/scholar/pdf_download/_cli.py +154 -0
  131. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  132. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  133. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  134. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  135. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  136. scitex/scholar/pipelines/_single_steps.py +71 -36
  137. scitex/scholar/storage/_LibraryManager.py +97 -1695
  138. scitex/scholar/storage/_mixins/__init__.py +30 -0
  139. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  140. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  141. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  142. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  143. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  144. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  145. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  146. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  147. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  148. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  149. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  150. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  151. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  152. scitex/security/README.md +3 -3
  153. scitex/session/README.md +1 -1
  154. scitex/sh/README.md +1 -1
  155. scitex/social/__init__.py +153 -0
  156. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  157. scitex/template/README.md +1 -1
  158. scitex/template/clone_writer_directory.py +5 -5
  159. scitex/writer/README.md +1 -1
  160. scitex/writer/_mcp/handlers.py +11 -744
  161. scitex/writer/_mcp/tool_schemas.py +5 -335
  162. scitex-2.15.1.dist-info/METADATA +648 -0
  163. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
  164. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  165. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  166. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  167. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  168. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  169. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  170. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  171. scitex/diagram/_compile.py +0 -312
  172. scitex/diagram/_diagram.py +0 -355
  173. scitex/diagram/_mcp/__init__.py +0 -4
  174. scitex/diagram/_mcp/handlers.py +0 -400
  175. scitex/diagram/_mcp/tool_schemas.py +0 -157
  176. scitex/diagram/_presets.py +0 -173
  177. scitex/diagram/_schema.py +0 -182
  178. scitex/diagram/_split.py +0 -278
  179. scitex/plt/_mcp/__init__.py +0 -4
  180. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  181. scitex/plt/_mcp/_handlers_figure.py +0 -195
  182. scitex/plt/_mcp/_handlers_plot.py +0 -252
  183. scitex/plt/_mcp/_handlers_style.py +0 -219
  184. scitex/plt/_mcp/handlers.py +0 -74
  185. scitex/plt/_mcp/tool_schemas.py +0 -497
  186. scitex/plt/mcp_server.py +0 -231
  187. scitex/scholar/data/.gitkeep +0 -0
  188. scitex/scholar/data/README.md +0 -44
  189. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  190. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  191. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  192. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  193. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  194. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  195. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  196. scitex/scholar/data/bib_files/pac.bib +0 -698
  197. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  198. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  199. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  200. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  201. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  202. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  203. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  204. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  205. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  206. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  207. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  208. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  209. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  210. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  211. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  212. scitex/scholar/data/impact_factor.db +0 -0
  213. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  214. scitex/scholar/examples/dev.py +0 -38
  215. scitex-2.14.0.dist-info/METADATA +0 -1238
  216. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
  217. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
  218. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/licenses/LICENSE +0 -0
scitex/cli/main.py CHANGED
@@ -15,11 +15,13 @@ from . import (
15
15
  cloud,
16
16
  config,
17
17
  convert,
18
+ introspect,
18
19
  mcp,
19
20
  repro,
20
21
  resource,
21
22
  scholar,
22
23
  security,
24
+ social,
23
25
  stats,
24
26
  template,
25
27
  tex,
@@ -28,36 +30,36 @@ from . import (
28
30
  )
29
31
 
30
32
 
31
- @click.group(context_settings={"help_option_names": ["-h", "--help"]})
33
+ @click.group(
34
+ context_settings={"help_option_names": ["-h", "--help"]},
35
+ invoke_without_command=True,
36
+ )
32
37
  @click.version_option()
33
- def cli():
38
+ @click.option("--help-recursive", is_flag=True, help="Show help for all commands")
39
+ @click.pass_context
40
+ def cli(ctx, help_recursive):
34
41
  """
35
42
  SciTeX - Integrated Scientific Research Platform
36
43
 
37
44
  \b
38
45
  Examples:
39
- scitex config list # Show all configured paths
40
- scitex config init # Initialize directories
41
- scitex cloud login
42
- scitex cloud clone ywatanabe/my-project
43
- scitex scholar bibtex papers.bib --project myresearch
44
- scitex scholar single --doi "10.1038/nature12373"
45
- scitex security check --save
46
- scitex web get-urls https://example.com
47
- scitex web download-images https://example.com --output ./downloads
48
- scitex audio speak "Hello world"
49
- scitex capture snap --output screenshot.jpg
50
- scitex resource usage
51
- scitex stats recommend --data data.csv
52
- scitex mcp list # List all MCP tools
53
- scitex mcp serve # Start MCP server
46
+ scitex config list # Show configured paths
47
+ scitex cloud clone user/project # Clone from cloud
48
+ scitex scholar bibtex papers.bib # Manage papers
49
+ scitex audio speak "Hello" # Text-to-speech
50
+ scitex mcp list-tools # List MCP tools
51
+ scitex mcp start # Start MCP server
54
52
 
55
53
  \b
56
54
  Enable tab-completion:
57
55
  scitex completion # Auto-install for your shell
58
56
  scitex completion --show # Show installation instructions
59
57
  """
60
- pass
58
+ if help_recursive:
59
+ _print_help_recursive(ctx)
60
+ ctx.exit(0)
61
+ elif ctx.invoked_subcommand is None:
62
+ click.echo(ctx.get_help())
61
63
 
62
64
 
63
65
  # Add command groups
@@ -67,11 +69,13 @@ cli.add_command(capture.capture)
67
69
  cli.add_command(cloud.cloud)
68
70
  cli.add_command(config.config)
69
71
  cli.add_command(convert.convert)
72
+ cli.add_command(introspect.introspect)
70
73
  cli.add_command(mcp.mcp)
71
74
  cli.add_command(repro.repro)
72
75
  cli.add_command(resource.resource)
73
76
  cli.add_command(scholar.scholar)
74
77
  cli.add_command(security.security)
78
+ cli.add_command(social.social)
75
79
  cli.add_command(stats.stats)
76
80
  cli.add_command(template.template)
77
81
  cli.add_command(tex.tex)
@@ -93,10 +97,8 @@ def _get_all_command_paths(group, prefix=""):
93
97
  return paths
94
98
 
95
99
 
96
- @cli.command("help-recursive")
97
- @click.pass_context
98
- def help_recursive(ctx):
99
- """Show help for all commands recursively."""
100
+ def _print_help_recursive(ctx):
101
+ """Print help for all commands recursively."""
100
102
  # Show main CLI help first
101
103
  click.secho("━━━ scitex ━━━", fg="cyan", bold=True)
102
104
  click.echo(cli.get_help(ctx))
@@ -111,49 +113,88 @@ def help_recursive(ctx):
111
113
  click.echo(cmd.get_help(sub_ctx))
112
114
 
113
115
 
114
- @cli.command()
115
- @click.option(
116
- "--shell",
117
- type=click.Choice(["bash", "zsh", "fish"], case_sensitive=False),
118
- help="Shell type (auto-detected if not provided)",
119
- )
120
- @click.option(
121
- "--show", is_flag=True, help="Show completion script instead of installing"
122
- )
123
- def completion(shell, show):
116
+ def _detect_shell() -> str | None:
117
+ """Auto-detect current shell."""
118
+ shell_env = os.environ.get("SHELL", "")
119
+ if "bash" in shell_env:
120
+ return "bash"
121
+ elif "zsh" in shell_env:
122
+ return "zsh"
123
+ elif "fish" in shell_env:
124
+ return "fish"
125
+ return None
126
+
127
+
128
+ def _get_rc_file(shell: str) -> str:
129
+ """Get shell config file path."""
130
+ if shell == "bash":
131
+ return os.path.expanduser("~/.bashrc")
132
+ elif shell == "zsh":
133
+ return os.path.expanduser("~/.zshrc")
134
+ elif shell == "fish":
135
+ return os.path.expanduser("~/.config/fish/config.fish")
136
+ return ""
137
+
138
+
139
+ def _generate_completion_script(shell: str) -> str:
140
+ """Generate completion script for scitex CLI."""
141
+ import shutil
142
+
143
+ cli_path = shutil.which("scitex")
144
+ if not cli_path:
145
+ return ""
146
+
147
+ if shell == "bash":
148
+ return f'# scitex tab completion\neval "$(_SCITEX_COMPLETE=bash_source {cli_path})"'
149
+ elif shell == "zsh":
150
+ return (
151
+ f'# scitex tab completion\neval "$(_SCITEX_COMPLETE=zsh_source {cli_path})"'
152
+ )
153
+ elif shell == "fish":
154
+ return f"# scitex tab completion\neval (env _SCITEX_COMPLETE=fish_source {cli_path})"
155
+ return ""
156
+
157
+
158
+ @cli.group(invoke_without_command=True)
159
+ @click.pass_context
160
+ def completion(ctx):
124
161
  """
125
- Install or show shell completion for scitex commands.
162
+ Shell completion for scitex CLI.
126
163
 
127
164
  \b
128
- Supported shells: bash, zsh, fish
165
+ Commands:
166
+ scitex completion install # Install completion (default)
167
+ scitex completion status # Check installation status
168
+ scitex completion bash # Show bash completion script
169
+ scitex completion zsh # Show zsh completion script
129
170
 
130
171
  \b
131
- Installation:
132
- # Auto-detect shell and install
133
- scitex completion
172
+ Quick install:
173
+ scitex completion install
174
+ """
175
+ if ctx.invoked_subcommand is None:
176
+ # Default to install
177
+ ctx.invoke(completion_install)
134
178
 
135
- # Specify shell
136
- scitex completion --shell bash
137
- scitex completion --shell zsh
138
179
 
139
- # Show completion script
140
- scitex completion --show
180
+ @completion.command("install")
181
+ @click.option(
182
+ "--shell",
183
+ type=click.Choice(["bash", "zsh", "fish"], case_sensitive=False),
184
+ help="Shell type (auto-detected if not provided).",
185
+ )
186
+ def completion_install(shell):
187
+ """
188
+ Install shell completion for scitex CLI.
141
189
 
142
190
  \b
143
- After installation, restart your shell or run:
144
- source ~/.bashrc # for bash
145
- source ~/.zshrc # for zsh
191
+ Examples:
192
+ scitex completion install # Auto-detect shell
193
+ scitex completion install --shell bash
146
194
  """
147
- # Auto-detect shell if not provided
148
195
  if not shell:
149
- shell_env = os.environ.get("SHELL", "")
150
- if "bash" in shell_env:
151
- shell = "bash"
152
- elif "zsh" in shell_env:
153
- shell = "zsh"
154
- elif "fish" in shell_env:
155
- shell = "fish"
156
- else:
196
+ shell = _detect_shell()
197
+ if not shell:
157
198
  click.secho(
158
199
  "Could not auto-detect shell. Please specify with --shell option.",
159
200
  fg="red",
@@ -162,72 +203,120 @@ def completion(shell, show):
162
203
  sys.exit(1)
163
204
 
164
205
  shell = shell.lower()
206
+ rc_file = _get_rc_file(shell)
207
+ completion_script = _generate_completion_script(shell)
165
208
 
166
- # Get full path to scitex executable
167
- scitex_path = sys.argv[0]
168
- if not os.path.isabs(scitex_path):
169
- # If relative path, find the full path
170
- import shutil
171
-
172
- scitex_full = shutil.which("scitex") or scitex_path
173
- else:
174
- scitex_full = scitex_path
175
-
176
- # Generate completion script
177
- if shell == "bash":
178
- rc_file = os.path.expanduser("~/.bashrc")
179
- eval_line = f'eval "$(_SCITEX_COMPLETE=bash_source {scitex_full})"'
180
- elif shell == "zsh":
181
- rc_file = os.path.expanduser("~/.zshrc")
182
- eval_line = f'eval "$(_SCITEX_COMPLETE=zsh_source {scitex_full})"'
183
- elif shell == "fish":
184
- rc_file = os.path.expanduser("~/.config/fish/config.fish")
185
- eval_line = f"eval (env _SCITEX_COMPLETE=fish_source {scitex_full})"
186
-
187
- if show:
188
- # Just show the completion script
189
- click.echo(f"Add this line to your {rc_file}:")
190
- click.echo()
191
- click.secho(eval_line, fg="green")
192
- sys.exit(0)
209
+ if not completion_script:
210
+ click.secho("scitex CLI not found in PATH.", fg="red", err=True)
211
+ sys.exit(1)
193
212
 
194
- # Check if already installed (and not commented out)
213
+ # Check if already installed
195
214
  if os.path.exists(rc_file):
196
215
  with open(rc_file) as f:
197
- for line in f:
198
- # Check if the line exists and is not commented
199
- stripped = line.strip()
200
- if stripped == eval_line and not stripped.startswith("#"):
201
- click.secho(
202
- f"Tab completion is already installed in {rc_file}", fg="yellow"
203
- )
204
- click.echo()
205
- click.echo("To reload, run:")
206
- click.secho(f" source {rc_file}", fg="cyan")
207
- sys.exit(0)
208
-
209
- # Install completion
216
+ content = f.read()
217
+ if "scitex tab completion" in content:
218
+ click.secho(f"Completion already installed in {rc_file}", fg="yellow")
219
+ click.echo(
220
+ "\nTo reinstall, first remove the existing block, then run again."
221
+ )
222
+ click.echo("\nTo reload, run:")
223
+ click.secho(f" source {rc_file}", fg="cyan")
224
+ sys.exit(0)
225
+
226
+ # Install
210
227
  try:
211
- # Create config directory if it doesn't exist (for fish)
212
228
  os.makedirs(os.path.dirname(rc_file), exist_ok=True)
213
-
214
229
  with open(rc_file, "a") as f:
215
- f.write("\n# SciTeX tab completion\n")
216
- f.write(f"{eval_line}\n")
230
+ f.write(f"\n{completion_script}\n")
217
231
 
218
- click.secho(f"Successfully installed tab completion to {rc_file}", fg="green")
219
- click.echo()
220
- click.echo("To activate completion in current shell, run:")
232
+ click.secho(f"Installed scitex completion to {rc_file}", fg="green")
233
+ click.echo("\nTo activate, run:")
221
234
  click.secho(f" source {rc_file}", fg="cyan")
222
- click.echo()
223
- click.echo("Or restart your shell.")
224
- sys.exit(0)
225
235
 
226
236
  except Exception as e:
227
- click.secho(f"ERROR: Failed to install completion: {e}", fg="red", err=True)
228
- click.echo()
229
- click.echo("You can manually add this line to your shell config:")
230
- click.secho(eval_line, fg="green")
237
+ click.secho(f"ERROR: {e}", fg="red", err=True)
238
+ click.echo("\nManually add to your shell config:")
239
+ click.echo(completion_script)
240
+ sys.exit(1)
241
+
242
+
243
+ @completion.command("status")
244
+ def completion_status():
245
+ """
246
+ Check shell completion installation status.
247
+
248
+ \b
249
+ Shows:
250
+ - Current shell
251
+ - Config file path
252
+ - Installation status
253
+ """
254
+ import shutil
255
+
256
+ shell = _detect_shell() or "unknown"
257
+ rc_file = _get_rc_file(shell) if shell != "unknown" else "N/A"
258
+
259
+ click.secho("Shell Completion Status", fg="cyan", bold=True)
260
+ click.echo(f" Shell: {shell}")
261
+ click.echo(f" Config: {rc_file}")
262
+
263
+ # Check if installed
264
+ installed = False
265
+ if rc_file != "N/A" and os.path.exists(rc_file):
266
+ with open(rc_file) as f:
267
+ content = f.read()
268
+ if "scitex tab completion" in content:
269
+ installed = True
270
+
271
+ status = (
272
+ click.style("installed", fg="green")
273
+ if installed
274
+ else click.style("not installed", fg="yellow")
275
+ )
276
+ click.echo(f" Status: {status}")
277
+
278
+ # Check if scitex is in PATH
279
+ cli_path = shutil.which("scitex")
280
+ path_status = (
281
+ click.style("OK", fg="green") if cli_path else click.style("missing", fg="red")
282
+ )
283
+ click.echo(f" scitex in PATH: {path_status}")
284
+
285
+ if not installed:
286
+ click.echo("\nTo install completion:")
287
+ click.secho(" scitex completion install", fg="cyan")
288
+
289
+
290
+ @completion.command("bash")
291
+ def completion_bash():
292
+ """Show bash completion script."""
293
+ script = _generate_completion_script("bash")
294
+ if script:
295
+ click.echo(script)
296
+ else:
297
+ click.secho("scitex CLI not found in PATH.", fg="red", err=True)
298
+ sys.exit(1)
299
+
300
+
301
+ @completion.command("zsh")
302
+ def completion_zsh():
303
+ """Show zsh completion script."""
304
+ script = _generate_completion_script("zsh")
305
+ if script:
306
+ click.echo(script)
307
+ else:
308
+ click.secho("scitex CLI not found in PATH.", fg="red", err=True)
309
+ sys.exit(1)
310
+
311
+
312
+ @completion.command("fish")
313
+ def completion_fish():
314
+ """Show fish completion script."""
315
+ script = _generate_completion_script("fish")
316
+ if script:
317
+ click.echo(script)
318
+ else:
319
+ click.secho("scitex CLI not found in PATH.", fg="red", err=True)
231
320
  sys.exit(1)
232
321
 
233
322
 
scitex/cli/mcp.py CHANGED
@@ -13,23 +13,26 @@ Commands:
13
13
  import click
14
14
 
15
15
 
16
- @click.group()
17
- def mcp():
16
+ @click.group(invoke_without_command=True)
17
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
18
+ @click.pass_context
19
+ def mcp(ctx, help_recursive):
18
20
  """
19
21
  MCP (Model Context Protocol) server management.
20
22
 
21
23
  \b
22
24
  Examples:
23
- scitex mcp list # List all tools
24
- scitex mcp list --module audio # List audio tools only
25
- scitex mcp doctor # Check server health
26
- scitex mcp serve # Start stdio server
27
- scitex mcp serve -t sse -p 8085 # Start SSE server
25
+ scitex mcp list-tools # List all tools
26
+ scitex mcp start # Start MCP server
28
27
  """
29
- pass
28
+ if help_recursive:
29
+ _print_help_recursive(ctx)
30
+ ctx.exit(0)
31
+ elif ctx.invoked_subcommand is None:
32
+ click.echo(ctx.get_help())
30
33
 
31
34
 
32
- @mcp.command("list")
35
+ @mcp.command("list-tools")
33
36
  @click.option(
34
37
  "--module",
35
38
  "-m",
@@ -44,9 +47,8 @@ def list_tools(module: str, as_json: bool):
44
47
 
45
48
  \b
46
49
  Examples:
47
- scitex mcp list # List all 106 tools
48
- scitex mcp list --module audio # List audio tools only
49
- scitex mcp list --json # Output as JSON
50
+ scitex mcp list-tools
51
+ scitex mcp list-tools --module audio
50
52
  """
51
53
  try:
52
54
  from scitex.mcp_server import FASTMCP_AVAILABLE
@@ -268,11 +270,8 @@ def doctor(verbose: bool):
268
270
  raise SystemExit(1 if issues else 0)
269
271
 
270
272
 
271
- @mcp.command("help-recursive")
272
- @click.pass_context
273
- def help_recursive(ctx):
274
- """Show help for all MCP commands recursively."""
275
- # Create a fake parent context to show "scitex mcp" in Usage
273
+ def _print_help_recursive(ctx):
274
+ """Print help for mcp and all its subcommands."""
276
275
  fake_parent = click.Context(click.Group(), info_name="scitex")
277
276
  parent_ctx = click.Context(mcp, info_name="mcp", parent=fake_parent)
278
277
 
@@ -289,7 +288,7 @@ def help_recursive(ctx):
289
288
  click.echo(cmd.get_help(sub_ctx))
290
289
 
291
290
 
292
- @mcp.command("serve")
291
+ @mcp.command("start")
293
292
  @click.option(
294
293
  "--transport",
295
294
  "-t",
@@ -301,27 +300,14 @@ def help_recursive(ctx):
301
300
  @click.option(
302
301
  "--port", "-p", default=8085, type=int, help="Port to bind (default: 8085)"
303
302
  )
304
- def serve(transport: str, host: str, port: int):
303
+ def start(transport: str, host: str, port: int):
305
304
  """
306
305
  Start the unified MCP server.
307
306
 
308
- \b
309
- Transport types:
310
- stdio - Standard I/O (for Claude Desktop)
311
- sse - Server-Sent Events (for remote via SSH)
312
- http - HTTP streamable (for web clients)
313
-
314
307
  \b
315
308
  Examples:
316
- scitex mcp serve # Start stdio server
317
- scitex mcp serve -t sse -p 8085 # Start SSE server
318
- scitex mcp serve -t http -p 8085 # Start HTTP server
319
-
320
- \b
321
- Remote Setup (SSE):
322
- 1. Local: scitex mcp serve -t sse -p 8085
323
- 2. SSH: ssh -R 8085:localhost:8085 remote-host
324
- 3. Config: {"type": "sse", "url": "http://localhost:8085/sse"}
309
+ scitex mcp start
310
+ scitex mcp start -t sse -p 8085
325
311
  """
326
312
  try:
327
313
  from scitex.mcp_server import run_server
@@ -333,4 +319,44 @@ def serve(transport: str, host: str, port: int):
333
319
  run_server(transport=transport, host=host, port=port)
334
320
 
335
321
 
322
+ @mcp.command("installation")
323
+ def installation():
324
+ """
325
+ Show Claude Desktop configuration for SciTeX MCP server.
326
+
327
+ \b
328
+ Examples:
329
+ scitex mcp installation
330
+ """
331
+ import shutil
332
+ import sys
333
+
334
+ from scitex import __version__
335
+
336
+ click.secho(f"SciTeX MCP Server v{__version__}", fg="cyan", bold=True)
337
+ click.echo()
338
+
339
+ # Get actual path
340
+ scitex_path = shutil.which("scitex")
341
+ python_path = sys.executable
342
+
343
+ if scitex_path:
344
+ click.echo(f"Your installation path: {scitex_path}")
345
+ click.echo(f"Your Python path: {python_path}")
346
+ click.echo()
347
+
348
+ click.secho("Claude Desktop Configuration:", fg="green", bold=True)
349
+ click.echo(
350
+ "Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)"
351
+ )
352
+ click.echo("or %APPDATA%\\Claude\\claude_desktop_config.json (Windows):")
353
+ click.echo()
354
+
355
+ config = f'''"scitex": {{
356
+ "command": "{scitex_path or "/path/to/.venv/bin/scitex"}",
357
+ "args": ["mcp", "start"]
358
+ }}'''
359
+ click.secho(config, fg="yellow")
360
+
361
+
336
362
  # EOF
@@ -15,12 +15,19 @@ 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
@@ -79,6 +86,7 @@ def help_recursive(ctx):
79
86
  click.echo(sub_cmd.get_help(sub_sub_ctx))
80
87
 
81
88
 
89
+ scholar.add_command(crossref_scitex)
82
90
  scholar.add_command(fetch)
83
91
  scholar.add_command(library)
84
92
  scholar.add_command(config)