code-agnostic 0.3.16__tar.gz → 0.3.18__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 (173) hide show
  1. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/PKG-INFO +33 -24
  2. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/README.md +32 -23
  3. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/__init__.py +1 -1
  4. code_agnostic-0.3.18/code_agnostic/agents/compilers.py +176 -0
  5. code_agnostic-0.3.18/code_agnostic/agents/copilot.py +117 -0
  6. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/parser.py +1 -1
  7. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/app_id.py +14 -0
  8. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/apps_service.py +2 -1
  9. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/schema.json +325 -50
  10. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/loader.py +1 -0
  11. code_agnostic-0.3.18/code_agnostic/apps/copilot/config_repository.py +65 -0
  12. code_agnostic-0.3.18/code_agnostic/apps/copilot/mapper.py +97 -0
  13. code_agnostic-0.3.18/code_agnostic/apps/copilot/service.py +140 -0
  14. code_agnostic-0.3.18/code_agnostic/apps/opencode/__init__.py +1 -0
  15. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/apply.py +8 -2
  16. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/plan.py +14 -3
  17. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/constants.py +4 -0
  18. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/imports/adapters.py +12 -0
  19. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/imports/service.py +75 -0
  20. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/lossiness.py +21 -11
  21. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/models.py +3 -0
  22. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/planner.py +44 -22
  23. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/project_artifacts.py +3 -0
  24. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/skills/compilers.py +12 -0
  25. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/skills/parser.py +1 -1
  26. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/spec/loaders.py +2 -2
  27. code_agnostic-0.3.18/code_agnostic/spec/schemas/agent.v1.schema.json +148 -0
  28. code_agnostic-0.3.18/code_agnostic/spec/schemas/rule.v1.schema.json +49 -0
  29. code_agnostic-0.3.18/code_agnostic/spec/schemas/skill.v1.schema.json +78 -0
  30. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/renderers.py +13 -4
  31. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/workspace_artifacts.py +17 -0
  32. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/PKG-INFO +33 -24
  33. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/SOURCES.txt +5 -0
  34. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/pyproject.toml +1 -1
  35. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_apply_apps.py +287 -1
  36. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_apply_codex.py +4 -2
  37. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_apps.py +1 -0
  38. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_explain_lossiness.py +38 -0
  39. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_import.py +44 -0
  40. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_plan.py +12 -0
  41. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_workspace_config_sync.py +5 -1
  42. code_agnostic-0.3.16/code_agnostic/agents/compilers.py +0 -87
  43. code_agnostic-0.3.16/code_agnostic/spec/schemas/agent.v1.schema.json +0 -70
  44. code_agnostic-0.3.16/code_agnostic/spec/schemas/rule.v1.schema.json +0 -22
  45. code_agnostic-0.3.16/code_agnostic/spec/schemas/skill.v1.schema.json +0 -33
  46. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/LICENSE +0 -0
  47. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/__main__.py +0 -0
  48. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/__init__.py +0 -0
  49. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/claude.py +0 -0
  50. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/codex.py +0 -0
  51. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/models.py +0 -0
  52. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/agents/opencode.py +0 -0
  53. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/__init__.py +0 -0
  54. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/claude/__init__.py +0 -0
  55. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/claude/config_repository.py +0 -0
  56. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/claude/mapper.py +0 -0
  57. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/claude/service.py +0 -0
  58. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/__init__.py +0 -0
  59. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/config_repository.py +0 -0
  60. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/mapper.py +0 -0
  61. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/schema_repository.py +0 -0
  62. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/codex/service.py +0 -0
  63. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/__init__.py +0 -0
  64. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/compiled_planning.py +0 -0
  65. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/framework.py +0 -0
  66. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
  67. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
  68. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/interfaces/repositories.py +0 -0
  69. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/interfaces/service.py +0 -0
  70. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/models.py +0 -0
  71. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/schema.py +0 -0
  72. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/symlink_planning.py +0 -0
  73. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/common/utils.py +0 -0
  74. {code_agnostic-0.3.16/code_agnostic/apps/cursor → code_agnostic-0.3.18/code_agnostic/apps/copilot}/__init__.py +0 -0
  75. {code_agnostic-0.3.16/code_agnostic/apps/opencode → code_agnostic-0.3.18/code_agnostic/apps/cursor}/__init__.py +0 -0
  76. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/cursor/config_repository.py +0 -0
  77. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/cursor/mapper.py +0 -0
  78. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/cursor/schema.json +0 -0
  79. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/cursor/schema_repository.py +0 -0
  80. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/cursor/service.py +0 -0
  81. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/opencode/config_repository.py +0 -0
  82. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/opencode/mapper.py +0 -0
  83. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/opencode/schema.json +0 -0
  84. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/opencode/schema_repository.py +0 -0
  85. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/apps/opencode/service.py +0 -0
  86. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/__init__.py +0 -0
  87. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/aliases.py +0 -0
  88. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/__init__.py +0 -0
  89. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/agents.py +0 -0
  90. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/apps.py +0 -0
  91. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
  92. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/import_.py +0 -0
  93. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/mcp.py +0 -0
  94. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/projects.py +0 -0
  95. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/restore.py +0 -0
  96. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/rules.py +0 -0
  97. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/skills.py +0 -0
  98. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/status.py +0 -0
  99. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/validate.py +0 -0
  100. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/commands/workspaces.py +0 -0
  101. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/helpers.py +0 -0
  102. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/cli/options.py +0 -0
  103. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/core/__init__.py +0 -0
  104. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/core/project_repository.py +0 -0
  105. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/core/repository.py +0 -0
  106. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/core/workspace_repository.py +0 -0
  107. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/errors.py +0 -0
  108. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/executor.py +0 -0
  109. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/generated_artifacts.py +0 -0
  110. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/git_exclude_service.py +0 -0
  111. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/imports/__init__.py +0 -0
  112. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/imports/filesystem.py +0 -0
  113. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/imports/models.py +0 -0
  114. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/mcp_service.py +0 -0
  115. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/rules/__init__.py +0 -0
  116. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/rules/compilers.py +0 -0
  117. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/rules/models.py +0 -0
  118. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/rules/parser.py +0 -0
  119. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/rules/repository.py +0 -0
  120. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/skills/__init__.py +0 -0
  121. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/skills/install_sources.py +0 -0
  122. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/skills/models.py +0 -0
  123. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/spec/__init__.py +0 -0
  124. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
  125. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
  126. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/status.py +0 -0
  127. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/__init__.py +0 -0
  128. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/enums.py +0 -0
  129. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/import_selector.py +0 -0
  130. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/sections.py +0 -0
  131. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/tui/tables.py +0 -0
  132. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/utils.py +0 -0
  133. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/validation.py +0 -0
  134. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic/workspaces.py +0 -0
  135. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/dependency_links.txt +0 -0
  136. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/entry_points.txt +0 -0
  137. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/requires.txt +0 -0
  138. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/code_agnostic.egg-info/top_level.txt +0 -0
  139. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/setup.cfg +0 -0
  140. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_agents.py +0 -0
  141. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_aliases.py +0 -0
  142. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_apply_cursor.py +0 -0
  143. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_apply_target.py +0 -0
  144. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_flags.py +0 -0
  145. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_git_exclude.py +0 -0
  146. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_import_interactive.py +0 -0
  147. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_mcp.py +0 -0
  148. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_module_organization.py +0 -0
  149. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_projects.py +0 -0
  150. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_restore.py +0 -0
  151. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_rules.py +0 -0
  152. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_skills.py +0 -0
  153. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_status.py +0 -0
  154. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_validate.py +0 -0
  155. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_workspace_resolution.py +0 -0
  156. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_cli_workspaces.py +0 -0
  157. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_common_mcp_to_dto.py +0 -0
  158. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_common_repository.py +0 -0
  159. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_compiled_planning.py +0 -0
  160. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_dto_to_common_mcp.py +0 -0
  161. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_git_exclude_service.py +0 -0
  162. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_mcp_service.py +0 -0
  163. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_planner_executor.py +0 -0
  164. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_planner_rules.py +0 -0
  165. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_project_config_sync.py +0 -0
  166. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_skill_install_sources.py +0 -0
  167. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_symlink_planning.py +0 -0
  168. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_sync_plan_model.py +0 -0
  169. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_transactional_executor.py +0 -0
  170. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_utils.py +0 -0
  171. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_version.py +0 -0
  172. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/tests/test_workspace_repo_status.py +0 -0
  173. {code_agnostic-0.3.16 → code_agnostic-0.3.18}/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.16
3
+ Version: 0.3.18
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
@@ -56,9 +56,10 @@ AI coding tools each want config in a different place and format. When you use m
56
56
  ~/.cursor/ Compiled & synced for Cursor
57
57
  ~/.codex/ Compiled & synced for Codex (or CODEX_HOME)
58
58
  ~/.claude.json and ~/.claude/ Compiled & synced for Claude Code
59
+ ~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
59
60
  ```
60
61
 
61
- Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, and `CLAUDE.local.md` memory for Claude Code.
62
+ Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex/Copilot, and `CLAUDE.local.md` memory for Claude Code.
62
63
 
63
64
  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.
64
65
 
@@ -77,8 +78,9 @@ Today the implementation is still mixed: some assets are compiled and some are s
77
78
 
78
79
  Workspace sync may generate repo-local outputs, but those outputs are not
79
80
  source. Project-local skill folders generated inside a repo, such as
80
- `.agents/skills` or `.opencode/skills`, are also generated outputs; install
81
- skills into managed project source first, then run `plan` / `apply`.
81
+ `.agents/skills`, `.opencode/skills`, or `.github/skills`, are also generated
82
+ outputs; install skills into managed project source first, then run `plan` /
83
+ `apply`.
82
84
 
83
85
  ## Install
84
86
 
@@ -114,6 +116,7 @@ code-agnostic import apply -a codex
114
116
  code-agnostic apps enable -a cursor
115
117
  code-agnostic apps enable -a opencode
116
118
  code-agnostic apps enable -a claude
119
+ code-agnostic apps enable -a copilot
117
120
 
118
121
  # Preview and apply
119
122
  code-agnostic validate
@@ -123,19 +126,19 @@ code-agnostic apply
123
126
 
124
127
  ## Editor compatibility
125
128
 
126
- | Feature | OpenCode | Cursor | Codex | Claude Code |
127
- |---------|:--------:|:------:|:-----:|:-----------:|
128
- | MCP sync | yes | yes | yes | yes |
129
- | Rules sync (cross-compiled) | yes | yes | yes | yes |
130
- | Skills sync | yes | yes | yes | yes |
131
- | Agents sync | yes | yes | yes | yes |
132
- | Workspace root `AGENTS.md` link | yes | yes | yes | yes |
133
- | Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
134
- | Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
135
- | Nested `AGENTS.md` discovery | -- | yes | yes | -- |
136
- | Workspace propagation | yes | yes | yes | yes |
137
- | Import from | yes | yes | yes | yes |
138
- | Interactive import (TUI) | yes | yes | yes | yes |
129
+ | Feature | OpenCode | Cursor | Codex | Claude Code | GitHub Copilot |
130
+ |---------|:--------:|:------:|:-----:|:-----------:|:--------------:|
131
+ | MCP sync | yes | yes | yes | yes | yes |
132
+ | Rules sync (cross-compiled) | yes | yes | yes | yes | yes |
133
+ | Skills sync | yes | yes | yes | yes | yes |
134
+ | Agents sync | yes | yes | yes | yes | yes |
135
+ | Workspace root `AGENTS.md` link | yes | yes | yes | yes | yes |
136
+ | Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- | -- |
137
+ | Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes | yes |
138
+ | Nested `AGENTS.md` discovery | -- | yes | yes | -- | yes |
139
+ | Workspace propagation | yes | yes | yes | yes | yes |
140
+ | Import from | yes | yes | yes | yes | yes |
141
+ | Interactive import (TUI) | yes | yes | yes | yes | yes |
139
142
 
140
143
  `yes` means the resource type is synced for that editor. Some metadata is still
141
144
  target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
@@ -143,7 +146,7 @@ that are omitted or rejected for a selected target.
143
146
 
144
147
  Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
145
148
 
146
- OpenCode workspace configs write project-root `opencode.json` files that 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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
149
+ OpenCode workspace configs write project-root `opencode.json` files that 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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`. Copilot repo-shared MCP, skills, and agents are written under `.github/`.
147
150
 
148
151
  Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load `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.
149
152
 
@@ -157,14 +160,13 @@ Plan-then-apply workflow. Preview every change before it touches disk.
157
160
  code-agnostic validate # check canonical source files
158
161
  code-agnostic plan -a cursor # dry-run for one editor
159
162
  code-agnostic plan # dry-run for all
160
- code-agnostic apply # apply changes for all enabled editors
161
163
  code-agnostic status # check drift and disabled app states
162
164
  code-agnostic explain-lossiness # show fields omitted or rejected per editor
163
165
  ```
164
166
 
165
167
  Bare `plan` and `apply` target every enabled editor; bare `status` also shows
166
- disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, or `-a claude`
167
- when you want one editor at a time.
168
+ disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, `-a claude`, or
169
+ `-a copilot` when you want one editor at a time.
168
170
 
169
171
  If managed outputs need repair after an apply, restore the active synced revision:
170
172
 
@@ -271,9 +273,12 @@ a coarse `[skills]` marker today. Codex generated skill outputs are written to
271
273
  `~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
272
274
  set, defaulting to `~/.codex`. Claude Code generated skills and agents are
273
275
  written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
274
- copies under repo-local `.claude/skills` and `.claude/agents`.
276
+ copies under repo-local `.claude/skills` and `.claude/agents`. GitHub Copilot
277
+ generated skills and agents are written under `~/.copilot/skills` and
278
+ `~/.copilot/agents`, with repo-shared copies under `.github/skills` and
279
+ `.github/agents`.
275
280
 
276
- If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, or `.claude/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
281
+ If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, `.claude/skills`, or `.github/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
277
282
 
278
283
  Planned convenience command:
279
284
 
@@ -287,7 +292,7 @@ first implementation slice.
287
292
 
288
293
  ### Workspaces
289
294
 
290
- 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 write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, and Claude; user-created target app skill folders remain unmanaged unless they were generated from managed source.
295
+ 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 write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Copilot receives repo-shared `.github/mcp.json`, `.github/skills`, and `.github/agents`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, Claude, and Copilot; user-created target app skill folders remain unmanaged unless they were generated from managed source.
291
296
 
292
297
  Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
293
298
 
@@ -300,7 +305,10 @@ code-agnostic workspaces list
300
305
 
301
306
  Prevent synced paths from showing up in `git status`. Managed per-workspace with customizable patterns.
302
307
 
308
+ Writing `.git/info/exclude` is always explicit — `apply` does **not** touch it by default. Opt in per run with `apply --apply-excludes`, or use the standalone command:
309
+
303
310
  ```bash
311
+ code-agnostic apply --apply-excludes # apply + write excludes
304
312
  code-agnostic workspaces git-exclude # all workspaces
305
313
  code-agnostic workspaces git-exclude -w myproject # one workspace
306
314
  code-agnostic workspaces exclude-add --pattern "*.generated" -w myproject
@@ -315,6 +323,7 @@ Migrate existing config from any supported editor into the hub.
315
323
  code-agnostic import plan -a codex
316
324
  code-agnostic import apply -a codex
317
325
  code-agnostic import plan -a claude
326
+ code-agnostic import plan -a copilot
318
327
  code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
319
328
  code-agnostic import plan -a codex -i # interactive TUI picker
320
329
  ```
@@ -31,9 +31,10 @@ AI coding tools each want config in a different place and format. When you use m
31
31
  ~/.cursor/ Compiled & synced for Cursor
32
32
  ~/.codex/ Compiled & synced for Codex (or CODEX_HOME)
33
33
  ~/.claude.json and ~/.claude/ Compiled & synced for Claude Code
34
+ ~/.copilot/ and repo .github/ Compiled & synced for GitHub Copilot
34
35
  ```
35
36
 
36
- Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex, and `CLAUDE.local.md` memory for Claude Code.
37
+ Each resource is cross-compiled to the target editor's native format. Rules become `.mdc` files for Cursor, `AGENTS.md` sections for OpenCode/Codex/Copilot, and `CLAUDE.local.md` memory for Claude Code.
37
38
 
38
39
  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.
39
40
 
@@ -52,8 +53,9 @@ Today the implementation is still mixed: some assets are compiled and some are s
52
53
 
53
54
  Workspace sync may generate repo-local outputs, but those outputs are not
54
55
  source. Project-local skill folders generated inside a repo, such as
55
- `.agents/skills` or `.opencode/skills`, are also generated outputs; install
56
- skills into managed project source first, then run `plan` / `apply`.
56
+ `.agents/skills`, `.opencode/skills`, or `.github/skills`, are also generated
57
+ outputs; install skills into managed project source first, then run `plan` /
58
+ `apply`.
57
59
 
58
60
  ## Install
59
61
 
@@ -89,6 +91,7 @@ code-agnostic import apply -a codex
89
91
  code-agnostic apps enable -a cursor
90
92
  code-agnostic apps enable -a opencode
91
93
  code-agnostic apps enable -a claude
94
+ code-agnostic apps enable -a copilot
92
95
 
93
96
  # Preview and apply
94
97
  code-agnostic validate
@@ -98,19 +101,19 @@ code-agnostic apply
98
101
 
99
102
  ## Editor compatibility
100
103
 
101
- | Feature | OpenCode | Cursor | Codex | Claude Code |
102
- |---------|:--------:|:------:|:-----:|:-----------:|
103
- | MCP sync | yes | yes | yes | yes |
104
- | Rules sync (cross-compiled) | yes | yes | yes | yes |
105
- | Skills sync | yes | yes | yes | yes |
106
- | Agents sync | yes | yes | yes | yes |
107
- | Workspace root `AGENTS.md` link | yes | yes | yes | yes |
108
- | Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
109
- | Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
110
- | Nested `AGENTS.md` discovery | -- | yes | yes | -- |
111
- | Workspace propagation | yes | yes | yes | yes |
112
- | Import from | yes | yes | yes | yes |
113
- | Interactive import (TUI) | yes | yes | yes | yes |
104
+ | Feature | OpenCode | Cursor | Codex | Claude Code | GitHub Copilot |
105
+ |---------|:--------:|:------:|:-----:|:-----------:|:--------------:|
106
+ | MCP sync | yes | yes | yes | yes | yes |
107
+ | Rules sync (cross-compiled) | yes | yes | yes | yes | yes |
108
+ | Skills sync | yes | yes | yes | yes | yes |
109
+ | Agents sync | yes | yes | yes | yes | yes |
110
+ | Workspace root `AGENTS.md` link | yes | yes | yes | yes | yes |
111
+ | Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- | -- |
112
+ | Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes | yes |
113
+ | Nested `AGENTS.md` discovery | -- | yes | yes | -- | yes |
114
+ | Workspace propagation | yes | yes | yes | yes | yes |
115
+ | Import from | yes | yes | yes | yes | yes |
116
+ | Interactive import (TUI) | yes | yes | yes | yes | yes |
114
117
 
115
118
  `yes` means the resource type is synced for that editor. Some metadata is still
116
119
  target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
@@ -118,7 +121,7 @@ that are omitted or rejected for a selected target.
118
121
 
119
122
  Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
120
123
 
121
- OpenCode workspace configs write project-root `opencode.json` files that 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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
124
+ OpenCode workspace configs write project-root `opencode.json` files that 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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`. Copilot repo-shared MCP, skills, and agents are written under `.github/`.
122
125
 
123
126
  Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic` does not copy or link the shared workspace `AGENTS.md` into child repos; Cursor will load `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.
124
127
 
@@ -132,14 +135,13 @@ Plan-then-apply workflow. Preview every change before it touches disk.
132
135
  code-agnostic validate # check canonical source files
133
136
  code-agnostic plan -a cursor # dry-run for one editor
134
137
  code-agnostic plan # dry-run for all
135
- code-agnostic apply # apply changes for all enabled editors
136
138
  code-agnostic status # check drift and disabled app states
137
139
  code-agnostic explain-lossiness # show fields omitted or rejected per editor
138
140
  ```
139
141
 
140
142
  Bare `plan` and `apply` target every enabled editor; bare `status` also shows
141
- disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, or `-a claude`
142
- when you want one editor at a time.
143
+ disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, `-a claude`, or
144
+ `-a copilot` when you want one editor at a time.
143
145
 
144
146
  If managed outputs need repair after an apply, restore the active synced revision:
145
147
 
@@ -246,9 +248,12 @@ a coarse `[skills]` marker today. Codex generated skill outputs are written to
246
248
  `~/.agents/skills`, while Codex agents and config remain under `CODEX_HOME` when
247
249
  set, defaulting to `~/.codex`. Claude Code generated skills and agents are
248
250
  written under `~/.claude/skills` and `~/.claude/agents`, with workspace/project
249
- copies under repo-local `.claude/skills` and `.claude/agents`.
251
+ copies under repo-local `.claude/skills` and `.claude/agents`. GitHub Copilot
252
+ generated skills and agents are written under `~/.copilot/skills` and
253
+ `~/.copilot/agents`, with repo-shared copies under `.github/skills` and
254
+ `.github/agents`.
250
255
 
251
- If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, or `.claude/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
256
+ If a target app discovers user-created repo-local skill folders such as `.agents/skills`, `.opencode/skills`, `.claude/skills`, or `.github/skills`, treat those as unmanaged app inputs unless they were generated from `code-agnostic` project source. Workspace and project sync write only the exact generated paths recorded in their `.sync-state.json` files.
252
257
 
253
258
  Planned convenience command:
254
259
 
@@ -262,7 +267,7 @@ first implementation slice.
262
267
 
263
268
  ### Workspaces
264
269
 
265
- 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 write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, and Claude; user-created target app skill folders remain unmanaged unless they were generated from managed source.
270
+ 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 write project-root `opencode.json` files that reference the shared workspace file through `instructions`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`. Copilot receives repo-shared `.github/mcp.json`, `.github/skills`, and `.github/agents`. Workspace source config, skills, and agents are propagated into repo-local generated paths for OpenCode, Cursor, Codex, Claude, and Copilot; user-created target app skill folders remain unmanaged unless they were generated from managed source.
266
271
 
267
272
  Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
268
273
 
@@ -275,7 +280,10 @@ code-agnostic workspaces list
275
280
 
276
281
  Prevent synced paths from showing up in `git status`. Managed per-workspace with customizable patterns.
277
282
 
283
+ Writing `.git/info/exclude` is always explicit — `apply` does **not** touch it by default. Opt in per run with `apply --apply-excludes`, or use the standalone command:
284
+
278
285
  ```bash
286
+ code-agnostic apply --apply-excludes # apply + write excludes
279
287
  code-agnostic workspaces git-exclude # all workspaces
280
288
  code-agnostic workspaces git-exclude -w myproject # one workspace
281
289
  code-agnostic workspaces exclude-add --pattern "*.generated" -w myproject
@@ -290,6 +298,7 @@ Migrate existing config from any supported editor into the hub.
290
298
  code-agnostic import plan -a codex
291
299
  code-agnostic import apply -a codex
292
300
  code-agnostic import plan -a claude
301
+ code-agnostic import plan -a copilot
293
302
  code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
294
303
  code-agnostic import plan -a codex -i # interactive TUI picker
295
304
  ```
@@ -1,3 +1,3 @@
1
1
  __all__ = ["__version__"]
2
2
 
3
- __version__ = "0.3.16"
3
+ __version__ = "0.3.18"
@@ -0,0 +1,176 @@
1
+ """Per-editor agent compilers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any
7
+
8
+ import yaml
9
+
10
+ from code_agnostic.agents.claude import serialize_claude_agent
11
+ from code_agnostic.agents.codex import serialize_codex_agent
12
+ from code_agnostic.agents.models import Agent
13
+ from code_agnostic.agents.opencode import serialize_opencode_agent
14
+ from code_agnostic.errors import InvalidConfigSchemaError
15
+
16
+ _CURSOR_AGENT_FRONTMATTER_KEYS = frozenset(
17
+ {"name", "description", "model", "readonly", "is_background"}
18
+ )
19
+ _COPILOT_AGENT_FRONTMATTER_KEYS = frozenset(
20
+ {
21
+ "name",
22
+ "description",
23
+ "model",
24
+ "target",
25
+ "disable-model-invocation",
26
+ "user-invocable",
27
+ "infer",
28
+ "mcp-servers",
29
+ "metadata",
30
+ }
31
+ )
32
+
33
+
34
+ class IAgentCompiler(ABC):
35
+ @abstractmethod
36
+ def compile(self, agent: Agent) -> str:
37
+ """Return compiled agent content for target editor."""
38
+
39
+
40
+ class OpenCodeAgentCompiler(IAgentCompiler):
41
+ """Cross-compile for OpenCode agents."""
42
+
43
+ def compile(self, agent: Agent) -> str:
44
+ return serialize_opencode_agent(agent)
45
+
46
+
47
+ class CursorAgentCompiler(IAgentCompiler):
48
+ """Cross-compile for Cursor subagents."""
49
+
50
+ def compile(self, agent: Agent) -> str:
51
+ return _serialize_cursor_agent(agent)
52
+
53
+
54
+ def _serialize_cursor_agent(agent: Agent) -> str:
55
+ overrides = dict(agent.metadata.app_overrides.get("cursor", {}))
56
+ fm: dict[str, Any] = {}
57
+ if agent.metadata.name:
58
+ fm["name"] = agent.metadata.name
59
+ if agent.metadata.description:
60
+ fm["description"] = agent.metadata.description
61
+
62
+ model = overrides.pop("model", agent.metadata.model)
63
+ if model:
64
+ fm["model"] = model
65
+
66
+ if agent.metadata.tools.write is False:
67
+ fm["readonly"] = True
68
+
69
+ for key in sorted(overrides):
70
+ if key not in _CURSOR_AGENT_FRONTMATTER_KEYS:
71
+ allowed = ", ".join(sorted(_CURSOR_AGENT_FRONTMATTER_KEYS))
72
+ raise InvalidConfigSchemaError(
73
+ agent.source_path,
74
+ f"x-cursor.{key} is not supported in cursor agent "
75
+ f"frontmatter; allowed keys: {allowed}",
76
+ )
77
+ fm[key] = overrides[key]
78
+
79
+ parts: list[str] = []
80
+ if fm:
81
+ parts.append("---")
82
+ parts.append(yaml.dump(fm, default_flow_style=False, sort_keys=False).rstrip())
83
+ parts.append("---")
84
+ parts.append("")
85
+ parts.append(agent.content)
86
+ return "\n".join(parts)
87
+
88
+
89
+ class CodexAgentCompiler(IAgentCompiler):
90
+ """Cross-compile for Codex subagents."""
91
+
92
+ def compile(self, agent: Agent) -> str:
93
+ return serialize_codex_agent(agent)
94
+
95
+
96
+ class CopilotAgentCompiler(IAgentCompiler):
97
+ """Cross-compile for GitHub Copilot custom agents."""
98
+
99
+ def compile(self, agent: Agent) -> str:
100
+ return _serialize_copilot_agent(agent)
101
+
102
+
103
+ def _serialize_copilot_agent(agent: Agent) -> str:
104
+ overrides = dict(agent.metadata.app_overrides.get("copilot", {}))
105
+ fm: dict[str, Any] = {}
106
+
107
+ name = overrides.pop("name", agent.metadata.name or agent.name)
108
+ if name:
109
+ fm["name"] = name
110
+
111
+ description = overrides.pop("description", agent.metadata.description)
112
+ if not description:
113
+ raise InvalidConfigSchemaError(
114
+ agent.source_path,
115
+ "Copilot agents require a description",
116
+ )
117
+ fm["description"] = description
118
+
119
+ model = overrides.pop("model", agent.metadata.model)
120
+ if model:
121
+ fm["model"] = model
122
+
123
+ tools = _copilot_tools(agent)
124
+ if tools is not None:
125
+ fm["tools"] = tools
126
+
127
+ for key in sorted(overrides):
128
+ if key not in _COPILOT_AGENT_FRONTMATTER_KEYS:
129
+ allowed = ", ".join(sorted(_COPILOT_AGENT_FRONTMATTER_KEYS))
130
+ raise InvalidConfigSchemaError(
131
+ agent.source_path,
132
+ f"x-copilot.{key} is not supported in copilot agent "
133
+ f"frontmatter; allowed keys: {allowed}",
134
+ )
135
+ if key in fm:
136
+ continue
137
+ fm[key] = overrides[key]
138
+
139
+ parts: list[str] = []
140
+ if fm:
141
+ parts.append("---")
142
+ parts.append(yaml.dump(fm, default_flow_style=False, sort_keys=False).rstrip())
143
+ parts.append("---")
144
+ parts.append("")
145
+ parts.append(agent.content)
146
+ return "\n".join(parts)
147
+
148
+
149
+ def _copilot_tools(agent: Agent) -> list[str] | None:
150
+ permissions = agent.metadata.tools
151
+ if permissions.read is True and permissions.write is True and not permissions.mcp:
152
+ return None
153
+
154
+ tools: list[str] = []
155
+ if permissions.read:
156
+ tools.append("read")
157
+ if permissions.write:
158
+ tools.append("edit")
159
+
160
+ for item in agent.metadata.tools.mcp:
161
+ server = item.get("server")
162
+ tool = item.get("tool")
163
+ if not server:
164
+ raise InvalidConfigSchemaError(
165
+ agent.source_path,
166
+ "Copilot agent MCP tools require a server",
167
+ )
168
+ tools.append(f"{server}/{tool}" if tool else f"{server}/*")
169
+ return tools
170
+
171
+
172
+ class ClaudeAgentCompiler(IAgentCompiler):
173
+ """Cross-compile for Claude Code subagents."""
174
+
175
+ def compile(self, agent: Agent) -> str:
176
+ return serialize_claude_agent(agent)
@@ -0,0 +1,117 @@
1
+ """GitHub Copilot custom agent Markdown conversion."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import yaml
11
+
12
+ from code_agnostic.agents.models import Agent, AgentMetadata, AgentToolPermissions
13
+
14
+ _FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
15
+ _READ_ALIASES = {"read", "notebookread", "view"}
16
+ _WRITE_ALIASES = {"edit", "multiedit", "write", "notebookedit"}
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class CopilotAgentParseResult:
21
+ agent: Agent
22
+ warnings: list[str]
23
+
24
+
25
+ def parse_copilot_agent(path: Path) -> CopilotAgentParseResult:
26
+ text = path.read_text(encoding="utf-8")
27
+ raw: dict[str, Any]
28
+ match = _FRONTMATTER_RE.match(text)
29
+ if match:
30
+ raw = yaml.safe_load(match.group(1)) or {}
31
+ content = text[match.end() :]
32
+ else:
33
+ raw = {}
34
+ content = text
35
+ if not isinstance(raw, dict):
36
+ raw = {}
37
+
38
+ fallback_name = _copilot_agent_stem(path)
39
+ tools, warnings = _parse_copilot_tools(raw.get("tools"), path)
40
+ agent = Agent(
41
+ name=fallback_name,
42
+ source_path=path,
43
+ metadata=AgentMetadata(
44
+ name=str(raw.get("name", fallback_name)),
45
+ description=str(raw.get("description", "")),
46
+ model=str(raw.get("model", "")),
47
+ tools=tools,
48
+ ),
49
+ content=content,
50
+ )
51
+ return CopilotAgentParseResult(agent=agent, warnings=warnings)
52
+
53
+
54
+ def copilot_agent_filename(path: Path) -> str:
55
+ return f"{_copilot_agent_stem(path)}.agent.md"
56
+
57
+
58
+ def _copilot_agent_stem(path: Path) -> str:
59
+ name = path.name
60
+ if name.endswith(".agent.md"):
61
+ return name[: -len(".agent.md")]
62
+ if name.endswith(".md"):
63
+ return name[: -len(".md")]
64
+ return path.stem
65
+
66
+
67
+ def _parse_copilot_tools(
68
+ raw: Any, path: Path
69
+ ) -> tuple[AgentToolPermissions, list[str]]:
70
+ if raw is None:
71
+ return AgentToolPermissions(), []
72
+
73
+ tokens = _coerce_tool_tokens(raw)
74
+ if tokens is None:
75
+ return AgentToolPermissions(), [
76
+ f"Skipped unrepresentable Copilot tools: {path}"
77
+ ]
78
+
79
+ if "*" in tokens:
80
+ return AgentToolPermissions(), []
81
+
82
+ read = False
83
+ write = False
84
+ mcp: list[dict[str, str]] = []
85
+ unsupported: list[str] = []
86
+ for token in tokens:
87
+ normalized = token.lower()
88
+ if normalized in _READ_ALIASES:
89
+ read = True
90
+ continue
91
+ if normalized in _WRITE_ALIASES:
92
+ write = True
93
+ continue
94
+ if "/" in token:
95
+ server, tool = token.split("/", 1)
96
+ if server and tool:
97
+ item = {"server": server}
98
+ if tool != "*":
99
+ item["tool"] = tool
100
+ mcp.append(item)
101
+ continue
102
+ unsupported.append(token)
103
+
104
+ if unsupported:
105
+ return AgentToolPermissions(), [
106
+ f"Skipped unrepresentable Copilot tools in {path}: {', '.join(unsupported)}"
107
+ ]
108
+
109
+ return AgentToolPermissions(read=read, write=write, mcp=mcp), []
110
+
111
+
112
+ def _coerce_tool_tokens(raw: Any) -> list[str] | None:
113
+ if isinstance(raw, str):
114
+ return [item.strip() for item in raw.split(",") if item.strip()]
115
+ if isinstance(raw, list):
116
+ return [item for item in raw if isinstance(item, str)]
117
+ return None
@@ -19,7 +19,7 @@ from code_agnostic.agents.models import (
19
19
  from code_agnostic.spec.loaders import load_agent_bundle
20
20
 
21
21
  _FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
22
- _APP_OVERRIDE_PREFIXES = ("cursor", "codex", "opencode", "claude")
22
+ _APP_OVERRIDE_PREFIXES = ("cursor", "codex", "opencode", "claude", "copilot")
23
23
 
24
24
 
25
25
  def parse_agent(path: Path) -> Agent:
@@ -6,6 +6,8 @@ from code_agnostic.constants import (
6
6
  CLAUDE_PROJECT_DIRNAME,
7
7
  CODEX_CONFIG_FILENAME,
8
8
  CODEX_PROJECT_DIRNAME,
9
+ COPILOT_PROJECT_CONFIG_FILENAME,
10
+ COPILOT_PROJECT_DIRNAME,
9
11
  CURSOR_CONFIG_FILENAME,
10
12
  CURSOR_PROJECT_DIRNAME,
11
13
  OPENCODE_CONFIG_FILENAME,
@@ -19,6 +21,7 @@ class AppId(str, Enum):
19
21
  CURSOR = "cursor"
20
22
  CODEX = "codex"
21
23
  CLAUDE = "claude"
24
+ COPILOT = "copilot"
22
25
 
23
26
 
24
27
  @dataclass(frozen=True)
@@ -89,6 +92,17 @@ APP_CATALOG: dict[AppId, AppMetadata] = {
89
92
  project_dir_name=CLAUDE_PROJECT_DIRNAME,
90
93
  config_filename=CLAUDE_CONFIG_FILENAME,
91
94
  ),
95
+ AppId.COPILOT: AppMetadata(
96
+ app_id=AppId.COPILOT,
97
+ label="GitHub Copilot",
98
+ targetable=True,
99
+ toggleable=True,
100
+ importable=True,
101
+ supports_import_agents=True,
102
+ supports_workspace_propagation=True,
103
+ project_dir_name=COPILOT_PROJECT_DIRNAME,
104
+ config_filename=COPILOT_PROJECT_CONFIG_FILENAME,
105
+ ),
92
106
  }
93
107
 
94
108
 
@@ -84,7 +84,7 @@ class AppsService:
84
84
  apps = self.load_apps()
85
85
  return [name for name in self.available_apps() if apps.get(name, False)]
86
86
 
87
- def plan_for_target(self, target: str) -> SyncPlan:
87
+ def plan_for_target(self, target: str, *, apply_excludes: bool = False) -> SyncPlan:
88
88
  normalized = target.lower()
89
89
  if normalized != "all" and not self.is_enabled(normalized):
90
90
  return SyncPlan([], [], [f"{normalized} is disabled for sync."])
@@ -94,6 +94,7 @@ class AppsService:
94
94
  core=self.core_repository,
95
95
  app_services=app_services,
96
96
  include_workspace=True,
97
+ include_git_excludes=apply_excludes,
97
98
  ).build()
98
99
  if normalized == "all":
99
100
  if (