mwclient-cli 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.
@@ -0,0 +1,5 @@
1
+ """mwclient-cli package."""
2
+
3
+ from .cli import run
4
+
5
+ __all__ = ["run"]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()
mwclient-cli/cli.py ADDED
@@ -0,0 +1,401 @@
1
+ """Command-line wrapper for mwclient."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import base64
7
+ import importlib
8
+ import inspect
9
+ import json
10
+ import os
11
+ import sys
12
+ from collections.abc import Iterator, Mapping
13
+ from typing import Any
14
+
15
+ import html2text
16
+
17
+ _ENTITY_LOCATIONS = {
18
+ "site": ("mwclient.client", "Site"),
19
+ "page": ("mwclient.page", "Page"),
20
+ "image": ("mwclient.image", "Image"),
21
+ }
22
+
23
+
24
+ class HelpFormatter(argparse.RawDescriptionHelpFormatter):
25
+ """Preserve newlines in help text."""
26
+
27
+
28
+ def resolve_entity_class(entity: str) -> Any:
29
+ module_name, class_name = _ENTITY_LOCATIONS[entity]
30
+ module = importlib.import_module(module_name)
31
+ return getattr(module, class_name)
32
+
33
+
34
+ def list_public_methods(entity: str) -> dict[str, Any]:
35
+ cls = resolve_entity_class(entity)
36
+ methods: dict[str, Any] = {}
37
+ for name, member in inspect.getmembers(cls, predicate=callable):
38
+ if name.startswith("_"):
39
+ continue
40
+ methods[name] = member
41
+ return methods
42
+
43
+
44
+ def parse_cli_value(raw: str) -> Any:
45
+ try:
46
+ return json.loads(raw)
47
+ except json.JSONDecodeError:
48
+ return raw
49
+
50
+
51
+ def parse_keyword_args(items: list[str]) -> dict[str, Any]:
52
+ kwargs: dict[str, Any] = {}
53
+ for item in items:
54
+ if "=" not in item:
55
+ raise ValueError(f"invalid --kw value '{item}', expected key=value")
56
+ key, raw_value = item.split("=", 1)
57
+ if not key:
58
+ raise ValueError("empty --kw key is not allowed")
59
+ kwargs[key] = parse_cli_value(raw_value)
60
+ return kwargs
61
+
62
+
63
+ def normalize_result(value: Any) -> Any:
64
+ if value is None:
65
+ return None
66
+ if isinstance(value, (str, int, float, bool)):
67
+ return value
68
+ if isinstance(value, bytes):
69
+ return {
70
+ "__type__": "bytes",
71
+ "base64": base64.b64encode(value).decode("ascii"),
72
+ "size": len(value),
73
+ }
74
+ if isinstance(value, Mapping):
75
+ return {str(key): normalize_result(item) for key, item in value.items()}
76
+ if isinstance(value, (list, tuple, set)):
77
+ return [normalize_result(item) for item in value]
78
+ return repr(value)
79
+
80
+
81
+ def build_site(args: argparse.Namespace) -> Any:
82
+ site_class = resolve_entity_class("site")
83
+ site = site_class(
84
+ args.host,
85
+ path=args.path,
86
+ ext=args.ext,
87
+ scheme=args.scheme,
88
+ do_init=not args.no_init,
89
+ force_login=not args.allow_anon,
90
+ clients_useragent=args.clients_useragent,
91
+ )
92
+ if args.username:
93
+ site.login(args.username, args.password)
94
+ return site
95
+
96
+
97
+ def build_target(site: Any, args: argparse.Namespace) -> Any:
98
+ if args.command == "site":
99
+ return site
100
+ if args.command == "page":
101
+ return site.pages[args.title]
102
+ if args.command == "image":
103
+ return site.images[args.title]
104
+ raise ValueError(f"unsupported command {args.command}")
105
+
106
+
107
+ def print_json(value: Any, indent: int | None) -> None:
108
+ json.dump(normalize_result(value), sys.stdout, indent=indent, ensure_ascii=False)
109
+ sys.stdout.write("\n")
110
+
111
+
112
+ def print_text(value: str) -> None:
113
+ sys.stdout.write(value.rstrip("\n"))
114
+ sys.stdout.write("\n")
115
+
116
+
117
+ def print_method_list(entity: str) -> int:
118
+ methods = list_public_methods(entity)
119
+ for name in sorted(methods):
120
+ signature = inspect.signature(methods[name])
121
+ print(f"{entity}.{name}{signature}")
122
+ return 0
123
+
124
+
125
+ def html_to_markdown(value: str) -> str:
126
+ converter = html2text.HTML2Text()
127
+ converter.body_width = 0
128
+ converter.ignore_images = False
129
+ converter.ignore_links = False
130
+ return converter.handle(value).strip()
131
+
132
+
133
+ def normalize_page_title(value: str) -> str:
134
+ return value.replace("_", " ").strip()
135
+
136
+
137
+ def extract_parse_html(parse_result: Any) -> str | None:
138
+ if not isinstance(parse_result, Mapping):
139
+ return None
140
+ text_data = parse_result.get("text")
141
+ if not isinstance(text_data, Mapping):
142
+ return None
143
+ text_html = text_data.get("*")
144
+ return text_html if isinstance(text_html, str) else None
145
+
146
+
147
+ def maybe_convert_markdown(args: argparse.Namespace, target: Any, result: Any) -> Any:
148
+ if not getattr(args, "markdown", False):
149
+ return result
150
+
151
+ if args.command == "page" and args.method == "text" and isinstance(result, str):
152
+ parse_data = target.site.parse(text=result, title=getattr(target, "name", None))
153
+ parse_html = extract_parse_html(parse_data)
154
+ title = normalize_page_title(
155
+ str(getattr(target, "name", getattr(target, "title", args.title)))
156
+ )
157
+ heading = f"# {title}".strip()
158
+ if parse_html:
159
+ body = html_to_markdown(parse_html).strip()
160
+ return f"{heading}\n\n{body}" if body else heading
161
+ body = result.strip()
162
+ return f"{heading}\n\n{body}" if body else heading
163
+
164
+ if args.command == "site" and args.method == "parse":
165
+ parse_html = extract_parse_html(result)
166
+ if parse_html:
167
+ return html_to_markdown(parse_html)
168
+ return result
169
+
170
+ return result
171
+
172
+
173
+ def build_parser() -> argparse.ArgumentParser:
174
+ parser = argparse.ArgumentParser(
175
+ prog="mwclient-cli",
176
+ formatter_class=HelpFormatter,
177
+ description=(
178
+ "Command-line wrapper around mwclient\n\n"
179
+ "Use one of these targets:\n"
180
+ " site methods from mwclient.Site\n"
181
+ " page methods from mwclient.page.Page\n"
182
+ " image methods from mwclient.image.Image"
183
+ ),
184
+ epilog=(
185
+ "Examples:\n"
186
+ " mwclient-cli methods site\n"
187
+ " mwclient-cli --host host.docker.internal --scheme http --path /w/ "
188
+ "page \"Main Page\" text\n"
189
+ " mwclient-cli --host host.docker.internal --scheme http --path /w/ "
190
+ "site search --arg space --kw what=text --max-items 5"
191
+ ),
192
+ )
193
+ parser.add_argument(
194
+ "--host",
195
+ default=os.getenv("MWCLI_HOST"),
196
+ metavar="HOST",
197
+ help="Wiki host (no scheme). Env fallback: MWCLI_HOST",
198
+ )
199
+ parser.add_argument(
200
+ "--path",
201
+ default=os.getenv("MWCLI_PATH", "/w/"),
202
+ metavar="PATH",
203
+ help="MediaWiki script path with trailing slash. Default: /w/. Env: MWCLI_PATH",
204
+ )
205
+ parser.add_argument(
206
+ "--ext",
207
+ default=os.getenv("MWCLI_EXT", ".php"),
208
+ metavar="EXT",
209
+ help="Script extension. Default: .php. Env: MWCLI_EXT",
210
+ )
211
+ parser.add_argument(
212
+ "--scheme",
213
+ default=os.getenv("MWCLI_SCHEME", "https"),
214
+ metavar="SCHEME",
215
+ help="URL scheme: http or https. Default: https. Env: MWCLI_SCHEME",
216
+ )
217
+ parser.add_argument(
218
+ "--username",
219
+ default=os.getenv("MWCLI_USERNAME"),
220
+ metavar="USERNAME",
221
+ help="Login username. If set, --password is required. Env: MWCLI_USERNAME",
222
+ )
223
+ parser.add_argument(
224
+ "--password",
225
+ default=os.getenv("MWCLI_PASSWORD"),
226
+ metavar="PASSWORD",
227
+ help="Login password. If set, --username is required. Env: MWCLI_PASSWORD",
228
+ )
229
+ parser.add_argument(
230
+ "--clients-useragent",
231
+ default=os.getenv("MWCLI_USER_AGENT"),
232
+ metavar="UA",
233
+ help="Custom user-agent prefix. Env: MWCLI_USER_AGENT",
234
+ )
235
+ parser.add_argument(
236
+ "--allow-anon",
237
+ action="store_true",
238
+ help="Allow unauthenticated edits (sets force_login=False on Site)",
239
+ )
240
+ parser.add_argument(
241
+ "--no-init",
242
+ action="store_true",
243
+ help="Skip initial site_init() call during Site creation",
244
+ )
245
+ parser.add_argument(
246
+ "--indent",
247
+ type=int,
248
+ default=None,
249
+ metavar="N",
250
+ help="Pretty-print JSON output with N-space indentation",
251
+ )
252
+ parser.add_argument(
253
+ "--markdown",
254
+ action="store_true",
255
+ help=(
256
+ "Convert content-read methods to Markdown "
257
+ "(currently: page text, site parse)"
258
+ ),
259
+ )
260
+
261
+ subparsers = parser.add_subparsers(
262
+ dest="command",
263
+ required=True,
264
+ title="commands",
265
+ metavar="COMMAND",
266
+ )
267
+
268
+ methods_parser = subparsers.add_parser(
269
+ "methods",
270
+ formatter_class=HelpFormatter,
271
+ help="list available methods on site/page/image targets",
272
+ description="List callable public methods and signatures.",
273
+ epilog=(
274
+ "Examples:\n"
275
+ " mwclient-cli methods all\n"
276
+ " mwclient-cli methods page"
277
+ ),
278
+ )
279
+ methods_parser.add_argument(
280
+ "entity",
281
+ choices=["site", "page", "image", "all"],
282
+ help="Target entity to inspect",
283
+ )
284
+
285
+ for entity in ("site", "page", "image"):
286
+ entity_parser = subparsers.add_parser(
287
+ entity,
288
+ formatter_class=HelpFormatter,
289
+ help=f"call {entity} method by name",
290
+ description=(
291
+ f"Call public method on {entity} target.\n"
292
+ "Arguments passed via repeated --arg and --kw options."
293
+ ),
294
+ epilog=(
295
+ "Examples:\n"
296
+ f" mwclient-cli --host HOST {entity} "
297
+ + ("\"Main Page\" " if entity == "page" else "\"Example.png\" " if entity == "image" else "")
298
+ + "METHOD --arg VALUE --kw key=value\n"
299
+ f" mwclient-cli methods {entity}"
300
+ ),
301
+ )
302
+ if entity in {"page", "image"}:
303
+ entity_parser.add_argument("title", help=f"{entity} title")
304
+ entity_parser.add_argument("method", help=f"{entity} method name")
305
+ entity_parser.add_argument(
306
+ "--arg",
307
+ action="append",
308
+ default=[],
309
+ metavar="VALUE",
310
+ help="Positional method arg (JSON parsed, fallback to string)",
311
+ )
312
+ entity_parser.add_argument(
313
+ "--kw",
314
+ action="append",
315
+ default=[],
316
+ metavar="KEY=VALUE",
317
+ help="Keyword method arg (value JSON parsed, fallback to string)",
318
+ )
319
+ entity_parser.add_argument(
320
+ "--stream",
321
+ action="store_true",
322
+ help="Stream list/tuple results as one JSON object per line",
323
+ )
324
+ entity_parser.add_argument(
325
+ "--max-items",
326
+ type=int,
327
+ default=None,
328
+ metavar="N",
329
+ help="Limit number of emitted items for iterator/list results",
330
+ )
331
+ entity_parser.add_argument(
332
+ "--markdown",
333
+ action="store_true",
334
+ default=argparse.SUPPRESS,
335
+ help="Convert content-read methods to Markdown",
336
+ )
337
+
338
+ return parser
339
+
340
+
341
+ def run(argv: list[str] | None = None) -> int:
342
+ parser = build_parser()
343
+ args = parser.parse_args(argv)
344
+
345
+ if args.command == "methods":
346
+ if args.entity == "all":
347
+ for entity in ("site", "page", "image"):
348
+ print_method_list(entity)
349
+ return 0
350
+ return print_method_list(args.entity)
351
+
352
+ if not args.host:
353
+ parser.error("missing --host (or env MWCLI_HOST)")
354
+
355
+ if bool(args.username) != bool(args.password):
356
+ parser.error("set both --username and --password or neither")
357
+
358
+ positionals = [parse_cli_value(raw) for raw in args.arg]
359
+ try:
360
+ kwargs = parse_keyword_args(args.kw)
361
+ except ValueError as exc:
362
+ parser.error(str(exc))
363
+
364
+ target = build_target(build_site(args), args)
365
+ methods = list_public_methods(args.command)
366
+ if args.method not in methods:
367
+ parser.error(f"unknown method {args.command}.{args.method}")
368
+ method = getattr(target, args.method)
369
+
370
+ result = method(*positionals, **kwargs)
371
+ result = maybe_convert_markdown(args, target, result)
372
+ if isinstance(result, Iterator):
373
+ max_items = args.max_items
374
+ count = 0
375
+ for item in result:
376
+ print_json(item, args.indent)
377
+ count += 1
378
+ if max_items is not None and count >= max_items:
379
+ break
380
+ return 0
381
+
382
+ if args.stream and isinstance(result, (list, tuple)):
383
+ max_items = args.max_items
384
+ items = result if max_items is None else result[:max_items]
385
+ for item in items:
386
+ print_json(item, args.indent)
387
+ return 0
388
+
389
+ if args.markdown and isinstance(result, str):
390
+ print_text(result)
391
+ else:
392
+ print_json(result, args.indent)
393
+ return 0
394
+
395
+
396
+ def main() -> None:
397
+ raise SystemExit(run())
398
+
399
+
400
+ if __name__ == "__main__":
401
+ main()
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.4
2
+ Name: mwclient-cli
3
+ Version: 0.1.0
4
+ Summary: CLI wrapper around mwclient
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: html2text==2025.4.15
7
+ Requires-Dist: mwclient==0.11.0
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest==9.0.2; extra == 'test'
10
+ Description-Content-Type: text/markdown
11
+
12
+ # mwcli
13
+
14
+ CLI wrapper around `mwclient`. Built for Agents.
15
+
16
+ Exposes `mwclient` methods from 3 targets:
17
+ - `site` -> `mwclient.Site`
18
+ - `page` -> `mwclient.page.Page`
19
+ - `image` -> `mwclient.image.Image`
20
+
21
+ ## Install
22
+
23
+ From project dir:
24
+
25
+ ```bash
26
+ pip install -r requirements.txt
27
+ pip install -e .
28
+ ```
29
+
30
+ ## Command shape
31
+
32
+ ```bash
33
+ python -m mwcli [connection flags] <site|page|image> <target args> <method> [--arg ...] [--kw ...] [--markdown]
34
+ ```
35
+
36
+ Connection flags:
37
+ - `--host` required (or `MWCLI_HOST`)
38
+ - `--scheme` default `https`
39
+ - `--path` default `/w/`
40
+ - `--ext` default `.php`
41
+ - `--username`, `--password` optional auth
42
+
43
+ Method args:
44
+ - `--arg VALUE` positional arg, JSON-parsed when possible
45
+ - `--kw KEY=VALUE` keyword arg, value JSON-parsed when possible
46
+ - `--max-items N` cap iterator/list output
47
+ - `--stream` print list/tuple one JSON per line
48
+ - `--markdown` convert content-read output to Markdown (see below)
49
+
50
+ Tip: quote strings with spaces/pipes.
51
+
52
+ ## Discover available methods
53
+
54
+ ```bash
55
+ python -m mwcli methods all
56
+ python -m mwcli methods site
57
+ python -m mwcli methods page
58
+ python -m mwcli methods image
59
+ ```
60
+
61
+ ## All commands
62
+
63
+ Top-level commands:
64
+
65
+ - `python -m mwcli methods {all|site|page|image}`
66
+ - `python -m mwcli site <method> [--arg ...] [--kw ...]`
67
+ - `python -m mwcli page "<title>" <method> [--arg ...] [--kw ...]`
68
+ - `python -m mwcli image "<title>" <method> [--arg ...] [--kw ...]`
69
+
70
+ `site` methods (mwclient 0.11.0):
71
+
72
+ - `allcategories`, `allimages`, `alllinks`, `allpages`, `allusers`
73
+ - `api`, `ask`, `blocks`, `checkuserlog`, `chunk_upload`, `clientlogin`
74
+ - `deletedrevisions`, `email`, `expandtemplates`, `exturlusage`
75
+ - `get`, `get_token`, `handle_api_result`, `logevents`, `login`
76
+ - `parse`, `patrol`, `post`, `random`, `raw_api`, `raw_call`, `raw_index`
77
+ - `recentchanges`, `require`, `revisions`, `search`, `site_init`
78
+ - `upload`, `usercontributions`, `users`, `version_tuple_from_generator`, `watchlist`
79
+
80
+ `page` methods:
81
+
82
+ - `append`, `backlinks`, `can`, `categories`, `delete`, `edit`, `embeddedin`
83
+ - `extlinks`, `get_token`, `handle_edit_error`, `images`, `iwlinks`, `langlinks`, `links`
84
+ - `move`, `normalize_title`, `prepend`, `purge`, `redirects_to`, `resolve_redirect`
85
+ - `revisions`, `save`, `strip_namespace`, `templates`, `text`, `touch`
86
+
87
+ `image` methods:
88
+
89
+ - all `page` methods, plus:
90
+ - `download`, `duplicatefiles`, `imagehistory`, `imageusage`
91
+
92
+ To confirm runtime command surface on your installed version:
93
+
94
+ ```bash
95
+ python -m mwcli methods all
96
+ ```
97
+
98
+ ## Main usage examples
99
+
100
+ ### Get page content
101
+
102
+ ```bash
103
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
104
+ page "Main Page" text
105
+ ```
106
+
107
+ Read one section:
108
+
109
+ ```bash
110
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
111
+ page "Main Page" text --kw section=1
112
+ ```
113
+
114
+ Read as Markdown (`page text` uses `site parse` + `html2text`):
115
+
116
+ ```bash
117
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
118
+ page "Main Page" text --markdown
119
+ ```
120
+
121
+ The markdown output starts with:
122
+
123
+ ```md
124
+ # Main Page
125
+ ```
126
+
127
+ ### Search
128
+
129
+ Full text search:
130
+
131
+ ```bash
132
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
133
+ site search --arg "space" --kw what=text --max-items 10
134
+ ```
135
+
136
+ Title-only search:
137
+
138
+ ```bash
139
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
140
+ site search --arg "Main" --kw what=title --max-items 10
141
+ ```
142
+
143
+ ### Authentication + edit
144
+
145
+ ```bash
146
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
147
+ --username "Admin" --password "secret" \
148
+ page "Sandbox" edit \
149
+ --arg "Edited from mwcli ~~~~" \
150
+ --kw summary="mwcli test edit" \
151
+ --kw bot=false
152
+ ```
153
+
154
+ ### File upload
155
+
156
+ Local file upload:
157
+
158
+ ```bash
159
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
160
+ --username "Admin" --password "secret" \
161
+ site upload \
162
+ --kw file="/tmp/example.png" \
163
+ --kw filename="Example.png" \
164
+ --kw description="Uploaded by mwcli"
165
+ ```
166
+
167
+ Upload from URL:
168
+
169
+ ```bash
170
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
171
+ --username "Admin" --password "secret" \
172
+ site upload \
173
+ --kw url="https://example.com/example.png" \
174
+ --kw filename="ExampleFromURL.png"
175
+ ```
176
+
177
+ ### Semantic MediaWiki ask API
178
+
179
+ ```bash
180
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
181
+ site ask --arg '[[Category:Item]]|?Has author|?Has status' --max-items 20
182
+ ```
183
+
184
+ With an explicit title context:
185
+
186
+ ```bash
187
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
188
+ site ask --arg '[[Category:Item]]|?Has author' --kw title="Main Page" --max-items 20
189
+ ```
190
+
191
+ Fetch all SMW properties for a page (`smwbrowse`):
192
+
193
+ ```bash
194
+ python -m mwcli --indent 2 --host host.docker.internal --scheme http --path /w/ \
195
+ site raw_api --arg smwbrowse --arg GET --kw browse=subject \
196
+ --kw params='"{\"subject\":\"Main Page\",\"ns\":0}"'
197
+ ```
198
+
199
+ Note: `smwbrowse` expects `params` as a JSON string, so pass a JSON object wrapped as a quoted string (double-encoded).
200
+
201
+ ### Arbitrary API calls
202
+
203
+ Use `get` / `post` / `api` / `raw_api` directly.
204
+
205
+ Get siteinfo:
206
+
207
+ ```bash
208
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
209
+ site get --arg query --kw meta=siteinfo --kw siprop='general|namespaces'
210
+ ```
211
+
212
+ Generic `api` call with GET method:
213
+
214
+ ```bash
215
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
216
+ site api --arg query --arg GET --kw prop=info --kw titles="Main Page"
217
+ ```
218
+
219
+ Raw API (no extra wrapper logic):
220
+
221
+ ```bash
222
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
223
+ site raw_api --arg query --arg GET --kw list=search --kw srsearch=space
224
+ ```
225
+
226
+ Parse wikitext/HTML directly as Markdown:
227
+
228
+ ```bash
229
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
230
+ site parse --kw text=$'== Header ==\n\nBody' --markdown
231
+ ```
232
+
233
+ `--markdown` currently applies to:
234
+
235
+ - `page text`
236
+ - `site parse`
237
+
238
+ Implementation uses `html2text`: https://pypi.org/project/html2text/
239
+
240
+ ### Page and image list iterators
241
+
242
+ Recent changes:
243
+
244
+ ```bash
245
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
246
+ site recentchanges --kw prop='title|timestamp|user|comment' --max-items 5
247
+ ```
248
+
249
+ Image usage:
250
+
251
+ ```bash
252
+ python -m mwcli --host host.docker.internal --scheme http --path /w/ \
253
+ image "Example.png" imageusage --max-items 10
254
+ ```
255
+
256
+ ## Env vars
257
+
258
+ You can use env vars instead of flags:
259
+
260
+ - `MWCLI_HOST`
261
+ - `MWCLI_PATH` (default `/w/`)
262
+ - `MWCLI_EXT` (default `.php`)
263
+ - `MWCLI_SCHEME` (default `https`)
264
+ - `MWCLI_USERNAME`
265
+ - `MWCLI_PASSWORD`
266
+ - `MWCLI_USER_AGENT`
267
+
268
+ Then run shorter commands, for example:
269
+
270
+ ```bash
271
+ export MWCLI_HOST=host.docker.internal
272
+ export MWCLI_SCHEME=http
273
+ export MWCLI_PATH=/w/
274
+ python -m mwcli site search --arg "space" --kw what=text --max-items 5
275
+ ```
@@ -0,0 +1,7 @@
1
+ mwclient-cli/__init__.py,sha256=ijQ-WRX2098384M1mIsFFZuAQsN2yd9cdMSlUPs-_ss,69
2
+ mwclient-cli/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
3
+ mwclient-cli/cli.py,sha256=nfpGfZhhrt6K9-BuQ-rPX51HmW5KBlQFQMhaZKyqzFE,12458
4
+ mwclient_cli-0.1.0.dist-info/METADATA,sha256=ShUr_rt3zc64UN7G_JmoX0oD27NoZYWfhyL5Tm0b-2w,6959
5
+ mwclient_cli-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
+ mwclient_cli-0.1.0.dist-info/entry_points.txt,sha256=kwASmUbnKbzvecArk8v0BWnoQQHQBnEvxV7cc0OnONI,48
7
+ mwclient_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mwcli = mwclient-cli.cli:main