yread 0.1.0__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.
yread/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Local repo-to-wiki generator."""
2
+
3
+ __version__ = "0.1.0"
yread/cli.py ADDED
@@ -0,0 +1,173 @@
1
+ """Command line entry point for yread."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import __version__, core, viewer
10
+
11
+
12
+ CONFIG_DIR = Path.home() / ".yread"
13
+ CONFIG_FILE = CONFIG_DIR / "config.env"
14
+
15
+ CONFIG_KEYS = {
16
+ "PROVIDER",
17
+ "BASE_URL",
18
+ "API_KEY",
19
+ "MODEL",
20
+ "DOC_LANG",
21
+ "MAX_STEPS",
22
+ "MAX_TOPICS",
23
+ "CONCURRENCY",
24
+ "ENABLE_SHELL",
25
+ "OUTPUT_DIR",
26
+ }
27
+
28
+
29
+ def _read_config() -> dict[str, str]:
30
+ if not CONFIG_FILE.exists():
31
+ return {}
32
+ return core._parse_env_file(CONFIG_FILE)
33
+
34
+
35
+ def _write_config(values: dict[str, str]) -> None:
36
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
37
+ lines = [
38
+ "# yread local configuration",
39
+ "# Edit with `yread config set KEY VALUE` or by changing this file.",
40
+ ]
41
+ for key in sorted(values):
42
+ lines.append(f"{key}={values[key]}")
43
+ CONFIG_FILE.write_text("\n".join(lines) + "\n", encoding="utf-8")
44
+
45
+
46
+ def _build_generate_parser() -> argparse.ArgumentParser:
47
+ return core.build_arg_parser()
48
+
49
+
50
+ def _build_parser() -> argparse.ArgumentParser:
51
+ parser = argparse.ArgumentParser(
52
+ prog="yread",
53
+ description="Turn a local repository into a lightweight structured wiki.",
54
+ )
55
+ sub = parser.add_subparsers(dest="command")
56
+
57
+ generate = sub.add_parser("generate", help="Generate or resume a repo wiki")
58
+ for action in _build_generate_parser()._actions:
59
+ if action.dest == "help":
60
+ continue
61
+ generate._add_action(action)
62
+
63
+ browse = sub.add_parser("browse", help="Open a generated wiki in the browser")
64
+ browse.add_argument("wiki_dir", nargs="?", help="Wiki root or version directory")
65
+ browse.add_argument("--host", default="localhost", help="Host to bind")
66
+ browse.add_argument("--port", type=int, default=8000)
67
+ browse.add_argument("--repo", default=None, help="Source repository for source links")
68
+
69
+ sub.add_parser("version", help="Print the version number")
70
+
71
+ config = sub.add_parser("config", help=f"Manage {CONFIG_FILE}")
72
+ config_sub = config.add_subparsers(dest="config_command")
73
+
74
+ config_sub.add_parser("init", help="Interactively set up the config file")
75
+ config_sub.add_parser("path", help="Print the config file path")
76
+ config_sub.add_parser("show", help="Print current config")
77
+
78
+ set_cmd = config_sub.add_parser("set", help="Set one config value")
79
+ set_cmd.add_argument("key", choices=sorted(CONFIG_KEYS))
80
+ set_cmd.add_argument("value")
81
+
82
+ unset_cmd = config_sub.add_parser("unset", help="Remove one config value")
83
+ unset_cmd.add_argument("key", choices=sorted(CONFIG_KEYS))
84
+
85
+ return parser
86
+
87
+
88
+ INIT_PROMPTS = [
89
+ ("PROVIDER", "Provider [minimax-cn/deepseek/openai-compatible]", "openai-compatible"),
90
+ ("BASE_URL", "OpenAI-compatible /v1 endpoint", ""),
91
+ ("API_KEY", "API key", ""),
92
+ ("MODEL", "Model name", ""),
93
+ ("DOC_LANG", "Documentation language code [zh/en]", "en"),
94
+ ("OUTPUT_DIR", "Output directory (blank = <repo>/.yread/wiki)", ""),
95
+ ]
96
+
97
+
98
+ def _run_config_init() -> int:
99
+ values = _read_config()
100
+ print(f"Configuring {CONFIG_FILE} (Enter keeps the shown value)\n")
101
+ for key, label, fallback in INIT_PROMPTS:
102
+ current = values.get(key, fallback)
103
+ suffix = f" [{current}]" if current else ""
104
+ entered = input(f"{label}{suffix}: ").strip()
105
+ chosen = entered or current
106
+ if chosen:
107
+ values[key] = chosen
108
+ else:
109
+ values.pop(key, None)
110
+ _write_config(values)
111
+ print(f"\nwrote {CONFIG_FILE}")
112
+ return 0
113
+
114
+
115
+ def _run_config(args: argparse.Namespace) -> int:
116
+ command = args.config_command or "show"
117
+ if command == "init":
118
+ return _run_config_init()
119
+ if command == "path":
120
+ print(CONFIG_FILE)
121
+ return 0
122
+ values = _read_config()
123
+ if command == "show":
124
+ if not values:
125
+ print(f"# no config yet: {CONFIG_FILE}")
126
+ return 0
127
+ for key in sorted(values):
128
+ print(f"{key}={values[key]}")
129
+ return 0
130
+ if command == "set":
131
+ values[args.key] = args.value
132
+ _write_config(values)
133
+ print(CONFIG_FILE)
134
+ return 0
135
+ if command == "unset":
136
+ values.pop(args.key, None)
137
+ _write_config(values)
138
+ print(CONFIG_FILE)
139
+ return 0
140
+ raise SystemExit(f"unknown config command: {command}")
141
+
142
+
143
+ def _run_browse(args: argparse.Namespace) -> int:
144
+ viewer_args = []
145
+ if args.wiki_dir:
146
+ viewer_args.append(args.wiki_dir)
147
+ viewer_args.extend(["--host", args.host, "--port", str(args.port)])
148
+ if args.repo:
149
+ viewer_args.extend(["--repo", args.repo])
150
+ viewer.main(viewer_args)
151
+ return 0
152
+
153
+
154
+ def main(argv: list[str] | None = None) -> int:
155
+ parser = _build_parser()
156
+ args = parser.parse_args(list(sys.argv[1:] if argv is None else argv))
157
+ if args.command == "version":
158
+ print(f"yread {__version__}")
159
+ return 0
160
+ if args.command == "config":
161
+ return _run_config(args)
162
+ if args.command == "browse":
163
+ return _run_browse(args)
164
+ if args.command == "generate":
165
+ config = core.config_from_args(args, config_files=[CONFIG_FILE])
166
+ core.run_generate(args, config)
167
+ return 0
168
+ parser.print_help()
169
+ return 0
170
+
171
+
172
+ if __name__ == "__main__":
173
+ raise SystemExit(main())