lazyopencode 0.2.2__tar.gz → 0.2.3__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 (128) hide show
  1. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/AGENTS.md +2 -0
  2. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/PKG-INFO +1 -1
  3. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/_version.py +2 -2
  4. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/discovery.py +43 -1
  5. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/conftest.py +30 -0
  6. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_skills.py +60 -0
  7. lazyopencode-0.2.3/tests/integration/fixtures/agents-skill/agents-compat-skill/SKILL.md +7 -0
  8. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.github/release-drafter.yml +0 -0
  9. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.github/workflows/ci.yml +0 -0
  10. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.github/workflows/publish.yml +0 -0
  11. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.github/workflows/release-drafter.yml +0 -0
  12. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.gitignore +0 -0
  13. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.opencode/command/run-quality-gates.md +0 -0
  14. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.opencode/skill/quality-gates/SKILL.md +0 -0
  15. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/.pre-commit-config.yaml +0 -0
  16. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/LICENSE +0 -0
  17. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/README.md +0 -0
  18. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/artifacts/demo.png +0 -0
  19. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/artifacts/lazyclaude-reference.png +0 -0
  20. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/opencode.json +0 -0
  21. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/pyproject.toml +0 -0
  22. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/scripts/check_quality.sh +0 -0
  23. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/__init__.py +0 -0
  24. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/__main__.py +0 -0
  25. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/app.py +0 -0
  26. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/bindings.py +0 -0
  27. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/mixins/filtering.py +0 -0
  28. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/mixins/help.py +0 -0
  29. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/mixins/navigation.py +0 -0
  30. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/models/__init__.py +0 -0
  31. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/models/customization.py +0 -0
  32. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/__init__.py +0 -0
  33. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/__init__.py +0 -0
  34. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/discovery.py +0 -0
  35. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/parsers/__init__.py +0 -0
  36. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/parsers/agent.py +0 -0
  37. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/parsers/command.py +0 -0
  38. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/parsers/skill.py +0 -0
  39. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/claude_code/plugin_loader.py +0 -0
  40. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/gitignore_filter.py +0 -0
  41. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/__init__.py +0 -0
  42. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/agent.py +0 -0
  43. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/command.py +0 -0
  44. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/mcp.py +0 -0
  45. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/plugin.py +0 -0
  46. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/rules.py +0 -0
  47. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/skill.py +0 -0
  48. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/parsers/tool.py +0 -0
  49. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/services/writer.py +0 -0
  50. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/styles/app.tcss +0 -0
  51. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/themes.py +0 -0
  52. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/__init__.py +0 -0
  53. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/app_footer.py +0 -0
  54. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/combined_panel.py +0 -0
  55. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/delete_confirm.py +0 -0
  56. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/detail_pane.py +0 -0
  57. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/filter_input.py +0 -0
  58. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/helpers/__init__.py +0 -0
  59. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/helpers/rendering.py +0 -0
  60. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/level_selector.py +0 -0
  61. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/status_panel.py +0 -0
  62. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/src/lazyopencode/widgets/type_panel.py +0 -0
  63. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/AGENTS.md +0 -0
  64. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/__init__.py +0 -0
  65. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/__init__.py +0 -0
  66. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_agents.py +0 -0
  67. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_commands.py +0 -0
  68. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_full_discovery.py +0 -0
  69. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_mcps.py +0 -0
  70. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/discovery/test_rules.py +0 -0
  71. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/agent/explorer.md +0 -0
  72. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/command/greet.md +0 -0
  73. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/mcp/project-opencode.json +0 -0
  74. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/mcp/user-opencode.json +0 -0
  75. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/memory/AGENTS.md +0 -0
  76. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/AGENTS.md +0 -0
  77. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/agent/reviewer.md +0 -0
  78. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/command/project-cmd.md +0 -0
  79. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/docs/guidelines.md +0 -0
  80. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/skill/project-skill/SKILL.md +0 -0
  81. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/project/skill/project-skill/src/helper.py +0 -0
  82. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/skill/task-tracker/SKILL.md +0 -0
  83. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/skill/task-tracker/reference.md +0 -0
  84. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/integration/fixtures/skill/task-tracker/scripts/run.sh +0 -0
  85. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/SPEC.md +0 -0
  86. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/__init__.py +0 -0
  87. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/conftest.py +0 -0
  88. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/agent/reviewer.md +0 -0
  89. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/command/verify.md +0 -0
  90. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/plugin/metrics.ts +0 -0
  91. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/skill/deploy-helper/SKILL.md +0 -0
  92. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/skill/deploy-helper/scripts/deploy.sh +0 -0
  93. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/.opencode/tool/search.ts +0 -0
  94. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/AGENTS.md +0 -0
  95. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/README.md +0 -0
  96. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/docs/guidelines.md +0 -0
  97. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/opencode.json +0 -0
  98. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/complex/prompts/auditor.txt +0 -0
  99. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_commands/.opencode/command/deploy.md +0 -0
  100. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_commands/.opencode/command/greet.md +0 -0
  101. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_commands/README.md +0 -0
  102. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_references/README.md +0 -0
  103. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_references/opencode.json +0 -0
  104. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_references/prompts/agent.txt +0 -0
  105. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/file_references/templates/cmd.txt +0 -0
  106. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/inline_commands/README.md +0 -0
  107. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/inline_commands/opencode.json +0 -0
  108. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/minimal/AGENTS.md +0 -0
  109. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/minimal/README.md +0 -0
  110. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/mixed_config/.opencode/agent/file-agent.md +0 -0
  111. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/mixed_config/.opencode/command/file-cmd.md +0 -0
  112. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/mixed_config/AGENTS.md +0 -0
  113. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/mixed_config/README.md +0 -0
  114. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/mixed_config/opencode.json +0 -0
  115. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/rich_opencode_json/README.md +0 -0
  116. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/rich_opencode_json/docs/api-standards.md +0 -0
  117. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/rich_opencode_json/opencode.json +0 -0
  118. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/rich_opencode_json/prompts/security-audit.txt +0 -0
  119. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/rich_opencode_json/scripts/fake-lint.sh +0 -0
  120. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/SKILL.md +0 -0
  121. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/docs/guide.md +0 -0
  122. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/skills_with_tree/.opencode/skill/my-skill/scripts/run.sh +0 -0
  123. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/scenarios/skills_with_tree/README.md +0 -0
  124. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/spec/test_scenarios.py +0 -0
  125. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/test_inline.py +0 -0
  126. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/test_version.py +0 -0
  127. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/tests/unit/__init__.py +0 -0
  128. {lazyopencode-0.2.2 → lazyopencode-0.2.3}/uv.lock +0 -0
@@ -85,6 +85,8 @@ The application discovers customizations from these locations:
85
85
  | Commands | `~/.config/opencode/command/*.md` | `.opencode/command/*.md` |
86
86
  | Agents | `~/.config/opencode/agent/*.md` | `.opencode/agent/*.md` |
87
87
  | Skills | `~/.config/opencode/skill/*/SKILL.md` | `.opencode/skill/*/SKILL.md` |
88
+ | Skills | `~/.agents/skill[s]/*/SKILL.md` | `.agents/skill[s]/*/SKILL.md` |
89
+ | Skills | (via Claude Code) `~/.claude/skills/*/SKILL.md` | `.claude/skill[s]/*/SKILL.md` |
88
90
  | Rules | `~/.config/opencode/AGENTS.md` | `AGENTS.md` |
89
91
  | MCPs | `~/.config/opencode/opencode.json` | `opencode.json` |
90
92
  | Tools | `~/.config/opencode/tool/*.ts` | `.opencode/tool/*.ts` |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lazyopencode
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A lazygit-style TUI for visualizing OpenCode customizations
5
5
  Project-URL: Homepage, https://github.com/nikiforovall/lazyopencode
6
6
  Project-URL: Repository, https://github.com/nikiforovall/lazyopencode
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.2'
32
- __version_tuple__ = version_tuple = (0, 2, 2)
31
+ __version__ = version = '0.2.3'
32
+ __version_tuple__ = version_tuple = (0, 2, 3)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -218,7 +218,12 @@ class ConfigDiscoveryService:
218
218
  def _discover_skills(
219
219
  self, base_path: Path, level: ConfigLevel
220
220
  ) -> list[Customization]:
221
- """Discover skill customizations from .opencode/skill/ or skills/."""
221
+ """Discover skill customizations from .opencode/skill/ or skills/.
222
+
223
+ Also discovers from agent-compatible paths (.agents/skill[s]/)
224
+ and Claude-compatible paths (.claude/skill[s]/) per OpenCode docs:
225
+ https://opencode.ai/docs/skills/#place-files
226
+ """
222
227
  customizations = []
223
228
  parser = self._parsers[CustomizationType.SKILL]
224
229
 
@@ -241,8 +246,45 @@ class ConfigDiscoveryService:
241
246
  if parser.can_parse(skill_file):
242
247
  customizations.append(parser.parse(skill_file, level))
243
248
 
249
+ # Discover from agent-compatible paths (.agents/skill[s]/)
250
+ self._discover_skills_from_compat_roots(
251
+ customizations, parser, level, ".agents"
252
+ )
253
+
244
254
  return customizations
245
255
 
256
+ def _discover_skills_from_compat_roots(
257
+ self,
258
+ customizations: list[Customization],
259
+ parser: object,
260
+ level: ConfigLevel,
261
+ compat_dir: str,
262
+ ) -> None:
263
+ """Discover skills from a compatibility root directory.
264
+
265
+ At PROJECT level: scans <project_root>/<compat_dir>/skill[s]/*/SKILL.md
266
+ At GLOBAL level: scans ~/<compat_dir>/skill[s]/*/SKILL.md
267
+
268
+ Args:
269
+ customizations: List to append discovered skills to
270
+ parser: Skill parser instance
271
+ level: Configuration level (GLOBAL or PROJECT)
272
+ compat_dir: Compatibility directory name (e.g., ".agents")
273
+ """
274
+ if level == ConfigLevel.PROJECT:
275
+ compat_base = self.project_root / compat_dir
276
+ else:
277
+ compat_base = Path.home() / compat_dir
278
+
279
+ for skills_path in self._get_path_variants(compat_base, "skill"):
280
+ for skill_dir in skills_path.iterdir():
281
+ if skill_dir.is_dir():
282
+ skill_file = skill_dir / "SKILL.md"
283
+ if parser.can_parse(skill_file): # type: ignore[attr-defined]
284
+ customizations.append(
285
+ parser.parse(skill_file, level) # type: ignore[attr-defined]
286
+ )
287
+
246
288
  def _discover_rules(self, level: ConfigLevel) -> list[Customization]:
247
289
  """Discover AGENTS.md rules files."""
248
290
  customizations = []
@@ -146,6 +146,36 @@ def project_config_path(fake_project_root: Path, fs: FakeFilesystem) -> Path:
146
146
  return project_opencode
147
147
 
148
148
 
149
+ @pytest.fixture
150
+ def agents_project_skills(fake_project_root: Path, fs: FakeFilesystem) -> Path:
151
+ """Create .agents/skills/ directory with fixture skill at project level."""
152
+ agents_skills = fake_project_root / ".agents" / "skills"
153
+ fs.create_dir(agents_skills)
154
+
155
+ fs.add_real_directory(
156
+ FIXTURES_DIR / "agents-skill" / "agents-compat-skill",
157
+ target_path=agents_skills / "agents-compat-skill",
158
+ read_only=False,
159
+ )
160
+
161
+ return agents_skills
162
+
163
+
164
+ @pytest.fixture
165
+ def agents_global_skills(fake_home: Path, fs: FakeFilesystem) -> Path:
166
+ """Create ~/.agents/skills/ directory with fixture skill at global level."""
167
+ agents_skills = fake_home / ".agents" / "skills"
168
+ fs.create_dir(agents_skills)
169
+
170
+ fs.add_real_directory(
171
+ FIXTURES_DIR / "agents-skill" / "agents-compat-skill",
172
+ target_path=agents_skills / "agents-compat-skill",
173
+ read_only=False,
174
+ )
175
+
176
+ return agents_skills
177
+
178
+
149
179
  @pytest.fixture
150
180
  def full_user_config(
151
181
  user_config_path: Path,
@@ -146,6 +146,66 @@ class TestSkillDiscovery:
146
146
  assert "reference.md" in file_names
147
147
  assert "scripts" in file_names
148
148
 
149
+ def test_discovers_project_agents_compat_skills(
150
+ self,
151
+ agents_project_skills: Path, # noqa: ARG002
152
+ fake_project_root: Path,
153
+ fake_home: Path,
154
+ ) -> None:
155
+ """Test discovering skills from .agents/skills/ at project level."""
156
+ service = ConfigDiscoveryService(
157
+ project_root=fake_project_root,
158
+ global_config_path=fake_home / ".config" / "opencode",
159
+ )
160
+
161
+ skills = service.by_type(CustomizationType.SKILL)
162
+ project_skills = [s for s in skills if s.level == ConfigLevel.PROJECT]
163
+
164
+ assert len(project_skills) == 1
165
+ assert project_skills[0].name == "agents-compat-skill"
166
+ assert project_skills[0].description == "Skill discovered from .agents/ path"
167
+
168
+ def test_discovers_global_agents_compat_skills(
169
+ self,
170
+ agents_global_skills: Path, # noqa: ARG002
171
+ fake_project_root: Path,
172
+ fake_home: Path,
173
+ ) -> None:
174
+ """Test discovering skills from ~/.agents/skills/ at global level."""
175
+ service = ConfigDiscoveryService(
176
+ project_root=fake_project_root,
177
+ global_config_path=fake_home / ".config" / "opencode",
178
+ )
179
+
180
+ skills = service.by_type(CustomizationType.SKILL)
181
+ global_skills = [s for s in skills if s.level == ConfigLevel.GLOBAL]
182
+
183
+ assert len(global_skills) == 1
184
+ assert global_skills[0].name == "agents-compat-skill"
185
+
186
+ def test_agents_compat_skills_deduplicated(
187
+ self,
188
+ project_config_path: Path, # noqa: ARG002
189
+ agents_project_skills: Path, # noqa: ARG002
190
+ fake_project_root: Path,
191
+ fake_home: Path,
192
+ ) -> None:
193
+ """Test that same skill in .opencode/ and .agents/ is not duplicated."""
194
+ # The agents-compat-skill is only in .agents/, project-skill is only in .opencode/
195
+ # So total project skills should be 2, not 3
196
+ service = ConfigDiscoveryService(
197
+ project_root=fake_project_root,
198
+ global_config_path=fake_home / ".config" / "opencode",
199
+ )
200
+
201
+ skills = service.by_type(CustomizationType.SKILL)
202
+ project_skills = [s for s in skills if s.level == ConfigLevel.PROJECT]
203
+
204
+ names = [s.name for s in project_skills]
205
+ assert "project-skill" in names
206
+ assert "agents-compat-skill" in names
207
+ assert len(project_skills) == 2
208
+
149
209
  def test_skill_nested_directory_structure(
150
210
  self,
151
211
  project_config_path: Path, # noqa: ARG002
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: agents-compat-skill
3
+ description: Skill discovered from .agents/ path
4
+ ---
5
+ # Agents-Compatible Skill
6
+
7
+ This skill is discovered from the .agents/skills/ directory.
File without changes
File without changes
File without changes
File without changes
File without changes