scitex 2.16.2__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.
- scitex/_dev/__init__.py +122 -0
- scitex/_dev/_config.py +391 -0
- scitex/_dev/_dashboard/__init__.py +11 -0
- scitex/_dev/_dashboard/_app.py +89 -0
- scitex/_dev/_dashboard/_routes.py +169 -0
- scitex/_dev/_dashboard/_scripts.py +301 -0
- scitex/_dev/_dashboard/_styles.py +205 -0
- scitex/_dev/_dashboard/_templates.py +117 -0
- scitex/_dev/_dashboard/static/version-dashboard-favicon.svg +12 -0
- scitex/_dev/_ecosystem.py +109 -0
- scitex/_dev/_github.py +360 -0
- scitex/_dev/_mcp/__init__.py +11 -0
- scitex/_dev/_mcp/handlers.py +182 -0
- scitex/_dev/_ssh.py +332 -0
- scitex/_dev/_versions.py +272 -0
- scitex/_mcp_resources/_cheatsheet.py +1 -1
- scitex/_mcp_resources/_modules.py +1 -1
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/dev.py +186 -0
- scitex/_mcp_tools/verify.py +256 -0
- scitex/audio/_audio_check.py +84 -41
- scitex/cli/capture.py +45 -22
- scitex/cli/dev.py +494 -0
- scitex/cli/main.py +4 -0
- scitex/cli/stats.py +48 -20
- scitex/cli/verify.py +473 -0
- scitex/dev/plt/__init__.py +1 -1
- scitex/dev/plt/mpl/get_dir_ax.py +1 -1
- scitex/dev/plt/mpl/get_signatures.py +1 -1
- scitex/dev/plt/mpl/get_signatures_details.py +1 -1
- scitex/io/_load.py +8 -1
- scitex/io/_save.py +12 -0
- scitex/plt/__init__.py +16 -6
- scitex/session/README.md +2 -2
- scitex/session/__init__.py +1 -0
- scitex/session/_decorator.py +57 -33
- scitex/session/_lifecycle/__init__.py +23 -0
- scitex/session/_lifecycle/_close.py +225 -0
- scitex/session/_lifecycle/_config.py +112 -0
- scitex/session/_lifecycle/_matplotlib.py +83 -0
- scitex/session/_lifecycle/_start.py +246 -0
- scitex/session/_lifecycle/_utils.py +186 -0
- scitex/session/_manager.py +40 -3
- scitex/session/template.py +1 -1
- scitex/template/__init__.py +18 -1
- scitex/template/_templates/plt.py +1 -1
- scitex/template/_templates/session.py +1 -1
- scitex/template/clone_research_minimal.py +111 -0
- scitex/verify/README.md +300 -0
- scitex/verify/__init__.py +208 -0
- scitex/verify/_chain.py +369 -0
- scitex/verify/_db.py +600 -0
- scitex/verify/_hash.py +187 -0
- scitex/verify/_integration.py +127 -0
- scitex/verify/_rerun.py +253 -0
- scitex/verify/_tracker.py +330 -0
- scitex/verify/_visualize.py +44 -0
- scitex/verify/_viz/__init__.py +38 -0
- scitex/verify/_viz/_colors.py +84 -0
- scitex/verify/_viz/_format.py +302 -0
- scitex/verify/_viz/_json.py +192 -0
- scitex/verify/_viz/_mermaid.py +440 -0
- scitex/verify/_viz/_templates.py +246 -0
- scitex/verify/_viz/_utils.py +56 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/METADATA +2 -1
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/RECORD +69 -28
- scitex/session/_lifecycle.py +0 -827
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/WHEEL +0 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/entry_points.txt +0 -0
- {scitex-2.16.2.dist-info → scitex-2.17.3.dist-info}/licenses/LICENSE +0 -0
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,
|
|
@@ -41,6 +42,7 @@ from . import (
|
|
|
41
42
|
stats,
|
|
42
43
|
template,
|
|
43
44
|
tex,
|
|
45
|
+
verify,
|
|
44
46
|
web,
|
|
45
47
|
writer,
|
|
46
48
|
)
|
|
@@ -86,6 +88,7 @@ cli.add_command(cloud.cloud)
|
|
|
86
88
|
cli.add_command(config.config)
|
|
87
89
|
cli.add_command(convert.convert)
|
|
88
90
|
cli.add_command(dataset.dataset)
|
|
91
|
+
cli.add_command(dev.dev)
|
|
89
92
|
cli.add_command(introspect.introspect)
|
|
90
93
|
cli.add_command(mcp.mcp)
|
|
91
94
|
cli.add_command(plt.plt)
|
|
@@ -97,6 +100,7 @@ cli.add_command(social.social)
|
|
|
97
100
|
cli.add_command(stats.stats)
|
|
98
101
|
cli.add_command(template.template)
|
|
99
102
|
cli.add_command(tex.tex)
|
|
103
|
+
cli.add_command(verify.verify)
|
|
100
104
|
cli.add_command(web.web)
|
|
101
105
|
cli.add_command(writer.writer)
|
|
102
106
|
|
scitex/cli/stats.py
CHANGED
|
@@ -438,30 +438,58 @@ def doctor():
|
|
|
438
438
|
|
|
439
439
|
|
|
440
440
|
@mcp.command("list-tools")
|
|
441
|
-
|
|
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
|
-
(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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.
|
|
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")
|