code-agnostic 0.3.3__tar.gz → 0.3.5__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 (156) hide show
  1. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/PKG-INFO +60 -17
  2. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/README.md +59 -16
  3. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/__init__.py +1 -1
  4. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/__main__.py +3 -1
  5. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/opencode.py +36 -12
  6. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/app_id.py +1 -1
  7. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/apps_service.py +3 -0
  8. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/codex/config_repository.py +2 -1
  9. code_agnostic-0.3.5/code_agnostic/apps/codex/schema.json +5188 -0
  10. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/codex/service.py +15 -2
  11. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/compiled_planning.py +19 -3
  12. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/interfaces/service.py +24 -1
  13. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/utils.py +46 -0
  14. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/service.py +2 -0
  15. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/opencode/config_repository.py +5 -3
  16. code_agnostic-0.3.5/code_agnostic/apps/opencode/schema.json +1252 -0
  17. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/opencode/service.py +42 -6
  18. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/skills.py +23 -2
  19. code_agnostic-0.3.5/code_agnostic/cli/commands/status.py +64 -0
  20. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/helpers.py +6 -7
  21. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/options.py +0 -12
  22. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/constants.py +7 -0
  23. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/core/repository.py +1 -1
  24. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/executor.py +71 -23
  25. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/git_exclude_service.py +11 -2
  26. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/planner.py +151 -100
  27. code_agnostic-0.3.5/code_agnostic/skills/compilers.py +75 -0
  28. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/skills/models.py +2 -0
  29. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/skills/parser.py +11 -0
  30. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/loaders.py +11 -0
  31. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/status.py +4 -2
  32. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/renderers.py +55 -8
  33. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/utils.py +14 -0
  34. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/PKG-INFO +60 -17
  35. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/SOURCES.txt +1 -0
  36. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/pyproject.toml +1 -1
  37. code_agnostic-0.3.5/tests/test_cli_apply_apps.py +809 -0
  38. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_apply_target.py +51 -0
  39. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_import.py +1 -1
  40. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_plan.py +19 -0
  41. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_skills.py +7 -1
  42. code_agnostic-0.3.5/tests/test_cli_status.py +155 -0
  43. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_workspaces.py +10 -4
  44. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_common_mcp_to_dto.py +64 -1
  45. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_common_repository.py +4 -5
  46. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_git_exclude_service.py +10 -3
  47. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_planner_executor.py +101 -12
  48. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_transactional_executor.py +325 -0
  49. code_agnostic-0.3.5/tests/test_version.py +11 -0
  50. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_workspace_config_sync.py +306 -32
  51. code_agnostic-0.3.3/code_agnostic/apps/codex/schema.json +0 -97
  52. code_agnostic-0.3.3/code_agnostic/apps/opencode/schema.json +0 -7675
  53. code_agnostic-0.3.3/code_agnostic/cli/commands/status.py +0 -52
  54. code_agnostic-0.3.3/code_agnostic/skills/compilers.py +0 -39
  55. code_agnostic-0.3.3/tests/test_cli_apply_apps.py +0 -405
  56. code_agnostic-0.3.3/tests/test_cli_status.py +0 -64
  57. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/LICENSE +0 -0
  58. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/__init__.py +0 -0
  59. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/codex.py +0 -0
  60. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/compilers.py +0 -0
  61. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/models.py +0 -0
  62. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/agents/parser.py +0 -0
  63. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/__init__.py +0 -0
  64. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/codex/__init__.py +0 -0
  65. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/codex/mapper.py +0 -0
  66. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/codex/schema_repository.py +0 -0
  67. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/__init__.py +0 -0
  68. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/framework.py +0 -0
  69. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
  70. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
  71. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/interfaces/repositories.py +0 -0
  72. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/loader.py +0 -0
  73. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/models.py +0 -0
  74. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/schema.py +0 -0
  75. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/common/symlink_planning.py +0 -0
  76. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/__init__.py +0 -0
  77. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/config_repository.py +0 -0
  78. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/mapper.py +0 -0
  79. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/schema.json +0 -0
  80. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/cursor/schema_repository.py +0 -0
  81. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/opencode/__init__.py +0 -0
  82. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/opencode/mapper.py +0 -0
  83. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/apps/opencode/schema_repository.py +0 -0
  84. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/__init__.py +0 -0
  85. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/aliases.py +0 -0
  86. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/__init__.py +0 -0
  87. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/agents.py +0 -0
  88. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/apply.py +0 -0
  89. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/apps.py +0 -0
  90. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
  91. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/import_.py +0 -0
  92. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/mcp.py +0 -0
  93. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/plan.py +0 -0
  94. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/restore.py +0 -0
  95. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/rules.py +0 -0
  96. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/validate.py +0 -0
  97. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/cli/commands/workspaces.py +0 -0
  98. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/core/__init__.py +0 -0
  99. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/core/workspace_repository.py +0 -0
  100. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/errors.py +0 -0
  101. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/imports/__init__.py +0 -0
  102. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/imports/adapters.py +0 -0
  103. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/imports/filesystem.py +0 -0
  104. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/imports/models.py +0 -0
  105. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/imports/service.py +0 -0
  106. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/lossiness.py +0 -0
  107. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/mcp_service.py +0 -0
  108. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/models.py +0 -0
  109. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/rules/__init__.py +0 -0
  110. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/rules/compilers.py +0 -0
  111. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/rules/models.py +0 -0
  112. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/rules/parser.py +0 -0
  113. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/rules/repository.py +0 -0
  114. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/skills/__init__.py +0 -0
  115. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/__init__.py +0 -0
  116. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/schemas/agent.v1.schema.json +0 -0
  117. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
  118. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
  119. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/schemas/rule.v1.schema.json +0 -0
  120. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/spec/schemas/skill.v1.schema.json +0 -0
  121. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/__init__.py +0 -0
  122. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/enums.py +0 -0
  123. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/import_selector.py +0 -0
  124. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/sections.py +0 -0
  125. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/tui/tables.py +0 -0
  126. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/validation.py +0 -0
  127. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic/workspaces.py +0 -0
  128. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/dependency_links.txt +0 -0
  129. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/entry_points.txt +0 -0
  130. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/requires.txt +0 -0
  131. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/code_agnostic.egg-info/top_level.txt +0 -0
  132. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/setup.cfg +0 -0
  133. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_agents.py +0 -0
  134. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_aliases.py +0 -0
  135. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_apply_codex.py +0 -0
  136. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_apply_cursor.py +0 -0
  137. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_apps.py +0 -0
  138. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_explain_lossiness.py +0 -0
  139. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_flags.py +0 -0
  140. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_git_exclude.py +0 -0
  141. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_import_interactive.py +0 -0
  142. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_mcp.py +0 -0
  143. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_module_organization.py +0 -0
  144. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_restore.py +0 -0
  145. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_rules.py +0 -0
  146. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_validate.py +0 -0
  147. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_cli_workspace_resolution.py +0 -0
  148. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_compiled_planning.py +0 -0
  149. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_dto_to_common_mcp.py +0 -0
  150. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_mcp_service.py +0 -0
  151. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_planner_rules.py +0 -0
  152. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_symlink_planning.py +0 -0
  153. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_sync_plan_model.py +0 -0
  154. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_utils.py +0 -0
  155. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_workspace_repo_status.py +0 -0
  156. {code_agnostic-0.3.3 → code_agnostic-0.3.5}/tests/test_workspaces.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-agnostic
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: Centralized hub for LLM coding config: MCP, skills, rules, and agents.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -36,11 +36,17 @@ AI coding tools each want config in a different place and format. When you use m
36
36
  ├── config/
37
37
  │ └── mcp.base.json MCP servers (editor-agnostic)
38
38
  ├── rules/
39
- │ └── python-style.md Rules with YAML frontmatter
39
+ │ └── python-style/
40
+ │ ├── meta.yaml Rule metadata
41
+ │ └── prompt.md Rule instructions
40
42
  ├── skills/
41
- │ └── code-reviewer/SKILL.md Skills with YAML frontmatter
43
+ │ └── code-reviewer/
44
+ │ ├── meta.yaml Skill metadata
45
+ │ └── prompt.md Skill instructions
42
46
  └── agents/
43
- └── architect.md Agents with YAML frontmatter
47
+ └── architect/
48
+ ├── meta.yaml Agent metadata
49
+ └── prompt.md Agent instructions
44
50
 
45
51
  ↓ plan / apply ↓
46
52
 
@@ -51,6 +57,8 @@ AI coding tools each want config in a different place and format. When you use m
51
57
 
52
58
  Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, etc.
53
59
 
60
+ Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are still supported for migration, but bundle directories are the preferred source format for new config.
61
+
54
62
  Today the implementation is still mixed: some assets are compiled and some are symlinked. The active migration plan is to move to generated outputs everywhere with a strict compiler contract instead of implicit per-app behavior.
55
63
 
56
64
  ## Install
@@ -88,6 +96,7 @@ code-agnostic apps enable -a cursor
88
96
  code-agnostic apps enable -a opencode
89
97
 
90
98
  # Preview and apply
99
+ code-agnostic validate
91
100
  code-agnostic plan
92
101
  code-agnostic apply
93
102
  ```
@@ -102,17 +111,17 @@ code-agnostic apply
102
111
  | Agents sync | yes | yes | yes |
103
112
  | Workspace root `AGENTS.md` link | yes | yes | yes |
104
113
  | Native repo config include for workspace `AGENTS.md` | yes | -- | -- |
105
- | Repo/subdir gets shared workspace `AGENTS.md` today | yes | -- | -- |
106
- | Root-level `AGENTS.md` discovery only | -- | yes | yes |
114
+ | Repo/subdir gets shared workspace `AGENTS.md` today | yes | -- | yes |
115
+ | Nested `AGENTS.md` discovery | -- | yes | yes |
107
116
  | Workspace propagation | yes | -- | yes |
108
117
  | Import from | yes | yes | yes |
109
118
  | Interactive import (TUI) | yes | yes | yes |
110
119
 
111
120
  Cursor workspace propagation is intentionally disabled to avoid duplicate MCP initialization in multi-root workspaces: https://forum.cursor.com/t/mcp-multi-root-workspace-causes-duplicate-mcp-server-initialization-4x-createclient-actions/144003
112
121
 
113
- OpenCode workspace configs include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions.
122
+ OpenCode workspace configs include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`.
114
123
 
115
- Cursor documents `AGENTS.md` as a root-level project file. Codex documents `AGENTS.md` discovery, but not a native config include for an extra workspace file. In practice that means subdirectories and repos opened below the workspace root will not reliably get the shared workspace `AGENTS.md` today for Cursor or Codex.
124
+ Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` still disables Cursor workspace propagation, so it does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load repo-local or nested `AGENTS.md` files that already exist in the opened project. Codex documents nested `AGENTS.md` discovery, but not a native config include for an extra workspace file.
116
125
 
117
126
  ## Features
118
127
 
@@ -121,12 +130,20 @@ Cursor documents `AGENTS.md` as a root-level project file. Codex documents `AGEN
121
130
  Plan-then-apply workflow. Preview every change before it touches disk.
122
131
 
123
132
  ```bash
133
+ code-agnostic validate # check canonical source files
124
134
  code-agnostic plan -a cursor # dry-run for one editor
125
135
  code-agnostic plan # dry-run for all
126
136
  code-agnostic apply # apply changes
127
137
  code-agnostic status # check drift
128
138
  ```
129
139
 
140
+ If managed outputs need repair after an apply, restore the active synced revision:
141
+
142
+ ```bash
143
+ code-agnostic restore
144
+ code-agnostic restore -w myproject
145
+ ```
146
+
130
147
  ### MCP management
131
148
 
132
149
  Add, remove, and list MCP servers without editing JSON by hand.
@@ -162,16 +179,35 @@ code-agnostic rules remove --name python-style
162
179
 
163
180
  ### Skills and agents
164
181
 
165
- Canonical YAML frontmatter format, cross-compiled per editor.
182
+ Canonical YAML frontmatter format, cross-compiled per editor. Install or edit skills in the `code-agnostic` source of truth, then run `plan` / `apply`; do not hand-copy generated skills into `.codex`, `.cursor`, or OpenCode directories.
166
183
 
167
184
  ```bash
168
185
  code-agnostic skills list
169
186
  code-agnostic agents list
170
187
  ```
171
188
 
189
+ Manual skill install today:
190
+
191
+ ```bash
192
+ mkdir -p ~/.config/code-agnostic/skills
193
+ cp -R ./my-skill ~/.config/code-agnostic/skills/my-skill
194
+ code-agnostic plan
195
+ code-agnostic apply
196
+ ```
197
+
198
+ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be inspected with `code-agnostic skills list -w <name>`. Codex generated skill outputs are written to `~/.agents/skills`, while Codex agents and config remain under `~/.codex`.
199
+
200
+ Planned convenience command:
201
+
202
+ ```bash
203
+ code-agnostic skills install ./my-skill --apply
204
+ ```
205
+
206
+ That command should copy the skill into the source of truth and then run the normal compiler/apply flow.
207
+
172
208
  ### Workspaces
173
209
 
174
- Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` and symlinked to the workspace root. Repos keep their own repo-specific `AGENTS.md`. OpenCode workspace configs also reference the shared workspace file through `instructions`, so a repo can load both its own `AGENTS.md` and the workspace-level one. Repo-local app config, skills, and agents are propagated for OpenCode and Codex.
210
+ Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs reference the shared workspace file through `instructions`. Repo-local app config, skills, and agents are propagated for OpenCode and Codex.
175
211
 
176
212
  `.cursor` workspace propagation is intentionally disabled to avoid duplicate MCP initialization when opening multi-root workspaces in Cursor (related issue: https://forum.cursor.com/t/mcp-multi-root-workspace-causes-duplicate-mcp-server-initialization-4x-createclient-actions/144003).
177
213
 
@@ -210,13 +246,12 @@ All commands use named flags (`-a`, `-w`, `-v`). Singular aliases work too: `app
210
246
 
211
247
  The compiler migration is documented in:
212
248
 
213
- - [ROADMAP.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/ROADMAP.md)
214
- - [docs/compiler/overview.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/overview.md)
215
- - [docs/compiler/skills.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/skills.md)
216
- - [docs/compiler/agents.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/agents.md)
217
- - [docs/compiler/rules.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/rules.md)
218
- - [docs/compiler/mcp.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/mcp.md)
219
- - [docs/compiler/lossiness.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/lossiness.md)
249
+ - [docs/compiler/overview.md](docs/compiler/overview.md)
250
+ - [docs/compiler/skills.md](docs/compiler/skills.md)
251
+ - [docs/compiler/agents.md](docs/compiler/agents.md)
252
+ - [docs/compiler/rules.md](docs/compiler/rules.md)
253
+ - [docs/compiler/mcp.md](docs/compiler/mcp.md)
254
+ - [docs/compiler/lossiness.md](docs/compiler/lossiness.md)
220
255
 
221
256
  ## Roadmap
222
257
 
@@ -243,3 +278,11 @@ The compiler migration is documented in:
243
278
  uv sync --dev
244
279
  uv run pytest
245
280
  ```
281
+
282
+ Real app-ingestion E2E is gated because it requires installed target CLIs and
283
+ uses each tool's own introspection surface:
284
+
285
+ ```bash
286
+ CODE_AGNOSTIC_REAL_APP_E2E=1 uv run pytest tests/e2e/test_real_app_ingestion_e2e.py -q
287
+ CODE_AGNOSTIC_REAL_APP_E2E=1 CODE_AGNOSTIC_REAL_APP_TARGETS=codex,opencode uv run pytest tests/e2e/test_real_app_ingestion_e2e.py -q
288
+ ```
@@ -13,11 +13,17 @@ AI coding tools each want config in a different place and format. When you use m
13
13
  ├── config/
14
14
  │ └── mcp.base.json MCP servers (editor-agnostic)
15
15
  ├── rules/
16
- │ └── python-style.md Rules with YAML frontmatter
16
+ │ └── python-style/
17
+ │ ├── meta.yaml Rule metadata
18
+ │ └── prompt.md Rule instructions
17
19
  ├── skills/
18
- │ └── code-reviewer/SKILL.md Skills with YAML frontmatter
20
+ │ └── code-reviewer/
21
+ │ ├── meta.yaml Skill metadata
22
+ │ └── prompt.md Skill instructions
19
23
  └── agents/
20
- └── architect.md Agents with YAML frontmatter
24
+ └── architect/
25
+ ├── meta.yaml Agent metadata
26
+ └── prompt.md Agent instructions
21
27
 
22
28
  ↓ plan / apply ↓
23
29
 
@@ -28,6 +34,8 @@ AI coding tools each want config in a different place and format. When you use m
28
34
 
29
35
  Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, etc.
30
36
 
37
+ Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are still supported for migration, but bundle directories are the preferred source format for new config.
38
+
31
39
  Today the implementation is still mixed: some assets are compiled and some are symlinked. The active migration plan is to move to generated outputs everywhere with a strict compiler contract instead of implicit per-app behavior.
32
40
 
33
41
  ## Install
@@ -65,6 +73,7 @@ code-agnostic apps enable -a cursor
65
73
  code-agnostic apps enable -a opencode
66
74
 
67
75
  # Preview and apply
76
+ code-agnostic validate
68
77
  code-agnostic plan
69
78
  code-agnostic apply
70
79
  ```
@@ -79,17 +88,17 @@ code-agnostic apply
79
88
  | Agents sync | yes | yes | yes |
80
89
  | Workspace root `AGENTS.md` link | yes | yes | yes |
81
90
  | Native repo config include for workspace `AGENTS.md` | yes | -- | -- |
82
- | Repo/subdir gets shared workspace `AGENTS.md` today | yes | -- | -- |
83
- | Root-level `AGENTS.md` discovery only | -- | yes | yes |
91
+ | Repo/subdir gets shared workspace `AGENTS.md` today | yes | -- | yes |
92
+ | Nested `AGENTS.md` discovery | -- | yes | yes |
84
93
  | Workspace propagation | yes | -- | yes |
85
94
  | Import from | yes | yes | yes |
86
95
  | Interactive import (TUI) | yes | yes | yes |
87
96
 
88
97
  Cursor workspace propagation is intentionally disabled to avoid duplicate MCP initialization in multi-root workspaces: https://forum.cursor.com/t/mcp-multi-root-workspace-causes-duplicate-mcp-server-initialization-4x-createclient-actions/144003
89
98
 
90
- OpenCode workspace configs include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions.
99
+ OpenCode workspace configs include the shared workspace `AGENTS.md` natively via `instructions`, so repos under the workspace get both repo-local and shared workspace instructions. Codex repos receive workspace instructions through a generated `AGENTS.override.md`, which is added to each repo's `.git/info/exclude`.
91
100
 
92
- Cursor documents `AGENTS.md` as a root-level project file. Codex documents `AGENTS.md` discovery, but not a native config include for an extra workspace file. In practice that means subdirectories and repos opened below the workspace root will not reliably get the shared workspace `AGENTS.md` today for Cursor or Codex.
101
+ Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` still disables Cursor workspace propagation, so it does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load repo-local or nested `AGENTS.md` files that already exist in the opened project. Codex documents nested `AGENTS.md` discovery, but not a native config include for an extra workspace file.
93
102
 
94
103
  ## Features
95
104
 
@@ -98,12 +107,20 @@ Cursor documents `AGENTS.md` as a root-level project file. Codex documents `AGEN
98
107
  Plan-then-apply workflow. Preview every change before it touches disk.
99
108
 
100
109
  ```bash
110
+ code-agnostic validate # check canonical source files
101
111
  code-agnostic plan -a cursor # dry-run for one editor
102
112
  code-agnostic plan # dry-run for all
103
113
  code-agnostic apply # apply changes
104
114
  code-agnostic status # check drift
105
115
  ```
106
116
 
117
+ If managed outputs need repair after an apply, restore the active synced revision:
118
+
119
+ ```bash
120
+ code-agnostic restore
121
+ code-agnostic restore -w myproject
122
+ ```
123
+
107
124
  ### MCP management
108
125
 
109
126
  Add, remove, and list MCP servers without editing JSON by hand.
@@ -139,16 +156,35 @@ code-agnostic rules remove --name python-style
139
156
 
140
157
  ### Skills and agents
141
158
 
142
- Canonical YAML frontmatter format, cross-compiled per editor.
159
+ Canonical YAML frontmatter format, cross-compiled per editor. Install or edit skills in the `code-agnostic` source of truth, then run `plan` / `apply`; do not hand-copy generated skills into `.codex`, `.cursor`, or OpenCode directories.
143
160
 
144
161
  ```bash
145
162
  code-agnostic skills list
146
163
  code-agnostic agents list
147
164
  ```
148
165
 
166
+ Manual skill install today:
167
+
168
+ ```bash
169
+ mkdir -p ~/.config/code-agnostic/skills
170
+ cp -R ./my-skill ~/.config/code-agnostic/skills/my-skill
171
+ code-agnostic plan
172
+ code-agnostic apply
173
+ ```
174
+
175
+ Global skills live under `~/.config/code-agnostic/skills`. Workspace-local skills live under `~/.config/code-agnostic/workspaces/<name>/skills` and can be inspected with `code-agnostic skills list -w <name>`. Codex generated skill outputs are written to `~/.agents/skills`, while Codex agents and config remain under `~/.codex`.
176
+
177
+ Planned convenience command:
178
+
179
+ ```bash
180
+ code-agnostic skills install ./my-skill --apply
181
+ ```
182
+
183
+ That command should copy the skill into the source of truth and then run the normal compiler/apply flow.
184
+
149
185
  ### Workspaces
150
186
 
151
- Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` and symlinked to the workspace root. Repos keep their own repo-specific `AGENTS.md`. OpenCode workspace configs also reference the shared workspace file through `instructions`, so a repo can load both its own `AGENTS.md` and the workspace-level one. Repo-local app config, skills, and agents are propagated for OpenCode and Codex.
187
+ Register workspace directories. Workspace rules are compiled into a canonical `AGENTS.md` at the workspace root. Repos keep their own repo-specific `AGENTS.md`; Codex receives the workspace rules through generated, git-excluded `AGENTS.override.md` files, while OpenCode workspace configs reference the shared workspace file through `instructions`. Repo-local app config, skills, and agents are propagated for OpenCode and Codex.
152
188
 
153
189
  `.cursor` workspace propagation is intentionally disabled to avoid duplicate MCP initialization when opening multi-root workspaces in Cursor (related issue: https://forum.cursor.com/t/mcp-multi-root-workspace-causes-duplicate-mcp-server-initialization-4x-createclient-actions/144003).
154
190
 
@@ -187,13 +223,12 @@ All commands use named flags (`-a`, `-w`, `-v`). Singular aliases work too: `app
187
223
 
188
224
  The compiler migration is documented in:
189
225
 
190
- - [ROADMAP.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/ROADMAP.md)
191
- - [docs/compiler/overview.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/overview.md)
192
- - [docs/compiler/skills.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/skills.md)
193
- - [docs/compiler/agents.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/agents.md)
194
- - [docs/compiler/rules.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/rules.md)
195
- - [docs/compiler/mcp.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/mcp.md)
196
- - [docs/compiler/lossiness.md](/Users/alexeyartishevsky/PycharmProjects/llm-sync/docs/compiler/lossiness.md)
226
+ - [docs/compiler/overview.md](docs/compiler/overview.md)
227
+ - [docs/compiler/skills.md](docs/compiler/skills.md)
228
+ - [docs/compiler/agents.md](docs/compiler/agents.md)
229
+ - [docs/compiler/rules.md](docs/compiler/rules.md)
230
+ - [docs/compiler/mcp.md](docs/compiler/mcp.md)
231
+ - [docs/compiler/lossiness.md](docs/compiler/lossiness.md)
197
232
 
198
233
  ## Roadmap
199
234
 
@@ -220,3 +255,11 @@ The compiler migration is documented in:
220
255
  uv sync --dev
221
256
  uv run pytest
222
257
  ```
258
+
259
+ Real app-ingestion E2E is gated because it requires installed target CLIs and
260
+ uses each tool's own introspection surface:
261
+
262
+ ```bash
263
+ CODE_AGNOSTIC_REAL_APP_E2E=1 uv run pytest tests/e2e/test_real_app_ingestion_e2e.py -q
264
+ CODE_AGNOSTIC_REAL_APP_E2E=1 CODE_AGNOSTIC_REAL_APP_TARGETS=codex,opencode uv run pytest tests/e2e/test_real_app_ingestion_e2e.py -q
265
+ ```
@@ -1,3 +1,3 @@
1
1
  __all__ = ["__version__"]
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.3.5"
@@ -48,13 +48,15 @@ cli.add_command(import_group)
48
48
 
49
49
  def main() -> int:
50
50
  try:
51
- cli(standalone_mode=False)
51
+ result = cli(standalone_mode=False)
52
52
  except click.exceptions.Exit as exc:
53
53
  code = exc.exit_code
54
54
  return code if isinstance(code, int) else 1
55
55
  except click.ClickException as exc:
56
56
  exc.show()
57
57
  return 2
58
+ if isinstance(result, int):
59
+ return result
58
60
  return 0
59
61
 
60
62
 
@@ -22,25 +22,25 @@ def serialize_opencode_agent(agent: Agent) -> str:
22
22
  if reasoning_effort:
23
23
  fm["reasoningEffort"] = reasoning_effort
24
24
 
25
- tools: dict[str, Any] = {}
26
- if agent.metadata.tools.read is not True:
27
- tools["read"] = agent.metadata.tools.read
28
- if agent.metadata.tools.write is not True:
29
- tools["write"] = agent.metadata.tools.write
30
- if agent.metadata.tools.mcp:
31
- tools["mcp"] = agent.metadata.tools.mcp
32
- if tools:
33
- fm["tools"] = tools
34
-
35
- for key, value in agent.metadata.app_passthrough(
25
+ passthrough = agent.metadata.app_passthrough(
36
26
  "opencode",
37
27
  consumed_keys={
38
28
  "model",
39
29
  "reasoning_effort",
40
30
  "sandbox_mode",
41
31
  "nickname_candidates",
32
+ "permission",
42
33
  },
43
- ).items():
34
+ )
35
+
36
+ permission = _merge_permission(
37
+ _compile_permission(agent),
38
+ agent.metadata.app_overrides.get("opencode", {}).get("permission"),
39
+ )
40
+ if permission:
41
+ fm["permission"] = permission
42
+
43
+ for key, value in passthrough.items():
44
44
  if key in fm:
45
45
  continue
46
46
  fm[key] = value
@@ -54,3 +54,27 @@ def serialize_opencode_agent(agent: Agent) -> str:
54
54
 
55
55
  parts.append(agent.content)
56
56
  return "\n".join(parts)
57
+
58
+
59
+ def _compile_permission(agent: Agent) -> dict[str, Any]:
60
+ permission: dict[str, Any] = {}
61
+ permission["read"] = "allow" if agent.metadata.tools.read else "deny"
62
+ permission["edit"] = "allow" if agent.metadata.tools.write else "deny"
63
+ for item in agent.metadata.tools.mcp:
64
+ server = item.get("server")
65
+ if not server:
66
+ continue
67
+ tool = item.get("tool")
68
+ key = f"{server}_{tool}" if tool else f"{server}_*"
69
+ permission[key] = "allow"
70
+ return permission
71
+
72
+
73
+ def _merge_permission(
74
+ generated: dict[str, Any], override: Any | None
75
+ ) -> dict[str, Any] | Any:
76
+ if override is None:
77
+ return generated
78
+ if isinstance(override, dict):
79
+ return {**generated, **override}
80
+ return override
@@ -60,7 +60,7 @@ APP_CATALOG: dict[AppId, AppMetadata] = {
60
60
  toggleable=True,
61
61
  importable=True,
62
62
  supports_import_agents=True,
63
- supports_workspace_propagation=True,
63
+ supports_workspace_propagation=False,
64
64
  project_dir_name=CURSOR_PROJECT_DIRNAME,
65
65
  config_filename=CURSOR_CONFIG_FILENAME,
66
66
  ),
@@ -86,6 +86,9 @@ class AppsService:
86
86
 
87
87
  def plan_for_target(self, target: str) -> SyncPlan:
88
88
  normalized = target.lower()
89
+ if normalized != "all" and not self.is_enabled(normalized):
90
+ return SyncPlan([], [], [f"{normalized} is disabled for sync."])
91
+
89
92
  app_services = self._resolve_services_for_target(normalized)
90
93
  plan = SyncPlanner(
91
94
  core=self.core_repository,
@@ -11,6 +11,7 @@ import tomlkit
11
11
  from code_agnostic.apps.common.interfaces.repositories import IAppConfigRepository
12
12
  from code_agnostic.constants import (
13
13
  AGENTS_DIRNAME,
14
+ AGENTS_PROJECT_DIRNAME,
14
15
  CODEX_CONFIG_FILENAME,
15
16
  CODEX_PROJECT_DIRNAME,
16
17
  SKILLS_DIRNAME,
@@ -32,7 +33,7 @@ class CodexConfigRepository(IAppConfigRepository):
32
33
 
33
34
  @property
34
35
  def skills_dir(self) -> Path:
35
- return self.root / SKILLS_DIRNAME
36
+ return self.root.parent / AGENTS_PROJECT_DIRNAME / SKILLS_DIRNAME
36
37
 
37
38
  @property
38
39
  def agents_dir(self) -> Path: