scitex 2.17.0__py3-none-any.whl → 2.17.3__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 (73) hide show
  1. scitex/_dev/__init__.py +122 -0
  2. scitex/_dev/_config.py +391 -0
  3. scitex/_dev/_dashboard/__init__.py +11 -0
  4. scitex/_dev/_dashboard/_app.py +89 -0
  5. scitex/_dev/_dashboard/_routes.py +169 -0
  6. scitex/_dev/_dashboard/_scripts.py +301 -0
  7. scitex/_dev/_dashboard/_styles.py +205 -0
  8. scitex/_dev/_dashboard/_templates.py +117 -0
  9. scitex/_dev/_dashboard/static/version-dashboard-favicon.svg +12 -0
  10. scitex/_dev/_ecosystem.py +109 -0
  11. scitex/_dev/_github.py +360 -0
  12. scitex/_dev/_mcp/__init__.py +11 -0
  13. scitex/_dev/_mcp/handlers.py +182 -0
  14. scitex/_dev/_ssh.py +332 -0
  15. scitex/_dev/_versions.py +272 -0
  16. scitex/_mcp_tools/__init__.py +2 -0
  17. scitex/_mcp_tools/dev.py +186 -0
  18. scitex/audio/_audio_check.py +84 -41
  19. scitex/cli/capture.py +45 -22
  20. scitex/cli/dev.py +494 -0
  21. scitex/cli/main.py +2 -0
  22. scitex/cli/stats.py +48 -20
  23. scitex/cli/verify.py +33 -36
  24. scitex/plt/__init__.py +16 -6
  25. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  26. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  27. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  28. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  29. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  30. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  31. scitex/template/__init__.py +18 -1
  32. scitex/template/clone_research_minimal.py +111 -0
  33. scitex/verify/README.md +0 -12
  34. scitex/verify/__init__.py +0 -4
  35. scitex/verify/_visualize.py +0 -4
  36. scitex/verify/_viz/__init__.py +0 -18
  37. {scitex-2.17.0.dist-info → scitex-2.17.3.dist-info}/METADATA +2 -1
  38. {scitex-2.17.0.dist-info → scitex-2.17.3.dist-info}/RECORD +41 -49
  39. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  40. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  41. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  42. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  43. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  44. scitex/scholar/data/.gitkeep +0 -0
  45. scitex/scholar/data/README.md +0 -44
  46. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  47. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  48. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  49. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  50. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  51. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  52. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  53. scitex/scholar/data/bib_files/pac.bib +0 -698
  54. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  55. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  56. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  57. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  58. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  59. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  60. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  61. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  62. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  63. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  64. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  65. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  66. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  67. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  68. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  69. scitex/scholar/data/impact_factor.db +0 -0
  70. scitex/verify/_viz/_plotly.py +0 -193
  71. {scitex-2.17.0.dist-info → scitex-2.17.3.dist-info}/WHEEL +0 -0
  72. {scitex-2.17.0.dist-info → scitex-2.17.3.dist-info}/entry_points.txt +0 -0
  73. {scitex-2.17.0.dist-info → scitex-2.17.3.dist-info}/licenses/LICENSE +0 -0
scitex/cli/capture.py CHANGED
@@ -406,32 +406,55 @@ def doctor():
406
406
 
407
407
 
408
408
  @mcp.command("list-tools")
409
- def list_tools():
410
- """
411
- List available MCP tools
412
-
413
- \b
414
- Example:
415
- scitex capture mcp list-tools
416
- """
409
+ @click.option("-v", "--verbose", count=True, help="-v params, -vv returns")
410
+ def list_tools(verbose):
411
+ """List available MCP tools for capture."""
417
412
  click.secho("Capture MCP Tools", fg="cyan", bold=True)
418
413
  click.echo()
414
+ # (name, desc, params, returns)
419
415
  tools = [
420
- ("capture_capture_screenshot", "Capture screenshot"),
421
- ("capture_capture_window", "Capture specific window"),
422
- ("capture_start_monitoring", "Start continuous capture"),
423
- ("capture_stop_monitoring", "Stop monitoring"),
424
- ("capture_get_monitoring_status", "Get monitoring status"),
425
- ("capture_analyze_screenshot", "Analyze screenshot for errors"),
426
- ("capture_list_recent_screenshots", "List recent screenshots"),
427
- ("capture_clear_cache", "Clear screenshot cache"),
428
- ("capture_create_gif", "Create animated GIF"),
429
- ("capture_list_sessions", "List monitoring sessions"),
430
- ("capture_get_info", "Get monitor/window info"),
431
- ("capture_list_windows", "List visible windows"),
416
+ (
417
+ "capture_screenshot",
418
+ "Capture screenshot",
419
+ "output_path=None, monitor=0",
420
+ "str",
421
+ ),
422
+ (
423
+ "capture_window",
424
+ "Capture specific window",
425
+ "window_id: str, output=None",
426
+ "str",
427
+ ),
428
+ (
429
+ "start_monitoring",
430
+ "Start continuous capture",
431
+ "interval=5.0, monitor=0",
432
+ "str",
433
+ ),
434
+ ("stop_monitoring", "Stop monitoring", "session_id=None", "str"),
435
+ ("get_monitoring_status", "Get monitoring status", "", "JSON"),
436
+ (
437
+ "analyze_screenshot",
438
+ "Analyze screenshot for errors",
439
+ "image_path: str",
440
+ "JSON",
441
+ ),
442
+ ("list_recent_screenshots", "List recent screenshots", "limit=10", "JSON"),
443
+ ("clear_cache", "Clear screenshot cache", "older_than_hours=24", "JSON"),
444
+ ("create_gif", "Create animated GIF", "session_id: str, output=None", "str"),
445
+ ("list_sessions", "List monitoring sessions", "", "JSON"),
446
+ ("get_info", "Get monitor/window info", "", "JSON"),
447
+ ("list_windows", "List visible windows", "", "JSON"),
432
448
  ]
433
- for name, desc in tools:
434
- click.echo(f" {name}: {desc}")
449
+ for name, desc, params, returns in tools:
450
+ click.secho(f" capture_{name}", fg="green", bold=True, nl=False)
451
+ click.echo(f": {desc}")
452
+ if verbose >= 1 and params:
453
+ click.echo(f" params: {params}")
454
+ if verbose >= 2:
455
+ click.echo(f" returns: {returns}")
456
+ if verbose >= 1:
457
+ click.echo()
435
458
 
436
459
 
437
460
  @capture.command("list-python-apis")
scitex/cli/dev.py ADDED
@@ -0,0 +1,494 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-02-02
3
+ # File: scitex/cli/dev.py
4
+
5
+ """
6
+ SciTeX Developer CLI Commands (Internal).
7
+
8
+ Commands for managing and inspecting the scitex ecosystem.
9
+ """
10
+
11
+ import click
12
+
13
+
14
+ @click.group(
15
+ context_settings={"help_option_names": ["-h", "--help"]},
16
+ invoke_without_command=True,
17
+ )
18
+ @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
19
+ @click.pass_context
20
+ def dev(ctx, help_recursive):
21
+ """
22
+ Developer utilities (internal).
23
+
24
+ \b
25
+ Examples:
26
+ scitex dev versions # List all ecosystem versions
27
+ scitex dev versions --check # Check version consistency
28
+ scitex dev versions --json # Output as JSON
29
+ scitex dev versions -p scitex # Check specific package
30
+ """
31
+ if help_recursive:
32
+ from . import print_help_recursive
33
+
34
+ print_help_recursive(ctx, dev)
35
+ ctx.exit(0)
36
+ elif ctx.invoked_subcommand is None:
37
+ click.echo(ctx.get_help())
38
+
39
+
40
+ @dev.command("versions")
41
+ @click.option(
42
+ "--check",
43
+ is_flag=True,
44
+ help="Check version consistency and show summary",
45
+ )
46
+ @click.option(
47
+ "--json",
48
+ "as_json",
49
+ is_flag=True,
50
+ help="Output as JSON",
51
+ )
52
+ @click.option(
53
+ "-p",
54
+ "--package",
55
+ multiple=True,
56
+ help="Filter to specific package(s)",
57
+ )
58
+ @click.option(
59
+ "--local-only",
60
+ is_flag=True,
61
+ help="Skip remote (PyPI) version checks",
62
+ )
63
+ @click.option(
64
+ "--host",
65
+ multiple=True,
66
+ help="Check specific SSH host(s)",
67
+ )
68
+ @click.option(
69
+ "--all-hosts",
70
+ is_flag=True,
71
+ help="Check all configured SSH hosts",
72
+ )
73
+ @click.option(
74
+ "--remote",
75
+ multiple=True,
76
+ help="Check specific GitHub remote(s)",
77
+ )
78
+ @click.option(
79
+ "--all-remotes",
80
+ is_flag=True,
81
+ help="Check all configured GitHub remotes",
82
+ )
83
+ def versions(check, as_json, package, local_only, host, all_hosts, remote, all_remotes):
84
+ """
85
+ List versions across the scitex ecosystem.
86
+
87
+ \b
88
+ Shows version information from multiple sources:
89
+ - pyproject.toml (local source)
90
+ - installed package (pip/importlib.metadata)
91
+ - git tag (latest version tag)
92
+ - git branch (current branch)
93
+ - PyPI (remote published version)
94
+ - SSH hosts (if configured)
95
+ - GitHub remotes (if configured)
96
+
97
+ \b
98
+ Examples:
99
+ scitex dev versions # List all versions
100
+ scitex dev versions --check # Check consistency
101
+ scitex dev versions --json # JSON output
102
+ scitex dev versions -p scitex # Single package
103
+ scitex dev versions -p scitex -p figrecipe # Multiple packages
104
+ scitex dev versions --local-only # Skip PyPI checks
105
+ scitex dev versions --host myhost # Check specific host
106
+ scitex dev versions --all-hosts # Check all hosts
107
+ scitex dev versions --remote ywatanabe1989 # Check GitHub remote
108
+ scitex dev versions --all-remotes # Check all remotes
109
+ """
110
+ import json as json_module
111
+
112
+ from scitex._dev import check_versions, list_versions
113
+
114
+ packages = list(package) if package else None
115
+
116
+ if check:
117
+ result = check_versions(packages)
118
+ if local_only:
119
+ for pkg_info in result["packages"].values():
120
+ pkg_info.get("remote", {}).pop("pypi", None)
121
+ else:
122
+ result = list_versions(packages)
123
+ if local_only:
124
+ for pkg_info in result.values():
125
+ pkg_info.get("remote", {}).pop("pypi", None)
126
+
127
+ # Add host data if requested
128
+ if host or all_hosts:
129
+ from scitex._dev import check_all_hosts
130
+
131
+ hosts_filter = list(host) if host else None
132
+ try:
133
+ result["hosts"] = check_all_hosts(packages=packages, hosts=hosts_filter)
134
+ except Exception as e:
135
+ result["hosts"] = {"error": str(e)}
136
+
137
+ # Add remote data if requested
138
+ if remote or all_remotes:
139
+ from scitex._dev import check_all_remotes
140
+
141
+ remotes_filter = list(remote) if remote else None
142
+ try:
143
+ result["remotes"] = check_all_remotes(
144
+ packages=packages, remotes=remotes_filter
145
+ )
146
+ except Exception as e:
147
+ result["remotes"] = {"error": str(e)}
148
+
149
+ if as_json:
150
+ click.echo(json_module.dumps(result, indent=2))
151
+ return
152
+
153
+ # Human-readable output
154
+ if check:
155
+ _print_check_result(result)
156
+ else:
157
+ _print_versions(result)
158
+
159
+ # Print host data
160
+ if "hosts" in result and result["hosts"]:
161
+ _print_hosts(result["hosts"])
162
+
163
+ # Print remote data
164
+ if "remotes" in result and result["remotes"]:
165
+ _print_remotes(result["remotes"])
166
+
167
+
168
+ def _print_versions(versions: dict) -> None:
169
+ """Print version information in human-readable format."""
170
+ click.secho("SciTeX Ecosystem Versions", fg="cyan", bold=True)
171
+ click.echo("=" * 60)
172
+ click.echo()
173
+
174
+ for pkg, info in versions.items():
175
+ status = info.get("status", "unknown")
176
+ status_color = {
177
+ "ok": "green",
178
+ "unreleased": "yellow",
179
+ "mismatch": "red",
180
+ "outdated": "magenta",
181
+ "unavailable": "white",
182
+ "unknown": "white",
183
+ }.get(status, "white")
184
+
185
+ click.secho(f"{pkg}", fg="cyan", bold=True, nl=False)
186
+ click.echo(" ", nl=False)
187
+ click.secho(f"[{status}]", fg=status_color)
188
+
189
+ # Local versions
190
+ local = info.get("local", {})
191
+ if local.get("pyproject_toml"):
192
+ click.echo(f" toml: {local['pyproject_toml']}")
193
+ if local.get("installed"):
194
+ click.echo(f" installed: {local['installed']}")
195
+
196
+ # Git info
197
+ git = info.get("git", {})
198
+ if git.get("latest_tag"):
199
+ click.echo(f" git tag: {git['latest_tag']}")
200
+ if git.get("branch"):
201
+ click.echo(f" branch: {git['branch']}")
202
+
203
+ # Remote
204
+ remote = info.get("remote", {})
205
+ if remote.get("pypi"):
206
+ click.echo(f" pypi: {remote['pypi']}")
207
+
208
+ # Issues
209
+ issues = info.get("issues", [])
210
+ if issues:
211
+ for issue in issues:
212
+ click.secho(f" ! {issue}", fg="yellow")
213
+
214
+ click.echo()
215
+
216
+
217
+ def _print_check_result(result: dict) -> None:
218
+ """Print version check result with summary."""
219
+ _print_versions(result.get("packages", {}))
220
+
221
+ summary = result.get("summary", {})
222
+ click.secho("Summary", fg="cyan", bold=True)
223
+ click.echo("-" * 30)
224
+
225
+ total = summary.get("total", 0)
226
+ ok = summary.get("ok", 0)
227
+ unreleased = summary.get("unreleased", 0)
228
+ mismatch = summary.get("mismatch", 0)
229
+ outdated = summary.get("outdated", 0)
230
+ unavailable = summary.get("unavailable", 0)
231
+
232
+ click.echo(f" Total: {total}")
233
+ click.secho(f" OK: {ok}", fg="green" if ok else "white")
234
+ if unreleased:
235
+ click.secho(f" Unreleased: {unreleased}", fg="yellow")
236
+ if mismatch:
237
+ click.secho(f" Mismatch: {mismatch}", fg="red")
238
+ if outdated:
239
+ click.secho(f" Outdated: {outdated}", fg="magenta")
240
+ if unavailable:
241
+ click.secho(f" Unavailable: {unavailable}", fg="white")
242
+
243
+ click.echo()
244
+ if mismatch > 0:
245
+ click.secho("Some packages have version mismatches!", fg="red", bold=True)
246
+ elif unreleased > 0:
247
+ click.secho("Some packages are ready to release.", fg="yellow")
248
+ else:
249
+ click.secho("All versions are consistent.", fg="green", bold=True)
250
+
251
+
252
+ def _print_hosts(hosts_data: dict) -> None:
253
+ """Print host version data."""
254
+ click.echo()
255
+ click.secho("SSH Hosts", fg="cyan", bold=True)
256
+ click.echo("-" * 40)
257
+
258
+ if "error" in hosts_data:
259
+ click.secho(f" Error: {hosts_data['error']}", fg="red")
260
+ return
261
+
262
+ for host_name, host_info in hosts_data.items():
263
+ if host_name.startswith("_"):
264
+ continue
265
+ click.secho(f" {host_name}", fg="yellow", bold=True)
266
+ meta = host_info.get("_host", {})
267
+ if meta:
268
+ click.echo(f" ({meta.get('hostname', '')} - {meta.get('role', '')})")
269
+ for pkg, pkg_info in host_info.items():
270
+ if pkg.startswith("_"):
271
+ continue
272
+ status = pkg_info.get("status", "unknown")
273
+ installed = pkg_info.get("installed", "-")
274
+ color = (
275
+ "green" if status == "ok" else "red" if status == "error" else "yellow"
276
+ )
277
+ click.echo(f" {pkg}: ", nl=False)
278
+ click.secho(f"{installed}", fg=color)
279
+
280
+
281
+ def _print_remotes(remotes_data: dict) -> None:
282
+ """Print GitHub remote version data."""
283
+ click.echo()
284
+ click.secho("GitHub Remotes", fg="cyan", bold=True)
285
+ click.echo("-" * 40)
286
+
287
+ if "error" in remotes_data:
288
+ click.secho(f" Error: {remotes_data['error']}", fg="red")
289
+ return
290
+
291
+ for remote_name, remote_info in remotes_data.items():
292
+ if remote_name.startswith("_"):
293
+ continue
294
+ click.secho(f" {remote_name}", fg="yellow", bold=True)
295
+ meta = remote_info.get("_remote", {})
296
+ if meta:
297
+ click.echo(f" (org: {meta.get('org', '')})")
298
+ for pkg, pkg_info in remote_info.items():
299
+ if pkg.startswith("_"):
300
+ continue
301
+ tag = pkg_info.get("latest_tag", "-")
302
+ release = pkg_info.get("release", "-")
303
+ click.echo(f" {pkg}: tag={tag}, release={release}")
304
+
305
+
306
+ # MCP subgroup
307
+ @dev.group(invoke_without_command=True)
308
+ @click.pass_context
309
+ def mcp(ctx):
310
+ """
311
+ MCP (Model Context Protocol) server operations.
312
+
313
+ \b
314
+ Commands:
315
+ list-tools - List available MCP tools
316
+
317
+ \b
318
+ Examples:
319
+ scitex dev mcp list-tools
320
+ """
321
+ if ctx.invoked_subcommand is None:
322
+ click.echo(ctx.get_help())
323
+
324
+
325
+ @mcp.command("list-tools")
326
+ @click.option("-v", "--verbose", count=True, help="-v params, -vv returns")
327
+ def list_tools(verbose):
328
+ """List available MCP tools for dev module."""
329
+ click.secho("Dev MCP Tools", fg="cyan", bold=True)
330
+ click.echo()
331
+ tools = [
332
+ ("dev_list_versions", "List versions across ecosystem", "packages", "JSON"),
333
+ ("dev_check_versions", "Check version consistency", "packages", "JSON"),
334
+ ("dev_check_hosts", "Check versions on SSH hosts", "packages, hosts", "JSON"),
335
+ ("dev_check_remotes", "Check versions on GitHub", "packages, remotes", "JSON"),
336
+ ("dev_get_config", "Get current configuration", "", "JSON"),
337
+ (
338
+ "dev_full_versions",
339
+ "Get comprehensive data",
340
+ "packages, hosts, remotes",
341
+ "JSON",
342
+ ),
343
+ ]
344
+ for name, desc, params, returns in tools:
345
+ click.secho(f" {name}", fg="green", bold=True, nl=False)
346
+ click.echo(f": {desc}")
347
+ if verbose >= 1 and params:
348
+ click.echo(f" params: {params}")
349
+ if verbose >= 2 and returns:
350
+ click.echo(f" returns: {returns}")
351
+
352
+
353
+ @dev.command("list-python-apis")
354
+ @click.option("-v", "--verbose", count=True, help="Verbosity: -v +doc, -vv full doc")
355
+ @click.option("-d", "--max-depth", type=int, default=5, help="Max recursion depth")
356
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
357
+ @click.pass_context
358
+ def list_python_apis(ctx, verbose, max_depth, as_json):
359
+ """List Python APIs (alias for: scitex introspect api scitex._dev)."""
360
+ from scitex.cli.introspect import api
361
+
362
+ ctx.invoke(
363
+ api,
364
+ dotted_path="scitex._dev",
365
+ verbose=verbose,
366
+ max_depth=max_depth,
367
+ as_json=as_json,
368
+ )
369
+
370
+
371
+ @dev.command("dashboard")
372
+ @click.option("--host", default="127.0.0.1", help="Host to bind to")
373
+ @click.option("--port", "-p", default=5000, type=int, help="Port to listen on")
374
+ @click.option("--debug", is_flag=True, help="Enable debug mode")
375
+ @click.option("--no-browser", is_flag=True, help="Don't open browser")
376
+ def dashboard(host, port, debug, no_browser):
377
+ """
378
+ Start the Flask version dashboard.
379
+
380
+ \b
381
+ Examples:
382
+ scitex dev dashboard # Start on localhost:5000
383
+ scitex dev dashboard --port 5001 # Custom port
384
+ scitex dev dashboard --no-browser # Don't open browser
385
+ """
386
+ from scitex._dev import run_dashboard
387
+
388
+ run_dashboard(host=host, port=port, debug=debug, open_browser=not no_browser)
389
+
390
+
391
+ # Config subgroup
392
+ @dev.group(invoke_without_command=True)
393
+ @click.pass_context
394
+ def config(ctx):
395
+ """
396
+ Configuration management.
397
+
398
+ \b
399
+ Commands:
400
+ show - Show current configuration
401
+ validate - Validate configuration file
402
+ create - Create default config file
403
+
404
+ \b
405
+ Examples:
406
+ scitex dev config show
407
+ scitex dev config create
408
+ """
409
+ if ctx.invoked_subcommand is None:
410
+ click.echo(ctx.get_help())
411
+
412
+
413
+ @config.command("show")
414
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
415
+ def config_show(as_json):
416
+ """Show current configuration."""
417
+ import json as json_module
418
+
419
+ from scitex._dev import get_config_path, load_config
420
+
421
+ config_path = get_config_path()
422
+ cfg = load_config()
423
+
424
+ if as_json:
425
+ data = {
426
+ "config_path": str(config_path),
427
+ "exists": config_path.exists(),
428
+ "packages": [p.name for p in cfg.packages],
429
+ "hosts": [{"name": h.name, "enabled": h.enabled} for h in cfg.hosts],
430
+ "remotes": [
431
+ {"name": r.name, "enabled": r.enabled} for r in cfg.github_remotes
432
+ ],
433
+ "branches": cfg.branches,
434
+ }
435
+ click.echo(json_module.dumps(data, indent=2))
436
+ return
437
+
438
+ click.secho("Configuration", fg="cyan", bold=True)
439
+ click.echo(f" Path: {config_path}")
440
+ click.echo(f" Exists: {config_path.exists()}")
441
+ click.echo()
442
+ click.secho("Packages:", fg="yellow")
443
+ for p in cfg.packages:
444
+ click.echo(f" - {p.name} ({p.pypi_name})")
445
+ click.echo()
446
+ click.secho("Hosts:", fg="yellow")
447
+ for h in cfg.hosts:
448
+ status = "enabled" if h.enabled else "disabled"
449
+ click.echo(f" - {h.name} ({h.hostname}) [{status}]")
450
+ click.echo()
451
+ click.secho("GitHub Remotes:", fg="yellow")
452
+ for r in cfg.github_remotes:
453
+ status = "enabled" if r.enabled else "disabled"
454
+ click.echo(f" - {r.name} (org: {r.org}) [{status}]")
455
+
456
+
457
+ @config.command("create")
458
+ @click.option("--force", is_flag=True, help="Overwrite existing config")
459
+ def config_create(force):
460
+ """Create default configuration file."""
461
+ from scitex._dev import create_default_config, get_config_path
462
+
463
+ config_path = get_config_path()
464
+ if config_path.exists() and not force:
465
+ click.secho(f"Config already exists: {config_path}", fg="yellow")
466
+ click.echo("Use --force to overwrite.")
467
+ return
468
+
469
+ path = create_default_config()
470
+ click.secho(f"Created config: {path}", fg="green")
471
+
472
+
473
+ @config.command("validate")
474
+ def config_validate():
475
+ """Validate configuration file."""
476
+ from scitex._dev import get_config_path, load_config
477
+
478
+ config_path = get_config_path()
479
+ if not config_path.exists():
480
+ click.secho(f"Config not found: {config_path}", fg="red")
481
+ click.echo("Run 'scitex dev config create' to create one.")
482
+ return
483
+
484
+ try:
485
+ cfg = load_config()
486
+ click.secho("Configuration is valid.", fg="green")
487
+ click.echo(f" Packages: {len(cfg.packages)}")
488
+ click.echo(f" Hosts: {len(cfg.hosts)}")
489
+ click.echo(f" Remotes: {len(cfg.github_remotes)}")
490
+ except Exception as e:
491
+ click.secho(f"Configuration error: {e}", fg="red")
492
+
493
+
494
+ # EOF
scitex/cli/main.py CHANGED
@@ -30,6 +30,7 @@ from . import (
30
30
  config,
31
31
  convert,
32
32
  dataset,
33
+ dev,
33
34
  introspect,
34
35
  mcp,
35
36
  plt,
@@ -87,6 +88,7 @@ cli.add_command(cloud.cloud)
87
88
  cli.add_command(config.config)
88
89
  cli.add_command(convert.convert)
89
90
  cli.add_command(dataset.dataset)
91
+ cli.add_command(dev.dev)
90
92
  cli.add_command(introspect.introspect)
91
93
  cli.add_command(mcp.mcp)
92
94
  cli.add_command(plt.plt)
scitex/cli/stats.py CHANGED
@@ -438,30 +438,58 @@ def doctor():
438
438
 
439
439
 
440
440
  @mcp.command("list-tools")
441
- def list_tools():
442
- """
443
- List available MCP tools
444
-
445
- \b
446
- Example:
447
- scitex stats mcp list-tools
448
- """
441
+ @click.option("-v", "--verbose", count=True, help="-v params, -vv returns")
442
+ def list_tools(verbose):
443
+ """List available MCP tools for statistics."""
449
444
  click.secho("Stats MCP Tools", fg="cyan", bold=True)
450
445
  click.echo()
446
+ # (name, desc, params, returns)
451
447
  tools = [
452
- ("stats_recommend_tests", "Recommend appropriate statistical tests"),
453
- ("stats_run_test", "Execute a statistical test on data"),
454
- ("stats_format_results", "Format results in journal style"),
455
- ("stats_power_analysis", "Calculate power or sample size"),
456
- ("stats_correct_pvalues", "Apply multiple comparison correction"),
457
- ("stats_describe", "Calculate descriptive statistics"),
458
- ("stats_effect_size", "Calculate effect size"),
459
- ("stats_normality_test", "Test for normal distribution"),
460
- ("stats_posthoc_test", "Run post-hoc pairwise comparisons"),
461
- ("stats_p_to_stars", "Convert p-value to significance stars"),
448
+ (
449
+ "recommend_tests",
450
+ "Recommend statistical tests",
451
+ "data_description: str",
452
+ "JSON",
453
+ ),
454
+ (
455
+ "run_test",
456
+ "Execute a statistical test",
457
+ "test_name: str, data: list",
458
+ "JSON",
459
+ ),
460
+ ("format_results", "Format results in journal style", "results: dict", "str"),
461
+ (
462
+ "power_analysis",
463
+ "Calculate power or sample size",
464
+ "test: str, effect=0.5",
465
+ "JSON",
466
+ ),
467
+ (
468
+ "correct_pvalues",
469
+ "Apply multiple comparison correction",
470
+ "pvalues: list",
471
+ "JSON",
472
+ ),
473
+ ("describe", "Calculate descriptive statistics", "data: list", "JSON"),
474
+ ("effect_size", "Calculate effect size", "group1: list, group2: list", "JSON"),
475
+ ("normality_test", "Test for normal distribution", "data: list", "JSON"),
476
+ ("posthoc_test", "Run post-hoc pairwise comparisons", "groups: list", "JSON"),
477
+ (
478
+ "p_to_stars",
479
+ "Convert p-value to significance stars",
480
+ "p_value: float",
481
+ "str",
482
+ ),
462
483
  ]
463
- for name, desc in tools:
464
- click.echo(f" {name}: {desc}")
484
+ for name, desc, params, returns in tools:
485
+ click.secho(f" stats_{name}", fg="green", bold=True, nl=False)
486
+ click.echo(f": {desc}")
487
+ if verbose >= 1 and params:
488
+ click.echo(f" params: {params}")
489
+ if verbose >= 2:
490
+ click.echo(f" returns: {returns}")
491
+ if verbose >= 1:
492
+ click.echo()
465
493
 
466
494
 
467
495
  @stats.command("list-python-apis")