code-agnostic 0.3.9__tar.gz → 0.3.10__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.
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/PKG-INFO +36 -8
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/README.md +33 -7
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/__init__.py +1 -1
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/__main__.py +4 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/codex.py +6 -3
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/app_id.py +1 -1
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/claude/service.py +2 -66
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/schema.json +36 -17
- code_agnostic-0.3.10/code_agnostic/apps/common/compiled_planning.py +115 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/repositories.py +10 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/service.py +30 -14
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/schema.json +4 -1
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/service.py +2 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/mcp.py +29 -10
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/skills.py +11 -4
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/status.py +21 -4
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/workspaces.py +29 -13
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/helpers.py +5 -1
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/core/repository.py +4 -2
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/executor.py +10 -0
- code_agnostic-0.3.10/code_agnostic/generated_artifacts.py +134 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/git_exclude_service.py +23 -41
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/imports/adapters.py +10 -10
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/mcp_service.py +4 -2
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/planner.py +15 -12
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/status.py +12 -63
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/renderers.py +13 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/tables.py +1 -1
- code_agnostic-0.3.10/code_agnostic/workspace_artifacts.py +238 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/PKG-INFO +36 -8
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/SOURCES.txt +2 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/requires.txt +2 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/pyproject.toml +4 -2
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_apply_apps.py +38 -12
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_mcp.py +7 -4
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_plan.py +14 -4
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_skills.py +1 -1
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_status.py +15 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_workspaces.py +95 -15
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_common_repository.py +20 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_git_exclude_service.py +31 -9
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_transactional_executor.py +6 -3
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_workspace_config_sync.py +136 -24
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_workspace_repo_status.py +57 -11
- code_agnostic-0.3.9/code_agnostic/apps/common/compiled_planning.py +0 -197
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/LICENSE +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/claude.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/compilers.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/opencode.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/agents/parser.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/apps_service.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/claude/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/claude/config_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/claude/mapper.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/config_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/mapper.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/schema_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/codex/service.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/framework.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/mapper.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/loader.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/schema.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/symlink_planning.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/utils.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/config_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/mapper.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/schema_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/cursor/service.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/config_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/mapper.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/opencode/schema_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/aliases.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/agents.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/apply.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/apps.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/explain_lossiness.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/import_.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/plan.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/restore.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/rules.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/commands/validate.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/cli/options.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/constants.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/core/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/core/workspace_repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/errors.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/imports/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/imports/filesystem.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/imports/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/imports/service.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/lossiness.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/rules/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/rules/compilers.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/rules/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/rules/parser.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/rules/repository.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/skills/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/skills/compilers.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/skills/models.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/skills/parser.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/loaders.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/schemas/agent.v1.schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/schemas/mcp.base.schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/schemas/mcp.v1.schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/schemas/rule.v1.schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/spec/schemas/skill.v1.schema.json +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/__init__.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/enums.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/import_selector.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/tui/sections.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/utils.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/validation.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/workspaces.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/dependency_links.txt +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/entry_points.txt +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic.egg-info/top_level.txt +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/setup.cfg +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_agents.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_aliases.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_apply_codex.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_apply_cursor.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_apply_target.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_apps.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_explain_lossiness.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_flags.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_git_exclude.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_import.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_import_interactive.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_module_organization.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_restore.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_rules.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_validate.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_cli_workspace_resolution.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_common_mcp_to_dto.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_compiled_planning.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_dto_to_common_mcp.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_mcp_service.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_planner_executor.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_planner_rules.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_symlink_planning.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_sync_plan_model.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_utils.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/tests/test_version.py +0 -0
- {code_agnostic-0.3.9 → code_agnostic-0.3.10}/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
|
+
Version: 0.3.10
|
|
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
|
|
@@ -19,6 +19,8 @@ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
|
19
19
|
Requires-Dist: jsonschema>=4.0; extra == "dev"
|
|
20
20
|
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
21
21
|
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
22
|
+
Requires-Dist: types-jsonschema>=4.0; extra == "dev"
|
|
23
|
+
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
22
24
|
Dynamic: license-file
|
|
23
25
|
|
|
24
26
|
# code-agnostic
|
|
@@ -62,6 +64,21 @@ Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are stil
|
|
|
62
64
|
|
|
63
65
|
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.
|
|
64
66
|
|
|
67
|
+
## Scope model
|
|
68
|
+
|
|
69
|
+
`code-agnostic` has two managed source scopes today:
|
|
70
|
+
|
|
71
|
+
- global source config under `~/.config/code-agnostic/`, synced to enabled
|
|
72
|
+
user-level app config;
|
|
73
|
+
- workspace source config under `~/.config/code-agnostic/workspaces/<name>/`,
|
|
74
|
+
propagated into repos inside a registered workspace.
|
|
75
|
+
|
|
76
|
+
Project-local skill folders that users create directly inside a repo, such as
|
|
77
|
+
`.agents/skills` or `.opencode/skills`, are app-native inputs but are not
|
|
78
|
+
managed as source by `code-agnostic` yet. First-class project-scoped installs
|
|
79
|
+
are planned so a single registered project can have managed local source config
|
|
80
|
+
without bypassing the hub.
|
|
81
|
+
|
|
65
82
|
## Install
|
|
66
83
|
|
|
67
84
|
```bash
|
|
@@ -115,7 +132,7 @@ code-agnostic apply
|
|
|
115
132
|
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
|
|
116
133
|
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
|
|
117
134
|
| Nested `AGENTS.md` discovery | -- | yes | yes | -- |
|
|
118
|
-
| Workspace propagation | yes |
|
|
135
|
+
| Workspace propagation | yes | yes | yes | yes |
|
|
119
136
|
| Import from | yes | yes | yes | yes |
|
|
120
137
|
| Interactive import (TUI) | yes | yes | yes | yes |
|
|
121
138
|
|
|
@@ -123,11 +140,11 @@ code-agnostic apply
|
|
|
123
140
|
target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
|
|
124
141
|
that are omitted or rejected for a selected target.
|
|
125
142
|
|
|
126
|
-
Cursor workspace propagation
|
|
143
|
+
Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
|
|
127
144
|
|
|
128
145
|
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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
|
|
129
146
|
|
|
130
|
-
Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic`
|
|
147
|
+
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 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.
|
|
131
148
|
|
|
132
149
|
## Features
|
|
133
150
|
|
|
@@ -139,10 +156,15 @@ Plan-then-apply workflow. Preview every change before it touches disk.
|
|
|
139
156
|
code-agnostic validate # check canonical source files
|
|
140
157
|
code-agnostic plan -a cursor # dry-run for one editor
|
|
141
158
|
code-agnostic plan # dry-run for all
|
|
142
|
-
code-agnostic apply # apply changes
|
|
143
|
-
code-agnostic status # check drift
|
|
159
|
+
code-agnostic apply # apply changes for all enabled editors
|
|
160
|
+
code-agnostic status # check drift and disabled app states
|
|
161
|
+
code-agnostic explain-lossiness # show fields omitted or rejected per editor
|
|
144
162
|
```
|
|
145
163
|
|
|
164
|
+
Bare `plan` and `apply` target every enabled editor; bare `status` also shows
|
|
165
|
+
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, or `-a claude`
|
|
166
|
+
when you want one editor at a time.
|
|
167
|
+
|
|
146
168
|
If managed outputs need repair after an apply, restore the active synced revision:
|
|
147
169
|
|
|
148
170
|
```bash
|
|
@@ -230,9 +252,9 @@ That command should copy the skill into the source of truth and then run the nor
|
|
|
230
252
|
|
|
231
253
|
### Workspaces
|
|
232
254
|
|
|
233
|
-
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`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`.
|
|
255
|
+
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`. 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 project-local skill folders remain unmanaged until project-scoped installs are supported.
|
|
234
256
|
|
|
235
|
-
|
|
257
|
+
Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
|
|
236
258
|
|
|
237
259
|
```bash
|
|
238
260
|
code-agnostic workspaces add --name myproject --path ~/code/myproject
|
|
@@ -262,6 +284,12 @@ code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
|
|
|
262
284
|
code-agnostic import plan -a codex -i # interactive TUI picker
|
|
263
285
|
```
|
|
264
286
|
|
|
287
|
+
`import plan` previews what will be copied into the hub; `import apply` writes
|
|
288
|
+
only the selected sections. Conflicts are skipped by default, so rerun with
|
|
289
|
+
`--on-conflict overwrite` only after reviewing the preview. Use `--include`,
|
|
290
|
+
`--exclude`, `--source-root`, and `--follow-symlinks` to narrow what gets
|
|
291
|
+
imported.
|
|
292
|
+
|
|
265
293
|
### CLI conventions
|
|
266
294
|
|
|
267
295
|
All commands use named flags (`-a`, `-w`, `-v`). Singular aliases work too: `app` = `apps`, `workspace` = `workspaces`.
|
|
@@ -39,6 +39,21 @@ Legacy single-file rules, `skills/<name>/SKILL.md`, and markdown agents are stil
|
|
|
39
39
|
|
|
40
40
|
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.
|
|
41
41
|
|
|
42
|
+
## Scope model
|
|
43
|
+
|
|
44
|
+
`code-agnostic` has two managed source scopes today:
|
|
45
|
+
|
|
46
|
+
- global source config under `~/.config/code-agnostic/`, synced to enabled
|
|
47
|
+
user-level app config;
|
|
48
|
+
- workspace source config under `~/.config/code-agnostic/workspaces/<name>/`,
|
|
49
|
+
propagated into repos inside a registered workspace.
|
|
50
|
+
|
|
51
|
+
Project-local skill folders that users create directly inside a repo, such as
|
|
52
|
+
`.agents/skills` or `.opencode/skills`, are app-native inputs but are not
|
|
53
|
+
managed as source by `code-agnostic` yet. First-class project-scoped installs
|
|
54
|
+
are planned so a single registered project can have managed local source config
|
|
55
|
+
without bypassing the hub.
|
|
56
|
+
|
|
42
57
|
## Install
|
|
43
58
|
|
|
44
59
|
```bash
|
|
@@ -92,7 +107,7 @@ code-agnostic apply
|
|
|
92
107
|
| Native repo config include for workspace `AGENTS.md` | yes | -- | -- | -- |
|
|
93
108
|
| Repo/subdir gets shared workspace instructions today | yes | -- | yes | yes |
|
|
94
109
|
| Nested `AGENTS.md` discovery | -- | yes | yes | -- |
|
|
95
|
-
| Workspace propagation | yes |
|
|
110
|
+
| Workspace propagation | yes | yes | yes | yes |
|
|
96
111
|
| Import from | yes | yes | yes | yes |
|
|
97
112
|
| Interactive import (TUI) | yes | yes | yes | yes |
|
|
98
113
|
|
|
@@ -100,11 +115,11 @@ code-agnostic apply
|
|
|
100
115
|
target-specific or lossy; run `code-agnostic explain-lossiness` to see fields
|
|
101
116
|
that are omitted or rejected for a selected target.
|
|
102
117
|
|
|
103
|
-
Cursor workspace propagation
|
|
118
|
+
Cursor workspace propagation writes repo-local MCP, skills, and agents when those resources exist in the workspace source config.
|
|
104
119
|
|
|
105
120
|
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`. Claude Code receives workspace instructions through generated `CLAUDE.local.md` files, never by editing committed `CLAUDE.md`.
|
|
106
121
|
|
|
107
|
-
Cursor documents `AGENTS.md` support in project roots and subdirectories. `code-agnostic`
|
|
122
|
+
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 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.
|
|
108
123
|
|
|
109
124
|
## Features
|
|
110
125
|
|
|
@@ -116,10 +131,15 @@ Plan-then-apply workflow. Preview every change before it touches disk.
|
|
|
116
131
|
code-agnostic validate # check canonical source files
|
|
117
132
|
code-agnostic plan -a cursor # dry-run for one editor
|
|
118
133
|
code-agnostic plan # dry-run for all
|
|
119
|
-
code-agnostic apply # apply changes
|
|
120
|
-
code-agnostic status # check drift
|
|
134
|
+
code-agnostic apply # apply changes for all enabled editors
|
|
135
|
+
code-agnostic status # check drift and disabled app states
|
|
136
|
+
code-agnostic explain-lossiness # show fields omitted or rejected per editor
|
|
121
137
|
```
|
|
122
138
|
|
|
139
|
+
Bare `plan` and `apply` target every enabled editor; bare `status` also shows
|
|
140
|
+
disabled app states. Use `-a codex`, `-a cursor`, `-a opencode`, or `-a claude`
|
|
141
|
+
when you want one editor at a time.
|
|
142
|
+
|
|
123
143
|
If managed outputs need repair after an apply, restore the active synced revision:
|
|
124
144
|
|
|
125
145
|
```bash
|
|
@@ -207,9 +227,9 @@ That command should copy the skill into the source of truth and then run the nor
|
|
|
207
227
|
|
|
208
228
|
### Workspaces
|
|
209
229
|
|
|
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`. Claude receives generated `CLAUDE.local.md` files and project MCP entries in `~/.claude.json["projects"][absolute_repo_path]["mcpServers"]`.
|
|
230
|
+
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`. 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 project-local skill folders remain unmanaged until project-scoped installs are supported.
|
|
211
231
|
|
|
212
|
-
|
|
232
|
+
Cursor propagation intentionally stays to repo-local MCP, skills, and agents; it does not copy the shared workspace `AGENTS.md` into child repos.
|
|
213
233
|
|
|
214
234
|
```bash
|
|
215
235
|
code-agnostic workspaces add --name myproject --path ~/code/myproject
|
|
@@ -239,6 +259,12 @@ code-agnostic import apply -a cursor --include mcp --on-conflict overwrite
|
|
|
239
259
|
code-agnostic import plan -a codex -i # interactive TUI picker
|
|
240
260
|
```
|
|
241
261
|
|
|
262
|
+
`import plan` previews what will be copied into the hub; `import apply` writes
|
|
263
|
+
only the selected sections. Conflicts are skipped by default, so rerun with
|
|
264
|
+
`--on-conflict overwrite` only after reviewing the preview. Use `--include`,
|
|
265
|
+
`--exclude`, `--source-root`, and `--follow-symlinks` to narrow what gets
|
|
266
|
+
imported.
|
|
267
|
+
|
|
242
268
|
### CLI conventions
|
|
243
269
|
|
|
244
270
|
All commands use named flags (`-a`, `-w`, `-v`). Singular aliases work too: `app` = `apps`, `workspace` = `workspaces`.
|
|
@@ -16,6 +16,7 @@ from code_agnostic.cli.commands.skills import skills
|
|
|
16
16
|
from code_agnostic.cli.commands.status import status
|
|
17
17
|
from code_agnostic.cli.commands.validate import validate
|
|
18
18
|
from code_agnostic.cli.commands.workspaces import workspaces
|
|
19
|
+
from code_agnostic.errors import SyncAppError
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
@click.group(
|
|
@@ -52,6 +53,9 @@ def main() -> int:
|
|
|
52
53
|
except click.exceptions.Exit as exc:
|
|
53
54
|
code = exc.exit_code
|
|
54
55
|
return code if isinstance(code, int) else 1
|
|
56
|
+
except SyncAppError as exc:
|
|
57
|
+
click.ClickException(str(exc)).show()
|
|
58
|
+
return 2
|
|
55
59
|
except click.ClickException as exc:
|
|
56
60
|
exc.show()
|
|
57
61
|
return 2
|
|
@@ -69,10 +69,13 @@ def serialize_codex_agent(agent: Agent) -> str:
|
|
|
69
69
|
description = agent.metadata.description or agent.metadata.name or agent.name
|
|
70
70
|
|
|
71
71
|
doc = tomlkit.document()
|
|
72
|
-
doc.add("name", agent.metadata.name or agent.name)
|
|
73
|
-
doc.add("description", description)
|
|
72
|
+
doc.add("name", tomlkit.item(agent.metadata.name or agent.name))
|
|
73
|
+
doc.add("description", tomlkit.item(description))
|
|
74
74
|
if agent.metadata.nickname_candidates:
|
|
75
|
-
doc.add(
|
|
75
|
+
doc.add(
|
|
76
|
+
"nickname_candidates",
|
|
77
|
+
tomlkit.item(list(agent.metadata.nickname_candidates)),
|
|
78
|
+
)
|
|
76
79
|
model = agent.metadata.effective_value("codex", "model")
|
|
77
80
|
if model:
|
|
78
81
|
doc.add("model", model)
|
|
@@ -63,7 +63,7 @@ APP_CATALOG: dict[AppId, AppMetadata] = {
|
|
|
63
63
|
toggleable=True,
|
|
64
64
|
importable=True,
|
|
65
65
|
supports_import_agents=True,
|
|
66
|
-
supports_workspace_propagation=
|
|
66
|
+
supports_workspace_propagation=True,
|
|
67
67
|
project_dir_name=CURSOR_PROJECT_DIRNAME,
|
|
68
68
|
config_filename=CURSOR_CONFIG_FILENAME,
|
|
69
69
|
),
|
|
@@ -8,10 +8,6 @@ from code_agnostic.agents.parser import parse_agent
|
|
|
8
8
|
from code_agnostic.apps.app_id import AppId, app_label
|
|
9
9
|
from code_agnostic.apps.claude.config_repository import ClaudeConfigRepository
|
|
10
10
|
from code_agnostic.apps.claude.mapper import ClaudeMCPMapper
|
|
11
|
-
from code_agnostic.apps.common.compiled_planning import (
|
|
12
|
-
find_replaceable_symlink_ancestor,
|
|
13
|
-
plan_owned_compiled_text_action,
|
|
14
|
-
)
|
|
15
11
|
from code_agnostic.apps.common.framework import RegisteredAppConfigService
|
|
16
12
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
17
13
|
from code_agnostic.apps.common.interfaces.repositories import IAppConfigRepository
|
|
@@ -135,7 +131,7 @@ class ClaudeConfigService(RegisteredAppConfigService):
|
|
|
135
131
|
removable_links: list[Path],
|
|
136
132
|
) -> tuple[list[Action], list[Path], list[str]]:
|
|
137
133
|
compiler = ClaudeSkillCompiler()
|
|
138
|
-
return self.
|
|
134
|
+
return self._plan_compiled_text_actions(
|
|
139
135
|
sources=sources,
|
|
140
136
|
target_dir=target_dir,
|
|
141
137
|
scope=scope,
|
|
@@ -173,7 +169,7 @@ class ClaudeConfigService(RegisteredAppConfigService):
|
|
|
173
169
|
agent = parse_agent(source)
|
|
174
170
|
return claude_agent_target_path(target_dir, agent), compiler.compile(agent)
|
|
175
171
|
|
|
176
|
-
return self.
|
|
172
|
+
return self._plan_compiled_text_actions(
|
|
177
173
|
sources=sources,
|
|
178
174
|
target_dir=target_dir,
|
|
179
175
|
scope=scope,
|
|
@@ -186,63 +182,3 @@ class ClaudeConfigService(RegisteredAppConfigService):
|
|
|
186
182
|
update_detail="update compiled claude agent",
|
|
187
183
|
conflict_message="Claude agent sync skipped (conflict): {target}",
|
|
188
184
|
)
|
|
189
|
-
|
|
190
|
-
def _plan_owned_text_actions(
|
|
191
|
-
self,
|
|
192
|
-
*,
|
|
193
|
-
sources: list[Path],
|
|
194
|
-
target_dir: Path,
|
|
195
|
-
scope: str,
|
|
196
|
-
app: str,
|
|
197
|
-
managed_paths: list[Path],
|
|
198
|
-
removable_links: list[Path],
|
|
199
|
-
compile_source,
|
|
200
|
-
create_detail: str,
|
|
201
|
-
noop_detail: str,
|
|
202
|
-
update_detail: str,
|
|
203
|
-
conflict_message: str,
|
|
204
|
-
) -> tuple[list[Action], list[Path], list[str]]:
|
|
205
|
-
managed_path_set = {path.resolve(strict=False) for path in managed_paths}
|
|
206
|
-
removable_link_set = {path.resolve(strict=False) for path in removable_links}
|
|
207
|
-
actions: list[Action] = []
|
|
208
|
-
desired_paths: list[Path] = []
|
|
209
|
-
skipped: list[str] = []
|
|
210
|
-
scheduled_removals: set[Path] = set()
|
|
211
|
-
|
|
212
|
-
for source in sources:
|
|
213
|
-
target, payload = compile_source(source)
|
|
214
|
-
desired_paths.append(target)
|
|
215
|
-
replaceable_symlink = find_replaceable_symlink_ancestor(target, target_dir)
|
|
216
|
-
if (
|
|
217
|
-
replaceable_symlink is not None
|
|
218
|
-
and replaceable_symlink not in scheduled_removals
|
|
219
|
-
):
|
|
220
|
-
scheduled_removals.add(replaceable_symlink)
|
|
221
|
-
removable_link_set.add(replaceable_symlink.resolve(strict=False))
|
|
222
|
-
actions.append(
|
|
223
|
-
Action(
|
|
224
|
-
kind=ActionKind.REMOVE_SYMLINK,
|
|
225
|
-
path=replaceable_symlink,
|
|
226
|
-
status=ActionStatus.REMOVE,
|
|
227
|
-
detail=f"replace compiled {scope} symlink",
|
|
228
|
-
app=app,
|
|
229
|
-
scope=scope,
|
|
230
|
-
)
|
|
231
|
-
)
|
|
232
|
-
action = plan_owned_compiled_text_action(
|
|
233
|
-
target=target,
|
|
234
|
-
payload=payload,
|
|
235
|
-
managed_paths=managed_path_set,
|
|
236
|
-
removable_link_paths=removable_link_set,
|
|
237
|
-
managed_root=target_dir,
|
|
238
|
-
scope=scope,
|
|
239
|
-
app=app,
|
|
240
|
-
create_detail=create_detail,
|
|
241
|
-
noop_detail=noop_detail,
|
|
242
|
-
update_detail=update_detail,
|
|
243
|
-
)
|
|
244
|
-
actions.append(action)
|
|
245
|
-
if action.status == ActionStatus.CONFLICT:
|
|
246
|
-
skipped.append(conflict_message.format(target=target))
|
|
247
|
-
|
|
248
|
-
return actions, desired_paths, skipped
|
|
@@ -355,6 +355,22 @@
|
|
|
355
355
|
},
|
|
356
356
|
"type": "object"
|
|
357
357
|
},
|
|
358
|
+
"CodeModeConfigToml": {
|
|
359
|
+
"additionalProperties": false,
|
|
360
|
+
"properties": {
|
|
361
|
+
"enabled": {
|
|
362
|
+
"type": "boolean"
|
|
363
|
+
},
|
|
364
|
+
"excluded_tool_namespaces": {
|
|
365
|
+
"description": "Exact tool namespaces to omit from the code-mode nested tool surface.",
|
|
366
|
+
"items": {
|
|
367
|
+
"type": "string"
|
|
368
|
+
},
|
|
369
|
+
"type": "array"
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
"type": "object"
|
|
373
|
+
},
|
|
358
374
|
"ConfigProfile": {
|
|
359
375
|
"additionalProperties": false,
|
|
360
376
|
"description": "Collection of common configuration options that a user can define as a unit in `config.toml`.",
|
|
@@ -410,7 +426,7 @@
|
|
|
410
426
|
"type": "boolean"
|
|
411
427
|
},
|
|
412
428
|
"code_mode": {
|
|
413
|
-
"
|
|
429
|
+
"$ref": "#/definitions/FeatureToml_for_CodeModeConfigToml"
|
|
414
430
|
},
|
|
415
431
|
"code_mode_only": {
|
|
416
432
|
"type": "boolean"
|
|
@@ -556,9 +572,6 @@
|
|
|
556
572
|
"request_rule": {
|
|
557
573
|
"type": "boolean"
|
|
558
574
|
},
|
|
559
|
-
"responses_websocket_response_processed": {
|
|
560
|
-
"type": "boolean"
|
|
561
|
-
},
|
|
562
575
|
"responses_websockets": {
|
|
563
576
|
"type": "boolean"
|
|
564
577
|
},
|
|
@@ -601,6 +614,9 @@
|
|
|
601
614
|
"terminal_resize_reflow": {
|
|
602
615
|
"type": "boolean"
|
|
603
616
|
},
|
|
617
|
+
"terminal_visualization_instructions": {
|
|
618
|
+
"type": "boolean"
|
|
619
|
+
},
|
|
604
620
|
"tool_call_mcp_elicitation": {
|
|
605
621
|
"type": "boolean"
|
|
606
622
|
},
|
|
@@ -830,6 +846,16 @@
|
|
|
830
846
|
}
|
|
831
847
|
]
|
|
832
848
|
},
|
|
849
|
+
"FeatureToml_for_CodeModeConfigToml": {
|
|
850
|
+
"anyOf": [
|
|
851
|
+
{
|
|
852
|
+
"type": "boolean"
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
"$ref": "#/definitions/CodeModeConfigToml"
|
|
856
|
+
}
|
|
857
|
+
]
|
|
858
|
+
},
|
|
833
859
|
"FeatureToml_for_MultiAgentV2ConfigToml": {
|
|
834
860
|
"anyOf": [
|
|
835
861
|
{
|
|
@@ -2480,15 +2506,8 @@
|
|
|
2480
2506
|
"type": "string"
|
|
2481
2507
|
},
|
|
2482
2508
|
"ReasoningEffort": {
|
|
2483
|
-
"description": "
|
|
2484
|
-
"
|
|
2485
|
-
"none",
|
|
2486
|
-
"minimal",
|
|
2487
|
-
"low",
|
|
2488
|
-
"medium",
|
|
2489
|
-
"high",
|
|
2490
|
-
"xhigh"
|
|
2491
|
-
],
|
|
2509
|
+
"description": "A non-empty reasoning effort value advertised by the model.",
|
|
2510
|
+
"minLength": 1,
|
|
2492
2511
|
"type": "string"
|
|
2493
2512
|
},
|
|
2494
2513
|
"ReasoningSummary": {
|
|
@@ -4533,7 +4552,7 @@
|
|
|
4533
4552
|
"type": "boolean"
|
|
4534
4553
|
},
|
|
4535
4554
|
"code_mode": {
|
|
4536
|
-
"
|
|
4555
|
+
"$ref": "#/definitions/FeatureToml_for_CodeModeConfigToml"
|
|
4537
4556
|
},
|
|
4538
4557
|
"code_mode_only": {
|
|
4539
4558
|
"type": "boolean"
|
|
@@ -4679,9 +4698,6 @@
|
|
|
4679
4698
|
"request_rule": {
|
|
4680
4699
|
"type": "boolean"
|
|
4681
4700
|
},
|
|
4682
|
-
"responses_websocket_response_processed": {
|
|
4683
|
-
"type": "boolean"
|
|
4684
|
-
},
|
|
4685
4701
|
"responses_websockets": {
|
|
4686
4702
|
"type": "boolean"
|
|
4687
4703
|
},
|
|
@@ -4724,6 +4740,9 @@
|
|
|
4724
4740
|
"terminal_resize_reflow": {
|
|
4725
4741
|
"type": "boolean"
|
|
4726
4742
|
},
|
|
4743
|
+
"terminal_visualization_instructions": {
|
|
4744
|
+
"type": "boolean"
|
|
4745
|
+
},
|
|
4727
4746
|
"tool_call_mcp_elicitation": {
|
|
4728
4747
|
"type": "boolean"
|
|
4729
4748
|
},
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from code_agnostic.generated_artifacts import (
|
|
4
|
+
ArtifactKind,
|
|
5
|
+
GeneratedArtifact,
|
|
6
|
+
OwnershipPolicy,
|
|
7
|
+
plan_generated_artifact,
|
|
8
|
+
)
|
|
9
|
+
from code_agnostic.models import Action
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def find_replaceable_symlink_ancestor(target: Path, managed_root: Path) -> Path | None:
|
|
13
|
+
current = target
|
|
14
|
+
while True:
|
|
15
|
+
if current.is_symlink() and (
|
|
16
|
+
current == managed_root or current.is_relative_to(managed_root)
|
|
17
|
+
):
|
|
18
|
+
return current
|
|
19
|
+
if current.parent == current:
|
|
20
|
+
return None
|
|
21
|
+
current = current.parent
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def plan_compiled_text_action(
|
|
25
|
+
*,
|
|
26
|
+
target: Path,
|
|
27
|
+
payload: str,
|
|
28
|
+
managed_paths: set[Path],
|
|
29
|
+
removable_link_paths: set[Path] | None = None,
|
|
30
|
+
managed_root: Path | None = None,
|
|
31
|
+
scope: str,
|
|
32
|
+
app: str,
|
|
33
|
+
create_detail: str,
|
|
34
|
+
noop_detail: str,
|
|
35
|
+
update_detail: str,
|
|
36
|
+
conflict_detail: str = "non-managed path exists",
|
|
37
|
+
) -> Action:
|
|
38
|
+
return _plan_text_action(
|
|
39
|
+
ownership=OwnershipPolicy.MANAGED_REPLACE,
|
|
40
|
+
target=target,
|
|
41
|
+
payload=payload,
|
|
42
|
+
managed_paths=managed_paths,
|
|
43
|
+
removable_link_paths=removable_link_paths,
|
|
44
|
+
managed_root=managed_root,
|
|
45
|
+
scope=scope,
|
|
46
|
+
app=app,
|
|
47
|
+
create_detail=create_detail,
|
|
48
|
+
noop_detail=noop_detail,
|
|
49
|
+
update_detail=update_detail,
|
|
50
|
+
conflict_detail=conflict_detail,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def plan_owned_compiled_text_action(
|
|
55
|
+
*,
|
|
56
|
+
target: Path,
|
|
57
|
+
payload: str,
|
|
58
|
+
managed_paths: set[Path],
|
|
59
|
+
removable_link_paths: set[Path] | None = None,
|
|
60
|
+
managed_root: Path | None = None,
|
|
61
|
+
scope: str,
|
|
62
|
+
app: str,
|
|
63
|
+
create_detail: str,
|
|
64
|
+
noop_detail: str,
|
|
65
|
+
update_detail: str,
|
|
66
|
+
conflict_detail: str = "non-managed path exists",
|
|
67
|
+
) -> Action:
|
|
68
|
+
return _plan_text_action(
|
|
69
|
+
ownership=OwnershipPolicy.OWNED_ONLY,
|
|
70
|
+
target=target,
|
|
71
|
+
payload=payload,
|
|
72
|
+
managed_paths=managed_paths,
|
|
73
|
+
removable_link_paths=removable_link_paths,
|
|
74
|
+
managed_root=managed_root,
|
|
75
|
+
scope=scope,
|
|
76
|
+
app=app,
|
|
77
|
+
create_detail=create_detail,
|
|
78
|
+
noop_detail=noop_detail,
|
|
79
|
+
update_detail=update_detail,
|
|
80
|
+
conflict_detail=conflict_detail,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _plan_text_action(
|
|
85
|
+
*,
|
|
86
|
+
ownership: OwnershipPolicy,
|
|
87
|
+
target: Path,
|
|
88
|
+
payload: str,
|
|
89
|
+
managed_paths: set[Path],
|
|
90
|
+
removable_link_paths: set[Path] | None,
|
|
91
|
+
managed_root: Path | None,
|
|
92
|
+
scope: str,
|
|
93
|
+
app: str,
|
|
94
|
+
create_detail: str,
|
|
95
|
+
noop_detail: str,
|
|
96
|
+
update_detail: str,
|
|
97
|
+
conflict_detail: str,
|
|
98
|
+
) -> Action:
|
|
99
|
+
return plan_generated_artifact(
|
|
100
|
+
GeneratedArtifact(
|
|
101
|
+
path=target,
|
|
102
|
+
kind=ArtifactKind.TEXT,
|
|
103
|
+
payload=payload,
|
|
104
|
+
ownership=ownership,
|
|
105
|
+
managed_root=managed_root,
|
|
106
|
+
scope=scope,
|
|
107
|
+
app=app,
|
|
108
|
+
create_detail=create_detail,
|
|
109
|
+
noop_detail=noop_detail,
|
|
110
|
+
update_detail=update_detail,
|
|
111
|
+
conflict_detail=conflict_detail,
|
|
112
|
+
),
|
|
113
|
+
managed_paths=managed_paths,
|
|
114
|
+
removable_link_paths=removable_link_paths,
|
|
115
|
+
)
|
{code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/repositories.py
RENAMED
|
@@ -20,6 +20,16 @@ class IAppConfigRepository(ABC):
|
|
|
20
20
|
def config_path(self) -> Path:
|
|
21
21
|
raise NotImplementedError
|
|
22
22
|
|
|
23
|
+
@property
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def skills_dir(self) -> Path:
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def agents_dir(self) -> Path:
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
23
33
|
@abstractmethod
|
|
24
34
|
def load_config(self) -> dict[str, Any]:
|
|
25
35
|
raise NotImplementedError
|
{code_agnostic-0.3.9 → code_agnostic-0.3.10}/code_agnostic/apps/common/interfaces/service.py
RENAMED
|
@@ -6,7 +6,6 @@ from typing import Any
|
|
|
6
6
|
from code_agnostic.apps.app_id import AppId, app_scope
|
|
7
7
|
from code_agnostic.apps.common.compiled_planning import (
|
|
8
8
|
find_replaceable_symlink_ancestor,
|
|
9
|
-
plan_compiled_text_action,
|
|
10
9
|
)
|
|
11
10
|
from code_agnostic.apps.common.interfaces.mapper import IAppMCPMapper
|
|
12
11
|
from code_agnostic.apps.common.interfaces.repositories import IAppConfigRepository
|
|
@@ -18,6 +17,12 @@ from code_agnostic.apps.common.symlink_planning import (
|
|
|
18
17
|
plan_stale_files_group,
|
|
19
18
|
plan_stale_group,
|
|
20
19
|
)
|
|
20
|
+
from code_agnostic.generated_artifacts import (
|
|
21
|
+
ArtifactKind,
|
|
22
|
+
GeneratedArtifact,
|
|
23
|
+
OwnershipPolicy,
|
|
24
|
+
plan_generated_artifact,
|
|
25
|
+
)
|
|
21
26
|
from code_agnostic.models import Action, ActionKind, ActionStatus, SyncPlan
|
|
22
27
|
|
|
23
28
|
|
|
@@ -92,7 +97,10 @@ class IAppConfigService(ABC):
|
|
|
92
97
|
raise NotImplementedError
|
|
93
98
|
|
|
94
99
|
def agent_action_removable_links(self, removable_links: list[Path]) -> list[Path]:
|
|
95
|
-
return
|
|
100
|
+
return removable_links
|
|
101
|
+
|
|
102
|
+
def compiled_resource_ownership_policy(self) -> OwnershipPolicy:
|
|
103
|
+
return OwnershipPolicy.OWNED_ONLY
|
|
96
104
|
|
|
97
105
|
@staticmethod
|
|
98
106
|
def _normalize_managed_group(value: Any) -> dict[str, Any]:
|
|
@@ -119,14 +127,18 @@ class IAppConfigService(ABC):
|
|
|
119
127
|
desired_paths: list[Path] = []
|
|
120
128
|
skipped: list[str] = []
|
|
121
129
|
scheduled_removals: set[Path] = set()
|
|
130
|
+
ownership = self.compiled_resource_ownership_policy()
|
|
122
131
|
|
|
123
132
|
for source in sources:
|
|
124
133
|
target, payload = compile_source(source)
|
|
125
134
|
desired_paths.append(target)
|
|
126
135
|
replaceable_symlink = find_replaceable_symlink_ancestor(target, target_dir)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
can_replace_symlink = replaceable_symlink is not None and (
|
|
137
|
+
ownership != OwnershipPolicy.OWNED_ONLY
|
|
138
|
+
or replaceable_symlink.resolve(strict=False) in removable_link_set
|
|
139
|
+
)
|
|
140
|
+
if replaceable_symlink is not None and (
|
|
141
|
+
can_replace_symlink and replaceable_symlink not in scheduled_removals
|
|
130
142
|
):
|
|
131
143
|
scheduled_removals.add(replaceable_symlink)
|
|
132
144
|
removable_link_set.add(replaceable_symlink.resolve(strict=False))
|
|
@@ -140,17 +152,21 @@ class IAppConfigService(ABC):
|
|
|
140
152
|
scope=scope,
|
|
141
153
|
)
|
|
142
154
|
)
|
|
143
|
-
action =
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
action = plan_generated_artifact(
|
|
156
|
+
GeneratedArtifact(
|
|
157
|
+
path=target,
|
|
158
|
+
kind=ArtifactKind.TEXT,
|
|
159
|
+
payload=payload,
|
|
160
|
+
ownership=ownership,
|
|
161
|
+
managed_root=target_dir,
|
|
162
|
+
scope=scope,
|
|
163
|
+
app=app,
|
|
164
|
+
create_detail=create_detail,
|
|
165
|
+
noop_detail=noop_detail,
|
|
166
|
+
update_detail=update_detail,
|
|
167
|
+
),
|
|
146
168
|
managed_paths=managed_path_set,
|
|
147
169
|
removable_link_paths=removable_link_set,
|
|
148
|
-
managed_root=target_dir,
|
|
149
|
-
scope=scope,
|
|
150
|
-
app=app,
|
|
151
|
-
create_detail=create_detail,
|
|
152
|
-
noop_detail=noop_detail,
|
|
153
|
-
update_detail=update_detail,
|
|
154
170
|
)
|
|
155
171
|
actions.append(action)
|
|
156
172
|
if action.status == ActionStatus.CONFLICT:
|