wenyi-cli 1.4.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.
@@ -0,0 +1,393 @@
1
+ """Register workflows commands with an invocation context accessor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import Enum
6
+
7
+ import typer
8
+ from rich.progress import Progress
9
+ from wenyi_core.config import PipelineConfig
10
+ from wenyi_core.errors import WenyiError
11
+ from wenyi_core.timing import load_timing
12
+
13
+ from wenyi_cli.commands import presentation
14
+ from wenyi_cli.commands import progress as progress_view
15
+
16
+ from .context import ContextAccessor
17
+ from .machine_output import MachineOutput
18
+ from .validation import (
19
+ require_input_file,
20
+ resolve_output_format,
21
+ )
22
+
23
+
24
+ class TranslationMode(str, Enum):
25
+ standard = "standard"
26
+ best_of_three = "best_of_three"
27
+
28
+
29
+ def register_workflows_commands(app: typer.Typer, context: ContextAccessor) -> None:
30
+ @app.command(rich_help_panel="Main workflow")
31
+ def parse(
32
+ input_path: str = typer.Argument(..., help="Source book to parse without AI"),
33
+ ) -> None:
34
+ """Parse source chapters and list the contents without AI initialization."""
35
+ from rich.table import Table
36
+ from wenyi_core.pipeline.prepare.input_preparation import (
37
+ locate_input_storage,
38
+ parse_document,
39
+ )
40
+ from wenyi_core.storage.file import FileStorage
41
+
42
+ console = context().console
43
+ try:
44
+ require_input_file(input_path, console=console)
45
+ config = context().load_config()
46
+ store, digest = locate_input_storage(input_path, config, FileStorage)
47
+ try:
48
+ with store.lock():
49
+ document = parse_document(store, input_path, config, expected_sha256=digest)
50
+ table = Table("Chapter", "Title", "Paragraphs")
51
+ for chapter in document.chapters:
52
+ table.add_row(
53
+ str(chapter.index), chapter.title, str(len(chapter.text_segments))
54
+ )
55
+ console.print(table)
56
+ console.print(f"State directory: {store.run_dir}", markup=False)
57
+ console.print("Source parsed. AI preparation has not been run by this command.")
58
+ finally:
59
+ store.close()
60
+ except (WenyiError, ImportError, OSError, ValueError, RuntimeError) as error:
61
+ console.print(f"[red]Error: {error}[/]")
62
+ raise typer.Exit(1) from None
63
+
64
+ def _translate_impl(
65
+ input_path: str,
66
+ *,
67
+ chapter: int | None = None,
68
+ fmt: str | None = None,
69
+ out: str | None = None,
70
+ polish: bool | None = None,
71
+ translation_mode: TranslationMode | None = None,
72
+ review: bool | None = None,
73
+ mono: bool | None = None,
74
+ bilingual: bool | None = None,
75
+ progress_json: str | None = None,
76
+ result_json: str | None = None,
77
+ ) -> None:
78
+ """Run translation and report expected input or configuration errors concisely."""
79
+ console = context().console
80
+ machine = None
81
+ try:
82
+ machine = MachineOutput(
83
+ progress_json,
84
+ result_json,
85
+ source=input_path,
86
+ config=context().config_path,
87
+ output=out,
88
+ )
89
+ machine.start()
90
+ _translate_impl_or_raise(
91
+ input_path,
92
+ chapter=chapter,
93
+ fmt=fmt,
94
+ out=out,
95
+ polish=polish,
96
+ translation_mode=translation_mode,
97
+ review=review,
98
+ mono=mono,
99
+ bilingual=bilingual,
100
+ machine=machine,
101
+ )
102
+ except typer.Exit:
103
+ raise
104
+ except (WenyiError, ImportError, OSError, ValueError, RuntimeError) as error:
105
+ console.print(f"[red]Error: {error}[/]")
106
+ raise typer.Exit(1) from None
107
+ finally:
108
+ # Includes cancellation and unexpected exceptions. Never leave a stale success
109
+ # receipt, and never replace the original error with a reporting failure.
110
+ if machine is not None and not machine.completed:
111
+ try:
112
+ machine.failed()
113
+ except (OSError, WenyiError):
114
+ console.print("[red]Could not publish the machine failure receipt.[/]")
115
+
116
+ def _translate_impl_or_raise(
117
+ input_path: str,
118
+ *,
119
+ chapter: int | None = None,
120
+ fmt: str | None = None,
121
+ out: str | None = None,
122
+ polish: bool | None = None,
123
+ translation_mode: TranslationMode | None = None,
124
+ review: bool | None = None,
125
+ mono: bool | None = None,
126
+ bilingual: bool | None = None,
127
+ machine: MachineOutput,
128
+ ) -> None:
129
+ """Run translation; let ``_translate_impl`` convert exceptions to CLI errors."""
130
+ console = context().console
131
+ from wenyi_core.pipeline.orchestrator import Orchestrator
132
+
133
+ fmt = resolve_output_format(input_path, fmt, console=console)
134
+ config = context().load_config()
135
+ pipeline_values = config.pipeline.model_dump()
136
+ if polish is not None:
137
+ pipeline_values["polish"] = polish
138
+ if translation_mode is not None:
139
+ pipeline_values["translation_mode"] = translation_mode.value
140
+ try:
141
+ config.pipeline = PipelineConfig.model_validate(pipeline_values)
142
+ except ValueError:
143
+ if (
144
+ pipeline_values["translation_mode"] == "best_of_three"
145
+ and not pipeline_values["polish"]
146
+ ):
147
+ raise ValueError(
148
+ "best_of_three translation mode requires pipeline.polish=true; remove --no-polish"
149
+ ) from None
150
+ raise
151
+ if review is not None:
152
+ config.pipeline.review = review
153
+ if mono is not None:
154
+ config.output.mono = mono
155
+ if bilingual is not None:
156
+ config.output.bilingual = bilingual
157
+ if chapter is not None:
158
+ ignored: list[str] = []
159
+ if fmt not in {None, "epub"}:
160
+ ignored.append("--format")
161
+ if out is not None:
162
+ ignored.append("--out")
163
+ if review is not None:
164
+ ignored.append("--review/--no-review")
165
+ if mono is not None:
166
+ ignored.append("--mono/--no-mono")
167
+ if bilingual is not None:
168
+ ignored.append("--bilingual/--no-bilingual")
169
+ if ignored:
170
+ raise ValueError(
171
+ "--chapter only translates and saves the selected chapter; incompatible finalization options: "
172
+ + ", ".join(ignored)
173
+ )
174
+
175
+ if chapter is not None:
176
+ config.pipeline.review = False
177
+ context().validate_api_configuration(config, "translate")
178
+ require_input_file(input_path, console=console)
179
+ orch = Orchestrator(config, allow_terminology_context=True)
180
+
181
+ with (
182
+ orch.client.interrupt_scope(),
183
+ Progress(
184
+ *progress_view.progress_columns(),
185
+ console=console,
186
+ ) as prog,
187
+ ):
188
+ rich_progress = progress_view.RichProgressBridge(prog, "Preparing…")
189
+
190
+ def cb(done: int, total: int, label: str) -> None:
191
+ rich_progress(done, total, label)
192
+ machine(done, total, label)
193
+
194
+ if chapter is not None:
195
+ try:
196
+ store = orch.run(input_path, only_chapter=chapter, progress=cb)
197
+ except ValueError as error:
198
+ console.print(f"[red]{error}[/]")
199
+ raise typer.Exit(2) from error
200
+ console.print(
201
+ f"[green]Translated chapter {chapter}[/], State directory: {store.run_dir}"
202
+ )
203
+ presentation.print_usage(console, store.load_usage() or {})
204
+ presentation.print_timing(console, load_timing(store.run_dir))
205
+ machine.success([], store.run_dir)
206
+ return
207
+
208
+ result = orch.run_all(
209
+ input_path,
210
+ progress=cb,
211
+ out_format=fmt,
212
+ out_path=out,
213
+ )
214
+
215
+ store = result["store"]
216
+ with store.lock(), store.state_lock():
217
+ chapters = store.load_manifest()["chapters"]
218
+ chapters_done = sum(chapter["status"] == "done" for chapter in chapters)
219
+ term_count = store.stats()["terms"]
220
+ presentation.print_translation_summary(
221
+ console,
222
+ chapters_done=chapters_done,
223
+ chapters_total=len(chapters),
224
+ terms=term_count,
225
+ )
226
+ presentation.print_usage(console, store.load_usage() or {})
227
+ presentation.print_timing(console, load_timing(store.run_dir))
228
+ for path in result.get("outputs") or [result["output"]]:
229
+ console.print(f"Translation: [bold]{path}[/]")
230
+ if result.get("review_dir"):
231
+ presentation.print_review_details(
232
+ console, result.get("review_result"), result["review_dir"]
233
+ )
234
+ machine.success(result.get("outputs") or [result["output"]], store.run_dir)
235
+
236
+ def _prepare_impl(input_path: str) -> None:
237
+ """Complete preparation without translating body text or exporting files."""
238
+ console = context().console
239
+ from wenyi_core.pipeline.orchestrator import Orchestrator
240
+
241
+ try:
242
+ config = context().load_config()
243
+ context().validate_api_configuration(config, "prepare")
244
+ require_input_file(input_path, console=console)
245
+ orch = Orchestrator(config, allow_terminology_context=True)
246
+ with (
247
+ orch.client.interrupt_scope(),
248
+ Progress(
249
+ *progress_view.progress_columns(),
250
+ console=console,
251
+ ) as prog,
252
+ ):
253
+ cb = progress_view.RichProgressBridge(prog, "Preparing…")
254
+ store = orch.prepare_for_translation(input_path, progress=cb)
255
+ except typer.Exit:
256
+ raise
257
+ except (WenyiError, ImportError, OSError, ValueError, RuntimeError) as error:
258
+ console.print(f"[red]Error: {error}[/]")
259
+ raise typer.Exit(1) from None
260
+
261
+ manifest = store.load_manifest()
262
+ chapters = manifest.get("chapters", [])
263
+ analysis = store.load_analysis() or {}
264
+ digests = sum(
265
+ bool(store.load_chapter(item["index"]).meta.get("source_digest")) for item in chapters
266
+ )
267
+ presentation.print_preparation_summary(
268
+ console,
269
+ chapter_count=len(chapters),
270
+ digest_count=digests,
271
+ has_synopsis=bool(analysis.get("book_synopsis")),
272
+ run_dir=store.run_dir,
273
+ )
274
+ presentation.print_usage(console, store.load_usage() or {})
275
+ presentation.print_timing(console, load_timing(store.run_dir))
276
+
277
+ @app.command(rich_help_panel="Main workflow")
278
+ def translate(
279
+ input: str = typer.Argument(
280
+ ...,
281
+ help="Book to translate (EPUB / FB2 / TXT / Markdown / HTML / PDF / DOCX)",
282
+ ),
283
+ chapter: int | None = typer.Option(
284
+ None,
285
+ "--chapter",
286
+ min=0,
287
+ help="Translate and save one chapter (zero-based); skip review and export",
288
+ ),
289
+ fmt: str | None = typer.Option(
290
+ None,
291
+ "--format",
292
+ help="Output format: epub / txt / html / markdown / docx / pdf (BabelDOC only); default: pdf for BabelDOC PDF state, docx for .docx input, epub otherwise",
293
+ ),
294
+ out: str | None = typer.Option(
295
+ None,
296
+ "--out",
297
+ help="Monolingual output path; defaults to the output directory beside the source",
298
+ ),
299
+ polish: bool | None = typer.Option(
300
+ None,
301
+ "--polish/--no-polish",
302
+ help="Override pipeline.polish to enable or disable polishing",
303
+ ),
304
+ translation_mode: TranslationMode | None = typer.Option(
305
+ None,
306
+ "--translation-mode",
307
+ help="Book translation mode; best_of_three requires polishing",
308
+ ),
309
+ review: bool | None = typer.Option(
310
+ None,
311
+ "--review/--no-review",
312
+ help="Override pipeline.review to enable or disable final whole-book review",
313
+ ),
314
+ mono: bool | None = typer.Option(
315
+ None,
316
+ "--mono/--no-mono",
317
+ help="Override output.mono to enable or disable monolingual output",
318
+ ),
319
+ bilingual: bool | None = typer.Option(
320
+ None,
321
+ "--bilingual/--no-bilingual",
322
+ help="Override output.bilingual to enable or disable bilingual output",
323
+ ),
324
+ progress_json: str | None = typer.Option(
325
+ None, "--progress-json", help="Atomically publish protocol-1 progress JSON"
326
+ ),
327
+ result_json: str | None = typer.Option(
328
+ None, "--result-json", help="Atomically publish a protocol-1 output receipt"
329
+ ),
330
+ ):
331
+ """Prepare, translate, optionally review and export. Repeat to resume."""
332
+ _translate_impl(
333
+ input,
334
+ chapter=chapter,
335
+ fmt=fmt,
336
+ out=out,
337
+ polish=polish,
338
+ translation_mode=translation_mode,
339
+ review=review,
340
+ mono=mono,
341
+ bilingual=bilingual,
342
+ progress_json=progress_json,
343
+ result_json=result_json,
344
+ )
345
+
346
+ @app.command(rich_help_panel="Main workflow")
347
+ def prepare(
348
+ input: str = typer.Argument(
349
+ ...,
350
+ help="Book to prepare (EPUB / FB2 / TXT / Markdown / HTML / PDF / DOCX)",
351
+ ),
352
+ ) -> None:
353
+ """Parse, detect language, analyze style and terms, and prescan the book."""
354
+ _prepare_impl(input)
355
+
356
+ @app.command(rich_help_panel="Quality checks")
357
+ def review(
358
+ input: str = typer.Argument(..., help="Source file whose entire body has been translated"),
359
+ autofix: bool | None = typer.Option(
360
+ None,
361
+ "--autofix/--no-autofix",
362
+ help="Override pipeline.review_autofix to publish revisions to formal chapters",
363
+ ),
364
+ ):
365
+ """Run evidence review, shadow revisions and blind rechecks, with optional autofix."""
366
+ console = context().console
367
+ from wenyi_core.pipeline.orchestrator import Orchestrator
368
+
369
+ try:
370
+ config = context().load_config()
371
+ if autofix is not None:
372
+ config.pipeline.review_autofix = autofix
373
+ context().validate_api_configuration(config, "review")
374
+ require_input_file(input, console=console)
375
+ orch = Orchestrator(config)
376
+
377
+ with (
378
+ orch.client.interrupt_scope(),
379
+ Progress(
380
+ *progress_view.progress_columns(),
381
+ console=console,
382
+ ) as prog,
383
+ ):
384
+ cb = progress_view.RichProgressBridge(prog, "Preparing whole-book review…")
385
+ result = orch.run_review(input, progress=cb)
386
+ except typer.Exit:
387
+ raise
388
+ except (WenyiError, ImportError, OSError, ValueError, RuntimeError) as error:
389
+ console.print(f"[red]Error: {error}[/]")
390
+ raise typer.Exit(1) from None
391
+
392
+ presentation.print_review_summary(console, result["review_result"], result["review_dir"])
393
+ presentation.print_timing(console, load_timing(result["store"].run_dir))
wenyi_cli/main.py ADDED
@@ -0,0 +1,5 @@
1
+ """Installed wenyi command entry point."""
2
+
3
+ from .cli import app, main
4
+
5
+ __all__ = ["app", "main"]
@@ -0,0 +1,165 @@
1
+ """Model configuration previews and explicit migration commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Callable
7
+ from pathlib import Path
8
+
9
+ import typer
10
+ import yaml
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+ from wenyi_core.config import Config
14
+ from wenyi_core.llm.operations import configured_operations, require_operation
15
+ from wenyi_core.llm.routing import resolve_routes
16
+
17
+
18
+ def register_model_commands(
19
+ app: typer.Typer, load_config: Callable[[], Config], console: Console
20
+ ) -> None:
21
+ """Register commands once; operation and provider details come from their registries."""
22
+ models = typer.Typer(
23
+ no_args_is_help=True, help="Inspect model routes and manage model settings."
24
+ )
25
+ app.add_typer(models, name="models", rich_help_panel="Configuration")
26
+
27
+ @models.command("list")
28
+ def list_models(
29
+ as_json: bool = typer.Option(False, "--json", help="Print structured routing data"),
30
+ ):
31
+ """Show all effective routes without credentials or network requests."""
32
+ config = load_config()
33
+ routes = resolve_routes(config.llm)
34
+ if as_json:
35
+ typer.echo(
36
+ json.dumps({key: value.describe() for key, value in routes.items()}, indent=2)
37
+ )
38
+ return
39
+ table = Table(
40
+ "Operation",
41
+ "Selection",
42
+ "Tier",
43
+ "Profile",
44
+ "Connection / adapter",
45
+ "Model",
46
+ "Output",
47
+ "Concurrency",
48
+ )
49
+ for operation, route in routes.items():
50
+ connection = config.llm.providers[route.provider]
51
+ table.add_row(
52
+ operation,
53
+ route.origin,
54
+ route.tier or "direct",
55
+ route.profile,
56
+ f"{route.provider} / {route.provider_kind}",
57
+ route.model,
58
+ str(route.max_output_tokens or "provider default"),
59
+ str(connection.max_concurrency or "unlimited"),
60
+ )
61
+ console.print(table)
62
+
63
+ @models.command("explain")
64
+ def explain(operation: str = typer.Option(..., "--operation")):
65
+ """Explain an operation's defaults, effective request and selection origin."""
66
+ try:
67
+ spec = require_operation(operation)
68
+ route = resolve_routes(load_config().llm)[operation]
69
+ typer.echo(json.dumps({"description": spec.description, **route.describe()}, indent=2))
70
+ except ValueError as error:
71
+ raise typer.BadParameter(str(error)) from None
72
+
73
+ @models.command("check")
74
+ def check(
75
+ workflow: str = typer.Option("translate", "--for", help="prepare, translate or review"),
76
+ ):
77
+ """Validate reachable credentials locally; never send a test request."""
78
+ try:
79
+ config = load_config()
80
+ operations = configured_operations(config, workflow)
81
+ # Imported here so ``--help`` never pays for the provider SDK.
82
+ from wenyi_core.llm.router import RoutedLLMClient
83
+
84
+ RoutedLLMClient(config.llm).validate_credentials(operations)
85
+ console.print(
86
+ f"Model configuration valid for {workflow}: {len(operations)} operations."
87
+ )
88
+ except (ValueError, RuntimeError) as error:
89
+ console.print(f"[red]Error: {error}[/]")
90
+ raise typer.Exit(1) from None
91
+
92
+ @models.command("ping")
93
+ def ping(
94
+ workflow: str = typer.Option("translate", "--for", help="prepare, translate or review"),
95
+ timeout: float = typer.Option(
96
+ 30.0, "--timeout", min=0.1, help="Seconds to wait for each provider"
97
+ ),
98
+ as_json: bool = typer.Option(False, "--json", help="Print structured probe results"),
99
+ ):
100
+ """Send one short live request per reachable model profile; uses a few tokens."""
101
+ from wenyi_core.llm.connectivity import probe_connections
102
+
103
+ try:
104
+ config = load_config()
105
+ operations = configured_operations(config, workflow)
106
+ results = probe_connections(config.llm, operations, timeout=timeout)
107
+ except (ValueError, RuntimeError) as error:
108
+ console.print(f"[red]Error: {error}[/]")
109
+ raise typer.Exit(1) from None
110
+ if as_json:
111
+ typer.echo(json.dumps([result.describe() for result in results], indent=2))
112
+ else:
113
+ table = Table("Profile", "Connection / adapter", "Model", "Status", "Latency", "Detail")
114
+ for result in results:
115
+ table.add_row(
116
+ result.profile,
117
+ f"{result.connection} / {result.provider}",
118
+ result.model,
119
+ "[green]ok[/]" if result.ok else f"[red]{result.category}[/]",
120
+ "-" if result.latency_ms is None else f"{result.latency_ms} ms",
121
+ result.message,
122
+ )
123
+ console.print(table)
124
+ failed = sum(not result.ok for result in results)
125
+ if failed:
126
+ if not as_json:
127
+ console.print(f"[red]{failed} of {len(results)} model profiles unreachable.[/]")
128
+ raise typer.Exit(1)
129
+ if not as_json:
130
+ console.print(f"All {len(results)} model profiles reachable for {workflow}.")
131
+
132
+ @models.command("migrate-config")
133
+ def migrate_config(
134
+ source: Path = typer.Argument(..., exists=True, dir_okay=False),
135
+ out: Path = typer.Option(..., "--out", help="New YAML file; must not exist"),
136
+ ):
137
+ """Convert a retired configuration into a separate, reviewable YAML file."""
138
+ from wenyi_core.llm.migration import convert_config
139
+
140
+ try:
141
+ raw = yaml.safe_load(source.read_text(encoding="utf-8"))
142
+ if not isinstance(raw, dict):
143
+ raise ValueError("Configuration must be a mapping")
144
+ converted = convert_config(raw)
145
+ Config.from_dict(converted)
146
+ with out.open("x", encoding="utf-8") as file:
147
+ yaml.safe_dump(converted, file, allow_unicode=True, sort_keys=False)
148
+ console.print(f"Converted configuration: {out}")
149
+ except (OSError, ValueError, yaml.YAMLError) as error:
150
+ console.print(f"[red]Error: {error}[/]")
151
+ raise typer.Exit(1) from None
152
+
153
+ @models.command("migrate-usage")
154
+ def migrate_usage(run_dir: Path = typer.Argument(..., exists=True, file_okay=False)):
155
+ """Back up and convert usage ledgers in one selected target run directory."""
156
+ from wenyi_core.storage.file_migrations import migrate_usage_ledgers
157
+
158
+ try:
159
+ count = migrate_usage_ledgers(run_dir)
160
+ console.print(
161
+ f"Converted {count} usage ledgers; original files backed up alongside them."
162
+ )
163
+ except (OSError, ValueError) as error:
164
+ console.print(f"[red]Error: {error}[/]")
165
+ raise typer.Exit(1) from None
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.5
2
+ Name: wenyi-cli
3
+ Version: 1.4.2
4
+ Summary: Local command-line interface for Wenyi's translation engine.
5
+ Project-URL: Repository, https://github.com/BigDawnGhost/wenyi
6
+ Project-URL: Documentation, https://github.com/BigDawnGhost/wenyi/blob/main/docs/cli.md
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: rich>=13.7
9
+ Requires-Dist: typer>=0.12
10
+ Requires-Dist: wenyi-core
11
+ Description-Content-Type: text/markdown
12
+
13
+ # Wenyi CLI
14
+
15
+ Local command-line application for multilingual long-form translation, powered
16
+ by `wenyi-core`. It supports terminology management, contextual translation,
17
+ polishing, whole-book review, resumable workflows, and multiple document formats.
18
+
19
+ Install into an activated Python 3.10+ virtual environment:
20
+
21
+ ```bash
22
+ uv pip install wenyi-cli
23
+ wenyi --version
24
+ wenyi --help
25
+ ```
26
+
27
+ The executable is `wenyi`; the module entry point is `python -m wenyi_cli`.
28
+ The translation engine is installed automatically. API keys are read from
29
+ environment variables; do not put credentials in configuration files.
30
+
31
+ PDF layout preservation additionally requires an independently installed
32
+ `wenyi-babeldoc-bridge` HTTP service. This package does not install BabelDOC,
33
+ the Web API, or the Desktop application.
34
+
35
+ - [CLI guide](https://github.com/BigDawnGhost/wenyi/blob/main/docs/cli.md)
36
+ - [Configuration](https://github.com/BigDawnGhost/wenyi/blob/main/docs/configuration.md)
37
+ - [Source and license](https://github.com/BigDawnGhost/wenyi)
38
+ - [简体中文](https://github.com/BigDawnGhost/wenyi/blob/main/docs/zh/cli.md)
@@ -0,0 +1,20 @@
1
+ wenyi_cli/__init__.py,sha256=WOmaRQbKRo7QSfUoy-CGriiK5WrC8qmUkhVAk4XljXI,274
2
+ wenyi_cli/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
3
+ wenyi_cli/cli.py,sha256=tKpOwEx-pMzf6F8diAoP935qx3fsZZ9y3E6xWmEiaNc,1552
4
+ wenyi_cli/main.py,sha256=YphRUYMjqX4Mjw-Wu8r3XYQwA09DbtQ1xhgAA29WeNc,98
5
+ wenyi_cli/model_commands.py,sha256=nmpO6R0e7aHPkfCsk40KOYIvWmJO7XIbsUMJQOncTrA,6955
6
+ wenyi_cli/commands/__init__.py,sha256=f9cLnOA-FXA1RuQedQ37Y4u4XIk2Gj2tMEKAWBPdeMQ,62
7
+ wenyi_cli/commands/bootstrap.py,sha256=v2RTEi8f-RCbCecaWT5QgdiUFmSpqUafmMBsx4aMunA,3697
8
+ wenyi_cli/commands/context.py,sha256=a0QySybZDGVCVQA2AxNm_IHVPHEqP-1ooumJW7Qiw9E,1739
9
+ wenyi_cli/commands/glossary.py,sha256=w7PpV0OUHmKvpFhFxXlnCgAVHe0Oommx-HvlxBkRr3Q,4265
10
+ wenyi_cli/commands/inspection.py,sha256=by9C3p7LuYSAevaRapKQC_IvMFNCGHOBgkU1sLrbQDg,7415
11
+ wenyi_cli/commands/machine_output.py,sha256=_BJ0G1QV_FsC7ZZ3uASkNYS-Jj4cx09zBnf4HDmmhEg,3519
12
+ wenyi_cli/commands/presentation.py,sha256=um7irPa6f1Enk8UgONUbFj0vau8CjG8GfQkUFwUcq6M,5890
13
+ wenyi_cli/commands/progress.py,sha256=5UnWFSZgljhvW0eut8BwnKY7yL_Qb9iMHP073B6191Y,3272
14
+ wenyi_cli/commands/run_report.py,sha256=ZhLwG0cs-1-rpydylb7RuzRJ0rNe303yPJJq1fB6YOY,3820
15
+ wenyi_cli/commands/validation.py,sha256=FRGfaVHf2MAxluluvVYKveV8ifVVGZiw81AKz-YEZos,3777
16
+ wenyi_cli/commands/workflows.py,sha256=q8xGFIzh1KXRK02x-eMM10q2njxNQmgQdn8y1ISkx6U,15538
17
+ wenyi_cli-1.4.2.dist-info/METADATA,sha256=R3ICtNpttA3j6UfDqOBJy02u7luMXKedRxaKwUlZLbQ,1519
18
+ wenyi_cli-1.4.2.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
19
+ wenyi_cli-1.4.2.dist-info/entry_points.txt,sha256=UOuMOGDjQXGrG-03A1dxl2LWBWhdmOHjkqHrZs95m4c,46
20
+ wenyi_cli-1.4.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ wenyi = wenyi_cli.main:main