onemore-design 1.0.0
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.
- package/LICENSE +21 -0
- package/SKILL.md +180 -0
- package/bin/onemore.js +23 -0
- package/data/audit/hig-checklist.csv +39 -0
- package/data/components/content.csv +17 -0
- package/data/components/controls.csv +21 -0
- package/data/components/feedback.csv +17 -0
- package/data/components/input.csv +11 -0
- package/data/components/navigation.csv +15 -0
- package/data/foundations/colors.csv +38 -0
- package/data/foundations/corners.csv +13 -0
- package/data/foundations/elevation.csv +17 -0
- package/data/foundations/spacing.csv +21 -0
- package/data/foundations/typography.csv +26 -0
- package/data/patterns/animation.csv +24 -0
- package/data/patterns/gestures.csv +11 -0
- package/data/patterns/interaction.csv +16 -0
- package/data/patterns/layout.csv +20 -0
- package/data/platforms/ios.csv +21 -0
- package/data/platforms/macos.csv +16 -0
- package/data/platforms/visionos.csv +11 -0
- package/data/platforms/watchos.csv +11 -0
- package/data/platforms/web-apple.csv +21 -0
- package/data/reasoning/apple-reasoning.csv +16 -0
- package/data/stacks/astro.csv +21 -0
- package/data/stacks/flutter.csv +29 -0
- package/data/stacks/html-tailwind.csv +26 -0
- package/data/stacks/nativewind.csv +26 -0
- package/data/stacks/nextjs.csv +26 -0
- package/data/stacks/nuxtjs.csv +21 -0
- package/data/stacks/react-native.csv +26 -0
- package/data/stacks/react.csv +26 -0
- package/data/stacks/shadcn.csv +25 -0
- package/data/stacks/svelte.csv +21 -0
- package/data/stacks/swiftui.csv +31 -0
- package/data/stacks/uikit.csv +21 -0
- package/data/stacks/vue.csv +21 -0
- package/package.json +51 -0
- package/scripts/__init__.py +0 -0
- package/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/scripts/__pycache__/exporter.cpython-314.pyc +0 -0
- package/scripts/__pycache__/platforms.cpython-314.pyc +0 -0
- package/scripts/__pycache__/redesign.cpython-314.pyc +0 -0
- package/scripts/core.py +242 -0
- package/scripts/design_system.py +291 -0
- package/scripts/exporter.py +717 -0
- package/scripts/platforms.py +309 -0
- package/scripts/redesign.py +758 -0
- package/scripts/search.py +426 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""OneMore CLI — Apple HIG design intelligence search."""
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Allow running as a script from any working directory
|
|
9
|
+
_SCRIPTS_DIR = Path(__file__).parent
|
|
10
|
+
_PROJECT_ROOT = _SCRIPTS_DIR.parent
|
|
11
|
+
if str(_PROJECT_ROOT) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(_PROJECT_ROOT))
|
|
13
|
+
|
|
14
|
+
from scripts.core import (
|
|
15
|
+
CSV_CONFIG,
|
|
16
|
+
PLATFORM_CONFIG,
|
|
17
|
+
STACK_CONFIG,
|
|
18
|
+
search,
|
|
19
|
+
search_platform,
|
|
20
|
+
search_stack,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Output Formatting
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
_TRUNCATE = 200
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _truncate(value: str, limit: int = _TRUNCATE) -> str:
|
|
31
|
+
if len(value) <= limit:
|
|
32
|
+
return value
|
|
33
|
+
return value[:limit] + "…"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def format_output(
|
|
37
|
+
results: list[dict[str, str]],
|
|
38
|
+
*,
|
|
39
|
+
query: str = "",
|
|
40
|
+
domain: str | None = None,
|
|
41
|
+
stack: str | None = None,
|
|
42
|
+
platform: str | None = None,
|
|
43
|
+
fmt: str = "ascii",
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Render results as a human-readable string."""
|
|
46
|
+
lines: list[str] = []
|
|
47
|
+
|
|
48
|
+
# --- Header ---
|
|
49
|
+
scope_parts: list[str] = []
|
|
50
|
+
if domain:
|
|
51
|
+
scope_parts.append(f"domain={domain}")
|
|
52
|
+
if stack:
|
|
53
|
+
scope_parts.append(f"stack={stack}")
|
|
54
|
+
if platform:
|
|
55
|
+
scope_parts.append(f"platform={platform}")
|
|
56
|
+
scope = ", ".join(scope_parts) if scope_parts else "auto"
|
|
57
|
+
|
|
58
|
+
if fmt == "markdown":
|
|
59
|
+
lines.append(f"## OneMore Search")
|
|
60
|
+
if query:
|
|
61
|
+
lines.append(f"**Query:** {query} ")
|
|
62
|
+
lines.append(f"**Scope:** {scope} ")
|
|
63
|
+
lines.append(f"**Results:** {len(results)}")
|
|
64
|
+
lines.append("")
|
|
65
|
+
else:
|
|
66
|
+
separator = "=" * 60
|
|
67
|
+
lines.append(separator)
|
|
68
|
+
lines.append(" OneMore")
|
|
69
|
+
if query:
|
|
70
|
+
lines.append(f" Query : {query}")
|
|
71
|
+
lines.append(f" Scope : {scope}")
|
|
72
|
+
lines.append(f" Results : {len(results)}")
|
|
73
|
+
lines.append(separator)
|
|
74
|
+
|
|
75
|
+
if not results:
|
|
76
|
+
lines.append("")
|
|
77
|
+
lines.append(" No results found.")
|
|
78
|
+
hints: list[str] = []
|
|
79
|
+
if domain and domain not in CSV_CONFIG:
|
|
80
|
+
hints.append(f" '{domain}' is not a known domain. Valid domains: {', '.join(sorted(CSV_CONFIG))}")
|
|
81
|
+
if stack and stack not in STACK_CONFIG:
|
|
82
|
+
hints.append(f" '{stack}' is not a known stack. Valid stacks: {', '.join(sorted(STACK_CONFIG))}")
|
|
83
|
+
if platform and platform not in PLATFORM_CONFIG:
|
|
84
|
+
hints.append(f" '{platform}' is not a known platform. Valid platforms: {', '.join(sorted(PLATFORM_CONFIG))}")
|
|
85
|
+
if not hints:
|
|
86
|
+
hints.append(" Try broadening your query or checking a different domain.")
|
|
87
|
+
lines.extend(hints)
|
|
88
|
+
lines.append("")
|
|
89
|
+
return "\n".join(lines)
|
|
90
|
+
|
|
91
|
+
# --- Results ---
|
|
92
|
+
for idx, row in enumerate(results, start=1):
|
|
93
|
+
if fmt == "markdown":
|
|
94
|
+
lines.append(f"### Result {idx}")
|
|
95
|
+
for key, value in row.items():
|
|
96
|
+
lines.append(f"- **{key}**: {_truncate(str(value))}")
|
|
97
|
+
lines.append("")
|
|
98
|
+
else:
|
|
99
|
+
lines.append(f"\n [{idx}]")
|
|
100
|
+
for key, value in row.items():
|
|
101
|
+
lines.append(f" {key:<20} {_truncate(str(value))}")
|
|
102
|
+
|
|
103
|
+
if fmt != "markdown":
|
|
104
|
+
lines.append("")
|
|
105
|
+
|
|
106
|
+
return "\n".join(lines)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Argument Parser
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
114
|
+
parser = argparse.ArgumentParser(
|
|
115
|
+
prog="onemore",
|
|
116
|
+
description="Search Apple HIG design intelligence from the command line.",
|
|
117
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
118
|
+
epilog="\n".join([
|
|
119
|
+
"Examples:",
|
|
120
|
+
" onemore 'blue accent color'",
|
|
121
|
+
" onemore 'button' --domain controls",
|
|
122
|
+
" onemore 'navigation' --platform ios",
|
|
123
|
+
" onemore 'button' --stack swiftui",
|
|
124
|
+
" onemore 'button' --json",
|
|
125
|
+
" onemore --design-system --project-name MyApp",
|
|
126
|
+
]),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
parser.add_argument(
|
|
130
|
+
"query",
|
|
131
|
+
nargs="?",
|
|
132
|
+
default="",
|
|
133
|
+
help="Search query text",
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"-d", "--domain",
|
|
137
|
+
choices=list(CSV_CONFIG.keys()),
|
|
138
|
+
metavar="DOMAIN",
|
|
139
|
+
help=f"Force a specific domain. Choices: {', '.join(sorted(CSV_CONFIG))}",
|
|
140
|
+
)
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
"-s", "--stack",
|
|
143
|
+
choices=list(STACK_CONFIG.keys()),
|
|
144
|
+
metavar="STACK",
|
|
145
|
+
help=f"Stack-specific search. Choices: {', '.join(sorted(STACK_CONFIG))}",
|
|
146
|
+
)
|
|
147
|
+
parser.add_argument(
|
|
148
|
+
"-p", "--platform",
|
|
149
|
+
choices=list(PLATFORM_CONFIG.keys()),
|
|
150
|
+
metavar="PLATFORM",
|
|
151
|
+
help=f"Platform-specific search. Choices: {', '.join(sorted(PLATFORM_CONFIG))}",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"-n", "--max-results",
|
|
155
|
+
type=int,
|
|
156
|
+
default=5,
|
|
157
|
+
metavar="N",
|
|
158
|
+
help="Maximum number of results to return (default: 5)",
|
|
159
|
+
)
|
|
160
|
+
parser.add_argument(
|
|
161
|
+
"--json",
|
|
162
|
+
action="store_true",
|
|
163
|
+
dest="json_output",
|
|
164
|
+
help="Output results as JSON",
|
|
165
|
+
)
|
|
166
|
+
parser.add_argument(
|
|
167
|
+
"--design-system",
|
|
168
|
+
action="store_true",
|
|
169
|
+
help="Generate a full design system (stub)",
|
|
170
|
+
)
|
|
171
|
+
parser.add_argument(
|
|
172
|
+
"--project-name",
|
|
173
|
+
default="MyApp",
|
|
174
|
+
metavar="NAME",
|
|
175
|
+
help="Project name for design system generation (default: MyApp)",
|
|
176
|
+
)
|
|
177
|
+
parser.add_argument(
|
|
178
|
+
"-f", "--format",
|
|
179
|
+
choices=["ascii", "markdown"],
|
|
180
|
+
default="ascii",
|
|
181
|
+
help="Output format: ascii or markdown (default: ascii)",
|
|
182
|
+
)
|
|
183
|
+
parser.add_argument(
|
|
184
|
+
"--persist",
|
|
185
|
+
action="store_true",
|
|
186
|
+
help="Save design system output to files",
|
|
187
|
+
)
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"--page",
|
|
190
|
+
metavar="PAGE",
|
|
191
|
+
help="Page-specific override for design system generation",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return parser
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ---------------------------------------------------------------------------
|
|
198
|
+
# Main
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
def _handle_init(args: list[str]) -> int:
|
|
202
|
+
"""Handle the 'init' subcommand for multi-platform distribution."""
|
|
203
|
+
from scripts.platforms import init_platform, list_platforms, detect_platforms, auto_init, PLATFORMS
|
|
204
|
+
|
|
205
|
+
if "--list" in args:
|
|
206
|
+
platforms = list_platforms()
|
|
207
|
+
print(f"{'Platform':<16} {'Name':<18} {'Type':<10} {'Status':<12} Path")
|
|
208
|
+
print("-" * 80)
|
|
209
|
+
for p in platforms:
|
|
210
|
+
status = "installed" if p["installed"] else "-"
|
|
211
|
+
print(f"{p['platform']:<16} {p['name']:<18} {p['type']:<10} {status:<12} {p['path']}")
|
|
212
|
+
return 0
|
|
213
|
+
|
|
214
|
+
# Find --ai value
|
|
215
|
+
ai_value = None
|
|
216
|
+
for i, arg in enumerate(args):
|
|
217
|
+
if arg == "--ai" and i + 1 < len(args):
|
|
218
|
+
ai_value = args[i + 1]
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
if not ai_value:
|
|
222
|
+
# Auto-detect mode: no --ai flag given
|
|
223
|
+
print("OneMore — Auto-Detecting AI Platforms...\n")
|
|
224
|
+
|
|
225
|
+
detected_list = detect_platforms()
|
|
226
|
+
for p in detected_list:
|
|
227
|
+
mark = "✓" if p["detected"] else "✗"
|
|
228
|
+
reason = p["reason"] if p["detected"] else "not detected"
|
|
229
|
+
print(f" {mark} {p['name']:<16} {reason}")
|
|
230
|
+
|
|
231
|
+
detected_count = sum(1 for p in detected_list if p["detected"])
|
|
232
|
+
if detected_count == 0:
|
|
233
|
+
print("\nNo AI platforms detected. Use --ai <platform> to install manually.")
|
|
234
|
+
print(f"Available platforms: {', '.join(sorted(PLATFORMS.keys()))}")
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
print(f"\nInstalling OneMore for {detected_count} detected platform(s)...\n")
|
|
238
|
+
|
|
239
|
+
result = auto_init()
|
|
240
|
+
installed_names = {p["platform"] for p in result["installed"]}
|
|
241
|
+
skipped_names = {p["platform"] for p in result["skipped"]}
|
|
242
|
+
|
|
243
|
+
for p in detected_list:
|
|
244
|
+
if not p["detected"]:
|
|
245
|
+
continue
|
|
246
|
+
platform_key = p["platform"]
|
|
247
|
+
config = PLATFORMS[platform_key]
|
|
248
|
+
install_path = config["path"]
|
|
249
|
+
if platform_key in installed_names:
|
|
250
|
+
print(f" ✓ {p['name']:<16} → {install_path}")
|
|
251
|
+
elif platform_key in skipped_names:
|
|
252
|
+
print(f" ✓ {p['name']:<16} → {install_path} (already installed)")
|
|
253
|
+
|
|
254
|
+
newly_installed = len(result["installed"])
|
|
255
|
+
already_installed = len(result["skipped"])
|
|
256
|
+
total = newly_installed + already_installed
|
|
257
|
+
if already_installed > 0:
|
|
258
|
+
print(f"\nDone! OneMore installed for {total} platform(s) ({already_installed} already installed).")
|
|
259
|
+
else:
|
|
260
|
+
print(f"\nDone! OneMore installed for {total} platform(s).")
|
|
261
|
+
return 0
|
|
262
|
+
|
|
263
|
+
# Determine which platforms to init
|
|
264
|
+
if ai_value == "all":
|
|
265
|
+
targets = [name for name, cfg in PLATFORMS.items() if cfg["type"] == "project"]
|
|
266
|
+
else:
|
|
267
|
+
targets = [t.strip() for t in ai_value.split(",")]
|
|
268
|
+
|
|
269
|
+
for platform_name in targets:
|
|
270
|
+
result = init_platform(platform_name)
|
|
271
|
+
if "error" in result:
|
|
272
|
+
print(f" ERROR {platform_name}: {result['error']}")
|
|
273
|
+
else:
|
|
274
|
+
print(f" {result['status'].upper():<18} {result['platform']:<18} {result.get('path', '')}")
|
|
275
|
+
|
|
276
|
+
return 0
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _handle_export(args: list[str]) -> int:
|
|
280
|
+
"""Handle the 'export' subcommand for design token code generation."""
|
|
281
|
+
from scripts.exporter import EXPORT_FORMATS, export_tokens
|
|
282
|
+
|
|
283
|
+
if "--list" in args:
|
|
284
|
+
print(f"{'Format':<16} {'Output File':<24}")
|
|
285
|
+
print("-" * 40)
|
|
286
|
+
for name, cfg in EXPORT_FORMATS.items():
|
|
287
|
+
print(f"{name:<16} {cfg['filename']:<24}")
|
|
288
|
+
return 0
|
|
289
|
+
|
|
290
|
+
# Parse --format and --output
|
|
291
|
+
fmt_value = None
|
|
292
|
+
output_value = None
|
|
293
|
+
for i, arg in enumerate(args):
|
|
294
|
+
if arg == "--format" and i + 1 < len(args):
|
|
295
|
+
fmt_value = args[i + 1]
|
|
296
|
+
elif arg == "--output" and i + 1 < len(args):
|
|
297
|
+
output_value = args[i + 1]
|
|
298
|
+
|
|
299
|
+
if not fmt_value:
|
|
300
|
+
print("Usage: onemore export --format <format|all> [--output <dir>]")
|
|
301
|
+
print(" onemore export --list")
|
|
302
|
+
print(f"\nAvailable formats: {', '.join(EXPORT_FORMATS.keys())}")
|
|
303
|
+
return 1
|
|
304
|
+
|
|
305
|
+
if fmt_value == "all":
|
|
306
|
+
targets = list(EXPORT_FORMATS.keys())
|
|
307
|
+
else:
|
|
308
|
+
targets = [fmt_value]
|
|
309
|
+
|
|
310
|
+
for target in targets:
|
|
311
|
+
result = export_tokens(target, output_dir=output_value)
|
|
312
|
+
if "error" in result:
|
|
313
|
+
print(f" ERROR {result['error']}")
|
|
314
|
+
else:
|
|
315
|
+
print(f" {result['status'].upper():<12} {result['format']:<16} -> {result['file']}")
|
|
316
|
+
|
|
317
|
+
return 0
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _handle_redesign(args: list[str]) -> int:
|
|
321
|
+
"""Handle the 'redesign' subcommand for HIG violation scanning."""
|
|
322
|
+
from scripts.redesign import cli_main
|
|
323
|
+
return cli_main(args)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def main() -> int:
|
|
327
|
+
# Handle subcommands before argparse
|
|
328
|
+
raw_args = sys.argv[1:]
|
|
329
|
+
if raw_args and raw_args[0] == "export":
|
|
330
|
+
return _handle_export(raw_args[1:])
|
|
331
|
+
if raw_args and raw_args[0] == "init":
|
|
332
|
+
return _handle_init(raw_args[1:])
|
|
333
|
+
if raw_args and raw_args[0] == "redesign":
|
|
334
|
+
return _handle_redesign(raw_args[1:])
|
|
335
|
+
|
|
336
|
+
# Use parse_known_args to allow unknown domain/stack/platform values
|
|
337
|
+
# without crashing, then validate manually so we can surface helpful messages.
|
|
338
|
+
parser = build_parser()
|
|
339
|
+
|
|
340
|
+
# Patch: temporarily widen domain/stack/platform to accept any string so
|
|
341
|
+
# unknown values are handled gracefully rather than argparse error-exiting.
|
|
342
|
+
_patched_parser = argparse.ArgumentParser(
|
|
343
|
+
prog="onemore",
|
|
344
|
+
description=parser.description,
|
|
345
|
+
formatter_class=parser.formatter_class,
|
|
346
|
+
epilog=parser.epilog,
|
|
347
|
+
add_help=True,
|
|
348
|
+
)
|
|
349
|
+
_patched_parser.add_argument("query", nargs="?", default="")
|
|
350
|
+
_patched_parser.add_argument("-d", "--domain", default=None, metavar="DOMAIN")
|
|
351
|
+
_patched_parser.add_argument("-s", "--stack", default=None, metavar="STACK")
|
|
352
|
+
_patched_parser.add_argument("-p", "--platform", default=None, metavar="PLATFORM")
|
|
353
|
+
_patched_parser.add_argument("-n", "--max-results", type=int, default=5, metavar="N")
|
|
354
|
+
_patched_parser.add_argument("--json", action="store_true", dest="json_output")
|
|
355
|
+
_patched_parser.add_argument("--design-system", action="store_true")
|
|
356
|
+
_patched_parser.add_argument("--project-name", default="MyApp", metavar="NAME")
|
|
357
|
+
_patched_parser.add_argument("-f", "--format", choices=["ascii", "markdown"], default="ascii")
|
|
358
|
+
_patched_parser.add_argument("--persist", action="store_true")
|
|
359
|
+
_patched_parser.add_argument("--page", default=None, metavar="PAGE")
|
|
360
|
+
|
|
361
|
+
# Show proper help from the nicer parser if --help requested
|
|
362
|
+
if "-h" in raw_args or "--help" in raw_args:
|
|
363
|
+
parser.print_help()
|
|
364
|
+
return 0
|
|
365
|
+
|
|
366
|
+
args = _patched_parser.parse_args(raw_args)
|
|
367
|
+
|
|
368
|
+
query: str = args.query or ""
|
|
369
|
+
domain: str | None = args.domain
|
|
370
|
+
stack: str | None = args.stack
|
|
371
|
+
platform: str | None = args.platform
|
|
372
|
+
max_results: int = args.max_results
|
|
373
|
+
json_output: bool = args.json_output
|
|
374
|
+
fmt: str = args.format
|
|
375
|
+
|
|
376
|
+
# ------------------------------------------------------------------
|
|
377
|
+
# Priority 1: --design-system
|
|
378
|
+
# ------------------------------------------------------------------
|
|
379
|
+
if args.design_system:
|
|
380
|
+
try:
|
|
381
|
+
from scripts.design_system import DesignSystemGenerator # type: ignore
|
|
382
|
+
generator = DesignSystemGenerator(project_name=args.project_name)
|
|
383
|
+
output = generator.generate(fmt=fmt, persist=args.persist, page=args.page)
|
|
384
|
+
print(output)
|
|
385
|
+
except ImportError:
|
|
386
|
+
print(
|
|
387
|
+
f"Design system generator for '{args.project_name}' is not yet implemented.\n"
|
|
388
|
+
"Run without --design-system to search the design token database."
|
|
389
|
+
)
|
|
390
|
+
return 0
|
|
391
|
+
|
|
392
|
+
# ------------------------------------------------------------------
|
|
393
|
+
# Priority 2: --stack
|
|
394
|
+
# ------------------------------------------------------------------
|
|
395
|
+
if stack:
|
|
396
|
+
results = search_stack(query, stack, max_results=max_results)
|
|
397
|
+
if json_output:
|
|
398
|
+
print(json.dumps(results, ensure_ascii=False, indent=2))
|
|
399
|
+
else:
|
|
400
|
+
print(format_output(results, query=query, stack=stack, fmt=fmt))
|
|
401
|
+
return 0
|
|
402
|
+
|
|
403
|
+
# ------------------------------------------------------------------
|
|
404
|
+
# Priority 3: --platform (without --domain)
|
|
405
|
+
# ------------------------------------------------------------------
|
|
406
|
+
if platform and not domain:
|
|
407
|
+
results = search_platform(query, platform, max_results=max_results)
|
|
408
|
+
if json_output:
|
|
409
|
+
print(json.dumps(results, ensure_ascii=False, indent=2))
|
|
410
|
+
else:
|
|
411
|
+
print(format_output(results, query=query, platform=platform, fmt=fmt))
|
|
412
|
+
return 0
|
|
413
|
+
|
|
414
|
+
# ------------------------------------------------------------------
|
|
415
|
+
# Default: search() with optional domain and platform filter
|
|
416
|
+
# ------------------------------------------------------------------
|
|
417
|
+
results = search(query, domain=domain, max_results=max_results, platform=platform)
|
|
418
|
+
if json_output:
|
|
419
|
+
print(json.dumps(results, ensure_ascii=False, indent=2))
|
|
420
|
+
else:
|
|
421
|
+
print(format_output(results, query=query, domain=domain, platform=platform, fmt=fmt))
|
|
422
|
+
return 0
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
if __name__ == "__main__":
|
|
426
|
+
sys.exit(main())
|