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.
Files changed (47) hide show
  1. spawn_cli-0.1.1/PKG-INFO +11 -0
  2. spawn_cli-0.1.1/pyproject.toml +31 -0
  3. spawn_cli-0.1.1/setup.cfg +4 -0
  4. spawn_cli-0.1.1/src/spawn_cli/__init__.py +3 -0
  5. spawn_cli-0.1.1/src/spawn_cli/cli.py +243 -0
  6. spawn_cli-0.1.1/src/spawn_cli/core/__init__.py +5 -0
  7. spawn_cli-0.1.1/src/spawn_cli/core/download.py +532 -0
  8. spawn_cli-0.1.1/src/spawn_cli/core/errors.py +5 -0
  9. spawn_cli-0.1.1/src/spawn_cli/core/high_level.py +462 -0
  10. spawn_cli-0.1.1/src/spawn_cli/core/low_level.py +914 -0
  11. spawn_cli-0.1.1/src/spawn_cli/core/scripts.py +246 -0
  12. spawn_cli-0.1.1/src/spawn_cli/errors.py +9 -0
  13. spawn_cli-0.1.1/src/spawn_cli/ide/__init__.py +63 -0
  14. spawn_cli-0.1.1/src/spawn_cli/ide/_helpers.py +168 -0
  15. spawn_cli-0.1.1/src/spawn_cli/ide/_stub.py +63 -0
  16. spawn_cli-0.1.1/src/spawn_cli/ide/_vacancy.py +221 -0
  17. spawn_cli-0.1.1/src/spawn_cli/ide/claude_code.py +145 -0
  18. spawn_cli-0.1.1/src/spawn_cli/ide/codex.py +120 -0
  19. spawn_cli-0.1.1/src/spawn_cli/ide/cursor.py +122 -0
  20. spawn_cli-0.1.1/src/spawn_cli/ide/gemini_cli.py +130 -0
  21. spawn_cli-0.1.1/src/spawn_cli/ide/github_copilot.py +167 -0
  22. spawn_cli-0.1.1/src/spawn_cli/ide/registry.py +142 -0
  23. spawn_cli-0.1.1/src/spawn_cli/ide/windsurf.py +91 -0
  24. spawn_cli-0.1.1/src/spawn_cli/io/__init__.py +21 -0
  25. spawn_cli-0.1.1/src/spawn_cli/io/json_io.py +21 -0
  26. spawn_cli-0.1.1/src/spawn_cli/io/lock.py +24 -0
  27. spawn_cli-0.1.1/src/spawn_cli/io/paths.py +23 -0
  28. spawn_cli-0.1.1/src/spawn_cli/io/text_io.py +16 -0
  29. spawn_cli-0.1.1/src/spawn_cli/io/toml_io.py +26 -0
  30. spawn_cli-0.1.1/src/spawn_cli/io/yaml_io.py +32 -0
  31. spawn_cli-0.1.1/src/spawn_cli/models/__init__.py +63 -0
  32. spawn_cli-0.1.1/src/spawn_cli/models/config.py +111 -0
  33. spawn_cli-0.1.1/src/spawn_cli/models/mcp.py +49 -0
  34. spawn_cli-0.1.1/src/spawn_cli/models/metadata.py +28 -0
  35. spawn_cli-0.1.1/src/spawn_cli/models/navigation.py +30 -0
  36. spawn_cli-0.1.1/src/spawn_cli/models/skill.py +29 -0
  37. spawn_cli-0.1.1/src/spawn_cli/resources/__init__.py +0 -0
  38. spawn_cli-0.1.1/src/spawn_cli/resources/default_core_config.yaml +5 -0
  39. spawn_cli-0.1.1/src/spawn_cli/warnings_display.py +35 -0
  40. spawn_cli-0.1.1/src/spawn_cli.egg-info/PKG-INFO +11 -0
  41. spawn_cli-0.1.1/src/spawn_cli.egg-info/SOURCES.txt +45 -0
  42. spawn_cli-0.1.1/src/spawn_cli.egg-info/dependency_links.txt +1 -0
  43. spawn_cli-0.1.1/src/spawn_cli.egg-info/entry_points.txt +2 -0
  44. spawn_cli-0.1.1/src/spawn_cli.egg-info/requires.txt +8 -0
  45. spawn_cli-0.1.1/src/spawn_cli.egg-info/top_level.txt +1 -0
  46. spawn_cli-0.1.1/tests/test_cli.py +318 -0
  47. spawn_cli-0.1.1/tests/test_warnings_display.py +48 -0
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "0.1.0"
@@ -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)
@@ -0,0 +1,5 @@
1
+ from spawn_cli.core import low_level
2
+ from spawn_cli.core.errors import SpawnError, SpawnWarning
3
+ from spawn_cli.core.low_level import * # noqa: F403
4
+
5
+ __all__ = ["SpawnError", "SpawnWarning", *low_level.__all__]