spawn-cli 0.1.1__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,32 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from ruamel.yaml import YAML
6
+
7
+
8
+ def configure_yaml_dump(yaml: YAML) -> None:
9
+ """Force block-style collections for dumps (avoid flow `{ ... }` / `[ ... ]` on nested nodes)."""
10
+
11
+ yaml.default_flow_style = False
12
+ yaml.width = 4096
13
+ yaml.sort_base_mapping_type_on_output = False
14
+
15
+
16
+ def load_yaml(path: Path) -> dict:
17
+ if not path.is_file():
18
+ return {}
19
+ yaml = YAML(typ="safe")
20
+ with path.open("r", encoding="utf-8") as fh:
21
+ data = yaml.load(fh)
22
+ return data if isinstance(data, dict) else {}
23
+
24
+
25
+ def save_yaml(path: Path, data: dict) -> None:
26
+ from spawn_cli.io.paths import ensure_dir
27
+
28
+ ensure_dir(path.parent)
29
+ yaml = YAML(typ="safe")
30
+ configure_yaml_dump(yaml)
31
+ with path.open("w", encoding="utf-8") as fh:
32
+ yaml.dump(data, fh)
@@ -0,0 +1,63 @@
1
+ from spawn_cli.models.config import (
2
+ CoreConfig,
3
+ ExtensionConfig,
4
+ ExtensionFileEntry,
5
+ ExtensionFolderEntry,
6
+ ExtensionsMeta,
7
+ FileMode,
8
+ IdeList,
9
+ ReadFlag,
10
+ SetupConfig,
11
+ SkillEntry,
12
+ SourceYaml,
13
+ )
14
+ from spawn_cli.models.mcp import (
15
+ McpCapabilities,
16
+ McpEnvVar,
17
+ McpServer,
18
+ McpTransport,
19
+ NormalizedMcp,
20
+ )
21
+ from spawn_cli.models.metadata import (
22
+ RenderedMcpEntry,
23
+ RenderedMcpMeta,
24
+ RenderedSkillEntry,
25
+ RenderedSkillsMeta,
26
+ )
27
+ from spawn_cli.models.navigation import (
28
+ NavExtGroup,
29
+ NavFile,
30
+ NavRulesGroup,
31
+ NavigationFile,
32
+ )
33
+ from spawn_cli.models.skill import SkillFileRef, SkillMetadata, SkillRawInfo
34
+
35
+ __all__ = [
36
+ "CoreConfig",
37
+ "ExtensionConfig",
38
+ "ExtensionFileEntry",
39
+ "ExtensionFolderEntry",
40
+ "ExtensionsMeta",
41
+ "FileMode",
42
+ "IdeList",
43
+ "ReadFlag",
44
+ "SetupConfig",
45
+ "SkillEntry",
46
+ "SourceYaml",
47
+ "McpCapabilities",
48
+ "McpEnvVar",
49
+ "McpServer",
50
+ "McpTransport",
51
+ "NormalizedMcp",
52
+ "RenderedMcpEntry",
53
+ "RenderedMcpMeta",
54
+ "RenderedSkillEntry",
55
+ "RenderedSkillsMeta",
56
+ "NavExtGroup",
57
+ "NavFile",
58
+ "NavRulesGroup",
59
+ "NavigationFile",
60
+ "SkillFileRef",
61
+ "SkillMetadata",
62
+ "SkillRawInfo",
63
+ ]
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Optional
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+
9
+ class FileMode(str, Enum):
10
+ static = "static"
11
+ artifact = "artifact"
12
+
13
+
14
+ class ReadFlag(str, Enum):
15
+ required = "required"
16
+ auto = "auto"
17
+ no = "no"
18
+
19
+
20
+ class ExtensionFileEntry(BaseModel):
21
+ model_config = ConfigDict(populate_by_name=True)
22
+
23
+ description: Optional[str] = None
24
+ mode: FileMode = FileMode.static
25
+ globalRead: ReadFlag = ReadFlag.no
26
+ localRead: ReadFlag = ReadFlag.no
27
+
28
+
29
+ class ExtensionFolderEntry(BaseModel):
30
+ model_config = ConfigDict(populate_by_name=True)
31
+
32
+ mode: FileMode = FileMode.static
33
+
34
+
35
+ class SkillEntry(BaseModel):
36
+ model_config = ConfigDict(populate_by_name=True)
37
+
38
+ name: Optional[str] = None
39
+ description: Optional[str] = None
40
+ required_read: list[str] = Field(default_factory=list, alias="required-read")
41
+
42
+
43
+ class SetupConfig(BaseModel):
44
+ model_config = ConfigDict(populate_by_name=True)
45
+
46
+ before_install: Optional[str] = Field(None, alias="before-install")
47
+ after_install: Optional[str] = Field(None, alias="after-install")
48
+ before_uninstall: Optional[str] = Field(None, alias="before-uninstall")
49
+ after_uninstall: Optional[str] = Field(None, alias="after-uninstall")
50
+ healthcheck: Optional[str] = None
51
+
52
+
53
+ class ExtensionConfig(BaseModel):
54
+ model_config = ConfigDict(populate_by_name=True)
55
+
56
+ name: Optional[str] = None
57
+ version: str
58
+ schema_version: int = Field(1, alias="schema")
59
+ files: dict[str, ExtensionFileEntry] = Field(default_factory=dict)
60
+ folders: dict[str, ExtensionFolderEntry] = Field(default_factory=dict)
61
+ agent_ignore: list[str] = Field(default_factory=list, alias="agent-ignore")
62
+ git_ignore: list[str] = Field(default_factory=list, alias="git-ignore")
63
+ skills: dict[str, SkillEntry] = Field(default_factory=dict)
64
+ setup: Optional[SetupConfig] = None
65
+
66
+
67
+ class CoreConfig(BaseModel):
68
+ model_config = ConfigDict(populate_by_name=True)
69
+
70
+ version: str
71
+ agent_ignore: list[str] = Field(default_factory=list, alias="agent-ignore")
72
+
73
+
74
+ class IdeList(BaseModel):
75
+ model_config = ConfigDict(populate_by_name=True)
76
+
77
+ ides: list[str] = Field(default_factory=list)
78
+
79
+
80
+ class SourceYaml(BaseModel):
81
+ model_config = ConfigDict(populate_by_name=True)
82
+
83
+ class SourceInfo(BaseModel):
84
+ model_config = ConfigDict(populate_by_name=True)
85
+
86
+ type: str
87
+ path: str
88
+ branch: Optional[str] = None
89
+ resolved: Optional[str] = None
90
+
91
+ class InstalledInfo(BaseModel):
92
+ model_config = ConfigDict(populate_by_name=True)
93
+
94
+ version: str
95
+ installedAt: str
96
+
97
+ extension: str
98
+ source: SourceInfo
99
+ installed: InstalledInfo
100
+
101
+
102
+ class ExtensionsMeta(BaseModel):
103
+ model_config = ConfigDict(populate_by_name=True)
104
+
105
+ class ExtEntry(BaseModel):
106
+ model_config = ConfigDict(populate_by_name=True)
107
+
108
+ path: str
109
+ branch: Optional[str] = None
110
+
111
+ extensions: list[ExtEntry] = Field(default_factory=list)
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+
8
+ class McpEnvVar(BaseModel):
9
+ model_config = ConfigDict(populate_by_name=True)
10
+
11
+ source: str = "user"
12
+ required: bool = True
13
+ secret: bool = False
14
+ value: Optional[str] = None
15
+
16
+
17
+ class McpCapabilities(BaseModel):
18
+ model_config = ConfigDict(populate_by_name=True)
19
+
20
+ tools: bool = True
21
+ resources: bool = False
22
+ prompts: bool = False
23
+
24
+
25
+ class McpTransport(BaseModel):
26
+ model_config = ConfigDict(populate_by_name=True)
27
+
28
+ type: str
29
+ command: Optional[str] = None
30
+ args: list[str] = Field(default_factory=list)
31
+ cwd: str = "."
32
+ url: Optional[str] = None
33
+ headers: dict[str, str] = Field(default_factory=dict)
34
+
35
+
36
+ class McpServer(BaseModel):
37
+ model_config = ConfigDict(populate_by_name=True)
38
+
39
+ name: str
40
+ extension: str
41
+ transport: McpTransport
42
+ env: dict[str, McpEnvVar] = Field(default_factory=dict)
43
+ capabilities: McpCapabilities = Field(default_factory=McpCapabilities)
44
+
45
+
46
+ class NormalizedMcp(BaseModel):
47
+ model_config = ConfigDict(populate_by_name=True)
48
+
49
+ servers: list[McpServer] = Field(default_factory=list)
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class RenderedSkillEntry(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True)
8
+
9
+ skill: str
10
+ path: str
11
+
12
+
13
+ class RenderedSkillsMeta(BaseModel):
14
+ model_config = ConfigDict(populate_by_name=True)
15
+
16
+ extensions: dict[str, list[RenderedSkillEntry]] = Field(default_factory=dict)
17
+
18
+
19
+ class RenderedMcpEntry(BaseModel):
20
+ model_config = ConfigDict(populate_by_name=True)
21
+
22
+ name: str
23
+
24
+
25
+ class RenderedMcpMeta(BaseModel):
26
+ model_config = ConfigDict(populate_by_name=True)
27
+
28
+ extensions: dict[str, list[RenderedMcpEntry]] = Field(default_factory=dict)
@@ -0,0 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class NavFile(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True)
8
+
9
+ path: str
10
+ description: str
11
+
12
+
13
+ class NavExtGroup(BaseModel):
14
+ model_config = ConfigDict(populate_by_name=True)
15
+
16
+ ext: str
17
+ files: list[NavFile] = Field(default_factory=list)
18
+
19
+
20
+ class NavRulesGroup(BaseModel):
21
+ model_config = ConfigDict(populate_by_name=True)
22
+
23
+ rules: list[NavFile] = Field(default_factory=list)
24
+
25
+
26
+ class NavigationFile(BaseModel):
27
+ model_config = ConfigDict(populate_by_name=True)
28
+
29
+ read_required: list[dict] = Field(default_factory=list, alias="read-required")
30
+ read_contextual: list[dict] = Field(default_factory=list, alias="read-contextual")
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class SkillFileRef(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True)
8
+
9
+ file: str
10
+ description: str
11
+
12
+
13
+ class SkillRawInfo(BaseModel):
14
+ model_config = ConfigDict(populate_by_name=True)
15
+
16
+ name: str
17
+ description: str
18
+ content: str
19
+ required_read: list[str] = Field(default_factory=list, alias="required-read")
20
+
21
+
22
+ class SkillMetadata(BaseModel):
23
+ model_config = ConfigDict(populate_by_name=True)
24
+
25
+ name: str
26
+ description: str
27
+ content: str
28
+ required_read: list[SkillFileRef] = Field(default_factory=list)
29
+ auto_read: list[SkillFileRef] = Field(default_factory=list)
File without changes
@@ -0,0 +1,5 @@
1
+ version: "0.1.0"
2
+ agent-ignore:
3
+ - spawn/**
4
+ - "!spawn/navigation.yaml"
5
+ - "!spawn/rules/**"
@@ -0,0 +1,35 @@
1
+ """CLI-only formatting for SpawnWarning (see task 9 spec)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import warnings
7
+ from collections.abc import Callable
8
+
9
+ from spawn_cli.errors import SpawnWarning
10
+
11
+ _prev_showwarning: Callable[..., None] | None = None
12
+
13
+
14
+ def install_spawn_warning_format() -> None:
15
+ global _prev_showwarning
16
+ if _prev_showwarning is not None:
17
+ return
18
+ _prev_showwarning = warnings.showwarning
19
+
20
+ def showwarning(message, category, filename, lineno, file=None, line=None) -> None:
21
+ if isinstance(category, type) and issubclass(category, SpawnWarning):
22
+ text = str(message).strip()
23
+ print(f"spawn: warning: {text}", file=sys.stderr)
24
+ return
25
+ if _prev_showwarning is not None:
26
+ _prev_showwarning(message, category, filename, lineno, file=file, line=line)
27
+
28
+ warnings.showwarning = showwarning # type: ignore[method-assign]
29
+
30
+
31
+ def reset_spawn_warning_format() -> None:
32
+ global _prev_showwarning
33
+ if _prev_showwarning is not None:
34
+ warnings.showwarning = _prev_showwarning
35
+ _prev_showwarning = None
@@ -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,41 @@
1
+ spawn_cli/__init__.py,sha256=4t_crzhrLum--oyowUMxtjBTzUtWp7oRTF22ewEvJG4,49
2
+ spawn_cli/cli.py,sha256=_72pDJOLNlqTksvf4MPcdmmp-8u3TFIsnumw5xtHH_c,7543
3
+ spawn_cli/errors.py,sha256=1OJx7Ss529EeJkD0WLvNy0I9sKqxDX2bXS6i3mnqN2E,230
4
+ spawn_cli/warnings_display.py,sha256=v4iTUTd4lziJ1Ivxh8-_zbcDaGZdZLEiCcQXo39Ftss,1116
5
+ spawn_cli/core/__init__.py,sha256=a5rOQzNZ55nbXJPXwcTVws20MqhoEAHQ5_Cay1h2mDU,211
6
+ spawn_cli/core/download.py,sha256=C8s2_sDcuKFJZr5YPyB2AOOX8P8aBXumSgunO_r-InA,19203
7
+ spawn_cli/core/errors.py,sha256=gyFGgD5QEUUi218mBOcSQRJv4191XNdRyLW_9d_EUso,132
8
+ spawn_cli/core/high_level.py,sha256=B9CH0H09Tlxw16_H0OLaarYjijVAr-HUP0uX1jG3-04,17377
9
+ spawn_cli/core/low_level.py,sha256=wVp3eTpB_FhJmCVQpZii3IUDWdVAJ5uZ72-_tlSuiq8,30882
10
+ spawn_cli/core/scripts.py,sha256=sowUZPeSGbrxu_hoI05DYjBhrQK7NAiiaw6ed99JwTc,7591
11
+ spawn_cli/ide/__init__.py,sha256=mtpcKkzqP9ykCZUyHxrafUAtF-8w11tOttt79Wi_WYQ,1592
12
+ spawn_cli/ide/_helpers.py,sha256=aXUMCDBdmuttuVE73s7_m-PmFsaAblamQNpjEt3j7sc,5770
13
+ spawn_cli/ide/_stub.py,sha256=9vBgzS6mLLxzTzbC5kIH4Cw9K94s2OLgMs-buLjKhsg,2513
14
+ spawn_cli/ide/_vacancy.py,sha256=uPxQE35U_wr1lD3Cp3ey4Z-cI56CAu9dEvU8I9Iq62g,7058
15
+ spawn_cli/ide/claude_code.py,sha256=4E15jJWEKqlwIUMzGyKaGoX9QRdi0f9vDyo0fWeVLcw,5784
16
+ spawn_cli/ide/codex.py,sha256=03BOU_kAWH3jBNycEhQKyo06vrxr8aAgB-foUle9r4I,4383
17
+ spawn_cli/ide/cursor.py,sha256=w_n4lC5N7uyqO7__VKQrBulY0SKtXMEeqSXTDn1_HjA,4753
18
+ spawn_cli/ide/gemini_cli.py,sha256=sFN-c5yceJnN-VyW7DEASj_KrGT6SnN_DxfWPQPLcxg,4993
19
+ spawn_cli/ide/github_copilot.py,sha256=COMo8TSUur1wHXhMVSlqjRb7wVjnbqh7YxIrMKIQ57g,6304
20
+ spawn_cli/ide/registry.py,sha256=9SfJRlxsrqo1IIb3xizUWKHMzBXggILE-kKksN-OX1g,4334
21
+ spawn_cli/ide/windsurf.py,sha256=aX9HaiM7IPa6b93q9ouzZhi5tRr2PqI2xblfm3aKwTc,3334
22
+ spawn_cli/io/__init__.py,sha256=x8PLDho22PdDmliprX2QnmxyTMDKR9mD0ro7TEkJ1Mg,550
23
+ spawn_cli/io/json_io.py,sha256=wVmuSM61iP9PgSbd3oFjP_KWXAnmgpZxUamb69q18Wo,552
24
+ spawn_cli/io/lock.py,sha256=YKWxfkEIRivZByvfjO3I40JujCkg8v0g9bS36vBP8FE,632
25
+ spawn_cli/io/paths.py,sha256=Yzcv0sAPlkNhBTjmXoVbmPW8VtbklRcDR8WpJCZ354g,557
26
+ spawn_cli/io/text_io.py,sha256=xWYQ4od3JuYKJjHsHn_ylarU3-EZkJ4KTV8Y5m52RfQ,416
27
+ spawn_cli/io/toml_io.py,sha256=sDV_DkqEJtE-7epehlvwR6XSvVgBXy3vjvNGh7olurk,558
28
+ spawn_cli/io/yaml_io.py,sha256=UtQ3WZ_1c97EOC96cAEJ8yjavSV9MaW25SwoS6uXj2o,846
29
+ spawn_cli/models/__init__.py,sha256=Hb8lW9Sc5Liko3uxFp0Dklhmru4LU8kZV4JWWU0EAnY,1254
30
+ spawn_cli/models/config.py,sha256=t4Dx0-_CwSx4YA6pm4DEsLUDZfPYu6HGza5Ws2xpLaI,3035
31
+ spawn_cli/models/mcp.py,sha256=SV6lZaWUAQsNw2xvu9UB0SB7sOl96y1qnMlWHtFbiEc,1193
32
+ spawn_cli/models/metadata.py,sha256=zxg9gvnj8x_L0YU6Hg9Mb3B2ZHt2qtYwEX0rAj0Tn84,658
33
+ spawn_cli/models/navigation.py,sha256=S25xqUgnq0uVEKXs7UjpCqBQ9PifkeSRogMsLnkXMqs,759
34
+ spawn_cli/models/skill.py,sha256=dnzParuZ0xIZ0PcKmS04WFkuiKti_ml5QGusta9aEJw,701
35
+ spawn_cli/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
+ spawn_cli/resources/default_core_config.yaml,sha256=ecnGtWB3R0eAYEHVkzZWeuh2_wt9EqsFKZf9BurZGUE,95
37
+ spawn_cli-0.1.1.dist-info/METADATA,sha256=iOoIDofwQ9XD0YRoQ_xtc3Z-lDMlJ_2hnvLp0neRh0U,337
38
+ spawn_cli-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
39
+ spawn_cli-0.1.1.dist-info/entry_points.txt,sha256=LPd7krLFNds0otEoDGEJpvDOiubPekGKJvMgcpvQd7M,45
40
+ spawn_cli-0.1.1.dist-info/top_level.txt,sha256=IzK1liuh9tgdhkh6yDvGQvtBDvKYXzSQ86Ty-a50lT8,10
41
+ spawn_cli-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ spawn = spawn_cli.cli:main
@@ -0,0 +1 @@
1
+ spawn_cli