spawn-cli 0.1.6__tar.gz → 0.1.7__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 (49) hide show
  1. {spawn_cli-0.1.6/src/spawn_cli.egg-info → spawn_cli-0.1.7}/PKG-INFO +1 -1
  2. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/pyproject.toml +1 -1
  3. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/cli.py +12 -0
  4. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/high_level.py +31 -0
  5. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/low_level.py +43 -3
  6. {spawn_cli-0.1.6 → spawn_cli-0.1.7/src/spawn_cli.egg-info}/PKG-INFO +1 -1
  7. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/tests/test_cli.py +22 -0
  8. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/LICENSE +0 -0
  9. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/README.md +0 -0
  10. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/setup.cfg +0 -0
  11. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/__init__.py +0 -0
  12. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/__init__.py +0 -0
  13. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/download.py +0 -0
  14. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/errors.py +0 -0
  15. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/core/scripts.py +0 -0
  16. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/errors.py +0 -0
  17. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/__init__.py +0 -0
  18. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/_helpers.py +0 -0
  19. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/_stub.py +0 -0
  20. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/_vacancy.py +0 -0
  21. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/claude_code.py +0 -0
  22. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/codex.py +0 -0
  23. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/cursor.py +0 -0
  24. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/gemini_cli.py +0 -0
  25. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/github_copilot.py +0 -0
  26. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/registry.py +0 -0
  27. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/ide/windsurf.py +0 -0
  28. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/__init__.py +0 -0
  29. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/json_io.py +0 -0
  30. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/lock.py +0 -0
  31. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/paths.py +0 -0
  32. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/text_io.py +0 -0
  33. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/toml_io.py +0 -0
  34. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/io/yaml_io.py +0 -0
  35. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/__init__.py +0 -0
  36. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/config.py +0 -0
  37. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/mcp.py +0 -0
  38. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/metadata.py +0 -0
  39. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/navigation.py +0 -0
  40. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/models/skill.py +0 -0
  41. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/resources/__init__.py +0 -0
  42. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/resources/default_core_config.yaml +0 -0
  43. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli/warnings_display.py +0 -0
  44. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli.egg-info/SOURCES.txt +0 -0
  45. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli.egg-info/dependency_links.txt +0 -0
  46. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli.egg-info/entry_points.txt +0 -0
  47. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli.egg-info/requires.txt +0 -0
  48. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/src/spawn_cli.egg-info/top_level.txt +0 -0
  49. {spawn_cli-0.1.6 → spawn_cli-0.1.7}/tests/test_warnings_display.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spawn-cli
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: A minimal installable Python CLI package.
5
5
  License: MIT
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spawn-cli"
7
- version = "0.1.6"
7
+ version = "0.1.7"
8
8
  description = "A minimal installable Python CLI package."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -40,6 +40,15 @@ def build_parser() -> argparse.ArgumentParser:
40
40
  ),
41
41
  )
42
42
 
43
+ sub.add_parser(
44
+ "refresh",
45
+ help="Sync core config from defaults and rebuild IDE renders",
46
+ description=(
47
+ "Merge bundled defaults into spawn/.core/config.yaml, then refresh skills, MCP, "
48
+ "agent ignore, navigation, gitignore metadata, and IDE entry points."
49
+ ),
50
+ )
51
+
43
52
  rules_p = sub.add_parser(
44
53
  "rules",
45
54
  help="Rebuild navigation mandatory reads from spawn/rules/",
@@ -321,6 +330,9 @@ def _dispatch(args: argparse.Namespace, target_root: Path) -> int:
321
330
  with spawn_lock(target_root):
322
331
  if cmd == "rules":
323
332
  return _dispatch_rules(args, target_root)
333
+ if cmd == "refresh":
334
+ hl.refresh_repository(target_root)
335
+ return 0
324
336
  if cmd == "ide":
325
337
  return _dispatch_ide(args, target_root)
326
338
  if cmd == "extension":
@@ -222,6 +222,36 @@ def refresh_rules_navigation(target_root: Path) -> None:
222
222
  ll.save_rules_navigation(target_root)
223
223
 
224
224
 
225
+ def refresh_repository(target_root: Path) -> None:
226
+ """Sync core config from package defaults and rebuild all IDE-facing metadata."""
227
+ _require_init(target_root)
228
+ ll.merge_core_config_from_defaults(target_root)
229
+ needs_skill = _any_extension_has_skill_files(target_root)
230
+ needs_mcp = _any_extension_has_mcp_servers(target_root)
231
+ for ide in ll.list_ides(target_root):
232
+ _warn_capability_gaps(
233
+ ide,
234
+ ide_get(ide).detect(target_root).capabilities,
235
+ needs_skill_render=needs_skill,
236
+ needs_mcp_merge=needs_mcp,
237
+ )
238
+ merged_any = False
239
+ for ide in ll.list_ides(target_root):
240
+ for ext in ll.list_extensions(target_root):
241
+ names = refresh_mcp(target_root, ide, ext, emit_mcp_merged_notice=False)
242
+ if names:
243
+ merged_any = True
244
+ _refresh_skills_all_extensions_for_ide(target_root, ide)
245
+ for ide in ll.list_ides(target_root):
246
+ refresh_agent_ignore(target_root, ide)
247
+ if merged_any:
248
+ print(MCP_MERGED_NOTICE)
249
+ refresh_gitignore(target_root)
250
+ refresh_navigation(target_root)
251
+ for ide in ll.list_ides(target_root):
252
+ refresh_entry_point(target_root, ide)
253
+
254
+
225
255
  def _refresh_extension_core(target_root: Path, extension: str) -> None:
226
256
  needs_skill = _any_extension_has_skill_files(target_root)
227
257
  needs_mcp = bool(ll.list_mcp(target_root, extension).servers)
@@ -536,6 +566,7 @@ __all__ = [
536
566
  "refresh_gitignore",
537
567
  "refresh_mcp",
538
568
  "refresh_navigation",
569
+ "refresh_repository",
539
570
  "refresh_rules_navigation",
540
571
  "refresh_skills",
541
572
  "reinstall_extension",
@@ -18,7 +18,7 @@ from spawn_cli.io.json_io import load_json
18
18
  from spawn_cli.io.paths import ensure_dir
19
19
  from spawn_cli.io.text_io import read_lines, write_lines
20
20
  from spawn_cli.io.yaml_io import configure_yaml_dump, load_yaml, save_yaml
21
- from spawn_cli.models.config import ExtensionConfig, FileMode, IdeList, ReadFlag
21
+ from spawn_cli.models.config import CoreConfig, ExtensionConfig, FileMode, IdeList, ReadFlag
22
22
  from spawn_cli.models.mcp import McpCapabilities, McpEnvVar, McpServer, McpTransport, NormalizedMcp
23
23
  from spawn_cli.models.skill import SkillFileRef, SkillMetadata, SkillRawInfo
24
24
 
@@ -451,8 +451,6 @@ def get_navigation_metadata(target_root: Path, extension: str) -> dict:
451
451
 
452
452
  def get_core_agent_ignore(target_root: Path) -> list[str]:
453
453
  path = _spawn(target_root) / ".core" / "config.yaml"
454
- from spawn_cli.models.config import CoreConfig
455
-
456
454
  data = load_yaml(path)
457
455
  if not data:
458
456
  return []
@@ -460,6 +458,47 @@ def get_core_agent_ignore(target_root: Path) -> list[str]:
460
458
  return list(core.agent_ignore)
461
459
 
462
460
 
461
+ def merge_core_config_from_defaults(target_root: Path) -> None:
462
+ """Merge bundled default core config into ``spawn/.core/config.yaml``.
463
+
464
+ ``version`` becomes the bundled default. ``agent-ignore`` is the bundled
465
+ list (in resource order) followed by any existing patterns not present in
466
+ the default set.
467
+ """
468
+ path = _spawn(target_root) / ".core" / "config.yaml"
469
+ if not path.is_file():
470
+ raise SpawnError("spawn/.core/config.yaml is missing; run spawn init.")
471
+ bundled_txt = (
472
+ resources.files("spawn_cli.resources")
473
+ .joinpath("default_core_config.yaml")
474
+ .read_text(encoding="utf-8")
475
+ )
476
+ yparse = YAML(typ="safe")
477
+ raw_default = yparse.load(bundled_txt)
478
+ if not isinstance(raw_default, dict):
479
+ raise SpawnError("internal error: bundled default core config is not a mapping")
480
+ try:
481
+ default_core = CoreConfig.model_validate(raw_default)
482
+ except Exception as e:
483
+ raise SpawnError(f"internal error: bundled default core config invalid: {e}") from e
484
+ raw_existing = load_yaml(path)
485
+ if not raw_existing:
486
+ raise SpawnError(
487
+ "spawn/.core/config.yaml is empty or invalid; restore it or run spawn init again."
488
+ )
489
+ try:
490
+ current = CoreConfig.model_validate(raw_existing)
491
+ except Exception as e:
492
+ raise SpawnError(f"invalid spawn/.core/config.yaml: {e}") from e
493
+ default_set = set(default_core.agent_ignore)
494
+ merged_ignore = list(default_core.agent_ignore)
495
+ for g in current.agent_ignore:
496
+ if g not in default_set:
497
+ merged_ignore.append(g)
498
+ out = CoreConfig(version=default_core.version, agent_ignore=merged_ignore)
499
+ save_yaml(path, out.model_dump(by_alias=True))
500
+
501
+
463
502
  def get_ext_agent_ignore(target_root: Path, extension: str) -> list[str]:
464
503
  cfg = _load_ext_config(target_root, extension)
465
504
  return list(cfg.agent_ignore)
@@ -890,6 +929,7 @@ __all__ = [
890
929
  "list_ides",
891
930
  "list_mcp",
892
931
  "list_skills",
932
+ "merge_core_config_from_defaults",
893
933
  "METADATA_TEMP_MAX_AGE_SECONDS",
894
934
  "normalize_skill_name",
895
935
  "prune_metadata_temp",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spawn-cli
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: A minimal installable Python CLI package.
5
5
  License: MIT
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -88,6 +88,28 @@ def test_spawn_rules_refresh(tmp_path, monkeypatch):
88
88
  mock_refresh.assert_called_once_with(root)
89
89
 
90
90
 
91
+ def test_spawn_refresh(tmp_path, monkeypatch):
92
+ monkeypatch.chdir(tmp_path)
93
+ from spawn_cli.core import low_level as ll
94
+
95
+ ll.init(tmp_path)
96
+ root = tmp_path.resolve()
97
+ mock_repo = MagicMock()
98
+
99
+ with patch("spawn_cli.cli.spawn_lock", _noop_lock), patch(
100
+ "spawn_cli.cli.hl.refresh_repository", mock_repo
101
+ ):
102
+ assert main(["refresh"]) == 0
103
+
104
+ mock_repo.assert_called_once_with(root)
105
+
106
+
107
+ def test_spawn_refresh_need_init(tmp_path, monkeypatch, capsys):
108
+ monkeypatch.chdir(tmp_path)
109
+ assert main(["refresh"]) == 1
110
+ assert "need init before" in capsys.readouterr().err
111
+
112
+
91
113
  def test_spawn_ide_list_supported_ides(tmp_path, monkeypatch, capsys):
92
114
  monkeypatch.chdir(tmp_path)
93
115
  (tmp_path / "spawn").mkdir()
File without changes
File without changes
File without changes