spawn-cli 0.1.1__tar.gz
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.
- spawn_cli-0.1.1/PKG-INFO +11 -0
- spawn_cli-0.1.1/pyproject.toml +31 -0
- spawn_cli-0.1.1/setup.cfg +4 -0
- spawn_cli-0.1.1/src/spawn_cli/__init__.py +3 -0
- spawn_cli-0.1.1/src/spawn_cli/cli.py +243 -0
- spawn_cli-0.1.1/src/spawn_cli/core/__init__.py +5 -0
- spawn_cli-0.1.1/src/spawn_cli/core/download.py +532 -0
- spawn_cli-0.1.1/src/spawn_cli/core/errors.py +5 -0
- spawn_cli-0.1.1/src/spawn_cli/core/high_level.py +462 -0
- spawn_cli-0.1.1/src/spawn_cli/core/low_level.py +914 -0
- spawn_cli-0.1.1/src/spawn_cli/core/scripts.py +246 -0
- spawn_cli-0.1.1/src/spawn_cli/errors.py +9 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/__init__.py +63 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/_helpers.py +168 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/_stub.py +63 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/_vacancy.py +221 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/claude_code.py +145 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/codex.py +120 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/cursor.py +122 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/gemini_cli.py +130 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/github_copilot.py +167 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/registry.py +142 -0
- spawn_cli-0.1.1/src/spawn_cli/ide/windsurf.py +91 -0
- spawn_cli-0.1.1/src/spawn_cli/io/__init__.py +21 -0
- spawn_cli-0.1.1/src/spawn_cli/io/json_io.py +21 -0
- spawn_cli-0.1.1/src/spawn_cli/io/lock.py +24 -0
- spawn_cli-0.1.1/src/spawn_cli/io/paths.py +23 -0
- spawn_cli-0.1.1/src/spawn_cli/io/text_io.py +16 -0
- spawn_cli-0.1.1/src/spawn_cli/io/toml_io.py +26 -0
- spawn_cli-0.1.1/src/spawn_cli/io/yaml_io.py +32 -0
- spawn_cli-0.1.1/src/spawn_cli/models/__init__.py +63 -0
- spawn_cli-0.1.1/src/spawn_cli/models/config.py +111 -0
- spawn_cli-0.1.1/src/spawn_cli/models/mcp.py +49 -0
- spawn_cli-0.1.1/src/spawn_cli/models/metadata.py +28 -0
- spawn_cli-0.1.1/src/spawn_cli/models/navigation.py +30 -0
- spawn_cli-0.1.1/src/spawn_cli/models/skill.py +29 -0
- spawn_cli-0.1.1/src/spawn_cli/resources/__init__.py +0 -0
- spawn_cli-0.1.1/src/spawn_cli/resources/default_core_config.yaml +5 -0
- spawn_cli-0.1.1/src/spawn_cli/warnings_display.py +35 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/PKG-INFO +11 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/SOURCES.txt +45 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/dependency_links.txt +1 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/entry_points.txt +2 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/requires.txt +8 -0
- spawn_cli-0.1.1/src/spawn_cli.egg-info/top_level.txt +1 -0
- spawn_cli-0.1.1/tests/test_cli.py +318 -0
- spawn_cli-0.1.1/tests/test_warnings_display.py +48 -0
spawn_cli-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spawn-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A minimal installable Python CLI package.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: pydantic>=2.0
|
|
7
|
+
Requires-Dist: ruamel.yaml>=0.18
|
|
8
|
+
Requires-Dist: filelock>=3.12
|
|
9
|
+
Requires-Dist: httpx>=0.27
|
|
10
|
+
Requires-Dist: tomli>=2.0; python_version < "3.11"
|
|
11
|
+
Requires-Dist: tomli-w>=1.0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "spawn-cli"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "A minimal installable Python CLI package."
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"pydantic>=2.0",
|
|
12
|
+
"ruamel.yaml>=0.18",
|
|
13
|
+
"filelock>=3.12",
|
|
14
|
+
"httpx>=0.27",
|
|
15
|
+
'tomli>=2.0; python_version < "3.11"',
|
|
16
|
+
"tomli-w>=1.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
spawn = "spawn_cli.cli:main"
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["src"]
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.package-data]
|
|
26
|
+
"spawn_cli.resources" = ["*.yaml"]
|
|
27
|
+
|
|
28
|
+
[tool.pytest.ini_options]
|
|
29
|
+
testpaths = ["tests"]
|
|
30
|
+
addopts = "-v"
|
|
31
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from spawn_cli import __version__
|
|
8
|
+
from spawn_cli.core import download as dl
|
|
9
|
+
from spawn_cli.core import high_level as hl
|
|
10
|
+
from spawn_cli.core import low_level as ll
|
|
11
|
+
from spawn_cli.core.errors import SpawnError
|
|
12
|
+
from spawn_cli.ide.registry import DetectResult, detect_supported_ides
|
|
13
|
+
from spawn_cli.io.lock import spawn_lock
|
|
14
|
+
from spawn_cli.warnings_display import install_spawn_warning_format
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _require_init(target_root: Path) -> None:
|
|
18
|
+
if not (target_root / "spawn").is_dir():
|
|
19
|
+
raise SpawnError("need init before: run spawn init in this repository.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
23
|
+
parser = argparse.ArgumentParser(prog="spawn", description="Spawn CLI")
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--version",
|
|
26
|
+
action="version",
|
|
27
|
+
version=f"spawn {__version__}",
|
|
28
|
+
)
|
|
29
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
30
|
+
|
|
31
|
+
sub.add_parser("init", help="Initialize spawn/ in the current repository")
|
|
32
|
+
|
|
33
|
+
rules_p = sub.add_parser("rules")
|
|
34
|
+
rules_sub = rules_p.add_subparsers(dest="rules_command", required=True)
|
|
35
|
+
rules_sub.add_parser("refresh", help="Sync spawn/rules/ into navigation.yaml")
|
|
36
|
+
|
|
37
|
+
ide_p = sub.add_parser("ide")
|
|
38
|
+
ide_sub = ide_p.add_subparsers(dest="ide_command", required=True)
|
|
39
|
+
_ide_add = ide_sub.add_parser("add")
|
|
40
|
+
_ide_add.add_argument("ides", nargs="+")
|
|
41
|
+
_ide_remove = ide_sub.add_parser("remove")
|
|
42
|
+
_ide_remove.add_argument("ides", nargs="+")
|
|
43
|
+
ide_sub.add_parser("list")
|
|
44
|
+
ide_sub.add_parser("list-supported-ides")
|
|
45
|
+
|
|
46
|
+
ext_p = sub.add_parser("extension")
|
|
47
|
+
ext_sub = ext_p.add_subparsers(dest="ext_command", required=True)
|
|
48
|
+
_ext_add = ext_sub.add_parser("add")
|
|
49
|
+
_ext_add.add_argument("path")
|
|
50
|
+
_ext_add.add_argument("--branch", default=None)
|
|
51
|
+
_ext_update = ext_sub.add_parser("update")
|
|
52
|
+
_ext_update.add_argument("extension_name")
|
|
53
|
+
_ext_remove = ext_sub.add_parser("remove")
|
|
54
|
+
_ext_remove.add_argument("extension_name")
|
|
55
|
+
_ext_reinstall = ext_sub.add_parser("reinstall")
|
|
56
|
+
_ext_reinstall.add_argument("extension_name")
|
|
57
|
+
ext_sub.add_parser("list")
|
|
58
|
+
_ext_init = ext_sub.add_parser("init")
|
|
59
|
+
_ext_init.add_argument("path", nargs="?", default=".")
|
|
60
|
+
_ext_init.add_argument("--name", required=True)
|
|
61
|
+
_ext_check = ext_sub.add_parser("check")
|
|
62
|
+
_ext_check.add_argument("path", nargs="?", default=".")
|
|
63
|
+
_ext_check.add_argument("--strict", action="store_true")
|
|
64
|
+
_ext_from_rules = ext_sub.add_parser("from-rules")
|
|
65
|
+
_ext_from_rules.add_argument("source")
|
|
66
|
+
_ext_from_rules.add_argument("--name", required=True)
|
|
67
|
+
_ext_from_rules.add_argument("--branch", default=None)
|
|
68
|
+
_ext_from_rules.add_argument("--output", default=".")
|
|
69
|
+
_ext_health = ext_sub.add_parser("healthcheck")
|
|
70
|
+
_ext_health.add_argument("extension_name")
|
|
71
|
+
|
|
72
|
+
build_p = sub.add_parser("build")
|
|
73
|
+
build_sub = build_p.add_subparsers(dest="build_command", required=True)
|
|
74
|
+
_build_install = build_sub.add_parser("install")
|
|
75
|
+
_build_install.add_argument("path")
|
|
76
|
+
_build_install.add_argument("--branch", default=None)
|
|
77
|
+
_build_list = build_sub.add_parser("list")
|
|
78
|
+
_build_list.add_argument("path")
|
|
79
|
+
_build_list.add_argument("--branch", default=None)
|
|
80
|
+
|
|
81
|
+
return parser
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main(argv: list[str] | None = None) -> int:
|
|
85
|
+
install_spawn_warning_format()
|
|
86
|
+
parser = build_parser()
|
|
87
|
+
args = parser.parse_args(argv)
|
|
88
|
+
target_root = Path.cwd().resolve()
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
return _dispatch(args, target_root)
|
|
92
|
+
except SpawnError as e:
|
|
93
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
94
|
+
return 1
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"Unexpected error: {e}", file=sys.stderr)
|
|
97
|
+
return 2
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _dispatch(args: argparse.Namespace, target_root: Path) -> int:
|
|
101
|
+
cmd = args.command
|
|
102
|
+
|
|
103
|
+
if cmd == "init":
|
|
104
|
+
with spawn_lock(target_root):
|
|
105
|
+
ll.init(target_root)
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
_require_init(target_root)
|
|
109
|
+
|
|
110
|
+
with spawn_lock(target_root):
|
|
111
|
+
if cmd == "rules":
|
|
112
|
+
return _dispatch_rules(args, target_root)
|
|
113
|
+
if cmd == "ide":
|
|
114
|
+
return _dispatch_ide(args, target_root)
|
|
115
|
+
if cmd == "extension":
|
|
116
|
+
return _dispatch_extension(args, target_root)
|
|
117
|
+
if cmd == "build":
|
|
118
|
+
return _dispatch_build(args, target_root)
|
|
119
|
+
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _dispatch_rules(args: argparse.Namespace, target_root: Path) -> int:
|
|
124
|
+
sub = args.rules_command
|
|
125
|
+
if sub == "refresh":
|
|
126
|
+
hl.refresh_rules_navigation(target_root)
|
|
127
|
+
return 0
|
|
128
|
+
return 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _dispatch_ide(args: argparse.Namespace, target_root: Path) -> int:
|
|
132
|
+
sub = args.ide_command
|
|
133
|
+
|
|
134
|
+
if sub == "list-supported-ides":
|
|
135
|
+
results = detect_supported_ides(target_root)
|
|
136
|
+
_print_yaml(results)
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
if sub == "list":
|
|
140
|
+
ides = ll.list_ides(target_root)
|
|
141
|
+
for ide in ides:
|
|
142
|
+
print(ide)
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
if sub == "add":
|
|
146
|
+
for ide in args.ides:
|
|
147
|
+
hl.add_ide(target_root, ide)
|
|
148
|
+
return 0
|
|
149
|
+
|
|
150
|
+
if sub == "remove":
|
|
151
|
+
for ide in args.ides:
|
|
152
|
+
hl.remove_ide(target_root, ide)
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
return 1
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _dispatch_extension(args: argparse.Namespace, target_root: Path) -> int:
|
|
159
|
+
sub = args.ext_command
|
|
160
|
+
|
|
161
|
+
if sub == "list":
|
|
162
|
+
for ext in ll.list_extensions(target_root):
|
|
163
|
+
print(ext)
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
if sub == "add":
|
|
167
|
+
dl.install_extension(target_root, args.path, args.branch)
|
|
168
|
+
return 0
|
|
169
|
+
|
|
170
|
+
if sub == "update":
|
|
171
|
+
hl.update_extension(target_root, args.extension_name)
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
if sub == "remove":
|
|
175
|
+
hl.remove_extension(target_root, args.extension_name)
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
if sub == "reinstall":
|
|
179
|
+
hl.reinstall_extension(target_root, args.extension_name)
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
if sub == "init":
|
|
183
|
+
hl.extension_init(Path(args.path).resolve(), args.name)
|
|
184
|
+
return 0
|
|
185
|
+
|
|
186
|
+
if sub == "check":
|
|
187
|
+
warn_list = hl.extension_check(Path(args.path).resolve(), strict=args.strict)
|
|
188
|
+
for w in warn_list:
|
|
189
|
+
print(f"Warning: {w}")
|
|
190
|
+
return 0
|
|
191
|
+
|
|
192
|
+
if sub == "from-rules":
|
|
193
|
+
hl.extension_from_rules(
|
|
194
|
+
args.source,
|
|
195
|
+
Path(args.output).resolve(),
|
|
196
|
+
args.name,
|
|
197
|
+
args.branch,
|
|
198
|
+
)
|
|
199
|
+
return 0
|
|
200
|
+
|
|
201
|
+
if sub == "healthcheck":
|
|
202
|
+
ok = hl.extension_healthcheck(target_root, args.extension_name)
|
|
203
|
+
return 0 if ok else 1
|
|
204
|
+
|
|
205
|
+
return 1
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _dispatch_build(args: argparse.Namespace, target_root: Path) -> int:
|
|
209
|
+
sub = args.build_command
|
|
210
|
+
|
|
211
|
+
if sub == "list":
|
|
212
|
+
exts = dl.list_build_extensions(args.path, args.branch)
|
|
213
|
+
_print_yaml(exts)
|
|
214
|
+
return 0
|
|
215
|
+
|
|
216
|
+
if sub == "install":
|
|
217
|
+
dl.install_build(target_root, args.path, args.branch)
|
|
218
|
+
return 0
|
|
219
|
+
|
|
220
|
+
return 1
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _print_yaml(data: object) -> None:
|
|
224
|
+
"""Serialize data to YAML. DetectResult dataclass values use utility.md output shape."""
|
|
225
|
+
from ruamel.yaml import YAML
|
|
226
|
+
|
|
227
|
+
from spawn_cli.io.yaml_io import configure_yaml_dump
|
|
228
|
+
|
|
229
|
+
def _to_serializable(obj: object) -> object:
|
|
230
|
+
if isinstance(obj, DetectResult):
|
|
231
|
+
return {
|
|
232
|
+
"used-in-repo": obj.used_in_repo,
|
|
233
|
+
"capabilities": obj.capabilities.to_dict(),
|
|
234
|
+
}
|
|
235
|
+
if isinstance(obj, dict):
|
|
236
|
+
return {k: _to_serializable(v) for k, v in obj.items()}
|
|
237
|
+
if isinstance(obj, list):
|
|
238
|
+
return [_to_serializable(i) for i in obj]
|
|
239
|
+
return obj
|
|
240
|
+
|
|
241
|
+
yaml = YAML()
|
|
242
|
+
configure_yaml_dump(yaml)
|
|
243
|
+
yaml.dump(_to_serializable(data), sys.stdout)
|