cldpm 0.1.0__py3-none-any.whl
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.
- cldpm/__init__.py +12 -0
- cldpm/__main__.py +6 -0
- cldpm/_banner.py +99 -0
- cldpm/cli.py +81 -0
- cldpm/commands/__init__.py +12 -0
- cldpm/commands/add.py +206 -0
- cldpm/commands/clone.py +184 -0
- cldpm/commands/create.py +418 -0
- cldpm/commands/get.py +375 -0
- cldpm/commands/init.py +331 -0
- cldpm/commands/link.py +320 -0
- cldpm/commands/remove.py +289 -0
- cldpm/commands/sync.py +91 -0
- cldpm/core/__init__.py +26 -0
- cldpm/core/config.py +182 -0
- cldpm/core/linker.py +265 -0
- cldpm/core/resolver.py +291 -0
- cldpm/schemas/__init__.py +13 -0
- cldpm/schemas/cldpm.py +32 -0
- cldpm/schemas/component.py +24 -0
- cldpm/schemas/project.py +42 -0
- cldpm/templates/CLAUDE.md.j2 +22 -0
- cldpm/templates/ROOT_CLAUDE.md.j2 +34 -0
- cldpm/templates/agent.md.j2 +22 -0
- cldpm/templates/gitignore.j2 +43 -0
- cldpm/templates/hook.md.j2 +20 -0
- cldpm/templates/rule.md.j2 +33 -0
- cldpm/templates/skill.md.j2 +15 -0
- cldpm/utils/__init__.py +27 -0
- cldpm/utils/fs.py +97 -0
- cldpm/utils/git.py +169 -0
- cldpm/utils/output.py +133 -0
- cldpm-0.1.0.dist-info/METADATA +15 -0
- cldpm-0.1.0.dist-info/RECORD +37 -0
- cldpm-0.1.0.dist-info/WHEEL +4 -0
- cldpm-0.1.0.dist-info/entry_points.txt +2 -0
- cldpm-0.1.0.dist-info/licenses/LICENSE +21 -0
cldpm/core/resolver.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Project resolution for CLDPM."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
from ..utils.fs import find_repo_root
|
|
7
|
+
from .config import (
|
|
8
|
+
get_project_path,
|
|
9
|
+
load_cldpm_config,
|
|
10
|
+
load_component_metadata,
|
|
11
|
+
load_project_config,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_component(
|
|
16
|
+
component_type: str, component_name: str, shared_dir: Path
|
|
17
|
+
) -> Optional[dict[str, Any]]:
|
|
18
|
+
"""Resolve a single component from the shared directory.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
component_type: Type of component (skills, agents, hooks, rules).
|
|
22
|
+
component_name: Name of the component.
|
|
23
|
+
shared_dir: Path to the shared directory.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dictionary with component info, or None if not found.
|
|
27
|
+
"""
|
|
28
|
+
component_path = shared_dir / component_type / component_name
|
|
29
|
+
|
|
30
|
+
if not component_path.exists():
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
# Get list of files in the component
|
|
34
|
+
if component_path.is_dir():
|
|
35
|
+
files = [f.name for f in component_path.iterdir() if f.is_file()]
|
|
36
|
+
else:
|
|
37
|
+
files = [component_path.name]
|
|
38
|
+
component_path = component_path.parent
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"name": component_name,
|
|
42
|
+
"type": "shared",
|
|
43
|
+
"sourcePath": str(component_path.relative_to(shared_dir.parent)),
|
|
44
|
+
"files": sorted(files),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def resolve_local_component(
|
|
49
|
+
component_type: str, component_name: str, project_path: Path
|
|
50
|
+
) -> Optional[dict[str, Any]]:
|
|
51
|
+
"""Resolve a single local component from the project directory.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
component_type: Type of component (skills, agents, hooks, rules).
|
|
55
|
+
component_name: Name of the component.
|
|
56
|
+
project_path: Path to the project directory.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary with component info, or None if not found.
|
|
60
|
+
"""
|
|
61
|
+
component_path = project_path / ".claude" / component_type / component_name
|
|
62
|
+
|
|
63
|
+
if not component_path.exists() or component_path.is_symlink():
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
# Get list of files in the component
|
|
67
|
+
if component_path.is_dir():
|
|
68
|
+
files = [f.name for f in component_path.iterdir() if f.is_file()]
|
|
69
|
+
else:
|
|
70
|
+
files = [component_path.name]
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"name": component_name,
|
|
74
|
+
"type": "local",
|
|
75
|
+
"sourcePath": f".claude/{component_type}/{component_name}",
|
|
76
|
+
"files": sorted(files),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_local_components_in_project(project_path: Path) -> dict[str, list[dict[str, Any]]]:
|
|
81
|
+
"""Get all local (non-symlinked) components in a project.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
project_path: Path to the project directory.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Dictionary mapping component types to lists of component info.
|
|
88
|
+
"""
|
|
89
|
+
claude_dir = project_path / ".claude"
|
|
90
|
+
result = {"skills": [], "agents": [], "hooks": [], "rules": []}
|
|
91
|
+
|
|
92
|
+
for component_type in result.keys():
|
|
93
|
+
type_dir = claude_dir / component_type
|
|
94
|
+
if not type_dir.exists():
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
for item in type_dir.iterdir():
|
|
98
|
+
# Skip .gitignore and symlinks
|
|
99
|
+
if item.name == ".gitignore" or item.is_symlink():
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
component = resolve_local_component(component_type, item.name, project_path)
|
|
103
|
+
if component:
|
|
104
|
+
result[component_type].append(component)
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def resolve_project(
|
|
110
|
+
project_path_or_name: str, repo_root: Optional[Path] = None
|
|
111
|
+
) -> dict[str, Any]:
|
|
112
|
+
"""Resolve a project and all its dependencies.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
project_path_or_name: Path to the project directory or project name.
|
|
116
|
+
repo_root: Path to the repo root. If None, will search for it.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Dictionary with resolved project info.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
FileNotFoundError: If the project is not found.
|
|
123
|
+
"""
|
|
124
|
+
if repo_root is None:
|
|
125
|
+
repo_root = find_repo_root()
|
|
126
|
+
if repo_root is None:
|
|
127
|
+
raise FileNotFoundError("Not in a CLDPM mono repo")
|
|
128
|
+
|
|
129
|
+
# Try to resolve as path first
|
|
130
|
+
project_path = Path(project_path_or_name)
|
|
131
|
+
if not project_path.is_absolute():
|
|
132
|
+
# Check if it's a relative path
|
|
133
|
+
full_path = repo_root / project_path_or_name
|
|
134
|
+
if full_path.exists() and (full_path / "project.json").exists():
|
|
135
|
+
project_path = full_path
|
|
136
|
+
else:
|
|
137
|
+
# Try as project name
|
|
138
|
+
project_path = get_project_path(project_path_or_name, repo_root)
|
|
139
|
+
|
|
140
|
+
if project_path is None or not project_path.exists():
|
|
141
|
+
raise FileNotFoundError(f"Project not found: {project_path_or_name}")
|
|
142
|
+
|
|
143
|
+
cldpm_config = load_cldpm_config(repo_root)
|
|
144
|
+
project_config = load_project_config(project_path)
|
|
145
|
+
|
|
146
|
+
shared_dir = repo_root / cldpm_config.shared_dir
|
|
147
|
+
|
|
148
|
+
# Resolve shared dependencies
|
|
149
|
+
shared = {"skills": [], "agents": [], "hooks": [], "rules": []}
|
|
150
|
+
|
|
151
|
+
dep_types = [
|
|
152
|
+
("skills", project_config.dependencies.skills),
|
|
153
|
+
("agents", project_config.dependencies.agents),
|
|
154
|
+
("hooks", project_config.dependencies.hooks),
|
|
155
|
+
("rules", project_config.dependencies.rules),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
for dep_type, deps in dep_types:
|
|
159
|
+
for dep_name in deps:
|
|
160
|
+
component = resolve_component(dep_type, dep_name, shared_dir)
|
|
161
|
+
if component:
|
|
162
|
+
shared[dep_type].append(component)
|
|
163
|
+
|
|
164
|
+
# Get local components
|
|
165
|
+
local = get_local_components_in_project(project_path)
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
"name": project_config.name,
|
|
169
|
+
"path": str(project_path.resolve()),
|
|
170
|
+
"config": project_config.model_dump(exclude_none=True),
|
|
171
|
+
"shared": shared,
|
|
172
|
+
"local": local,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def list_shared_components(
|
|
177
|
+
repo_root: Optional[Path] = None,
|
|
178
|
+
) -> dict[str, list[str]]:
|
|
179
|
+
"""List all shared components in the mono repo.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
repo_root: Path to the repo root. If None, will search for it.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Dictionary mapping component types to lists of component names.
|
|
186
|
+
"""
|
|
187
|
+
if repo_root is None:
|
|
188
|
+
repo_root = find_repo_root()
|
|
189
|
+
if repo_root is None:
|
|
190
|
+
return {"skills": [], "agents": [], "hooks": [], "rules": []}
|
|
191
|
+
|
|
192
|
+
cldpm_config = load_cldpm_config(repo_root)
|
|
193
|
+
shared_dir = repo_root / cldpm_config.shared_dir
|
|
194
|
+
|
|
195
|
+
result = {}
|
|
196
|
+
for component_type in ["skills", "agents", "hooks", "rules"]:
|
|
197
|
+
type_dir = shared_dir / component_type
|
|
198
|
+
if type_dir.exists():
|
|
199
|
+
result[component_type] = sorted(
|
|
200
|
+
[d.name for d in type_dir.iterdir() if d.is_dir()]
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
result[component_type] = []
|
|
204
|
+
|
|
205
|
+
return result
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def resolve_component_dependencies(
|
|
209
|
+
comp_type: str,
|
|
210
|
+
comp_name: str,
|
|
211
|
+
repo_root: Path,
|
|
212
|
+
resolved: Optional[set[str]] = None,
|
|
213
|
+
) -> list[tuple[str, str]]:
|
|
214
|
+
"""Recursively resolve all dependencies of a component.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
comp_type: Component type (skills, agents, hooks, rules).
|
|
218
|
+
comp_name: Component name.
|
|
219
|
+
repo_root: Path to the repo root.
|
|
220
|
+
resolved: Set of already resolved components (for circular detection).
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
List of (comp_type, comp_name) tuples for all dependencies.
|
|
224
|
+
|
|
225
|
+
Raises:
|
|
226
|
+
ValueError: If circular dependency detected.
|
|
227
|
+
"""
|
|
228
|
+
if resolved is None:
|
|
229
|
+
resolved = set()
|
|
230
|
+
|
|
231
|
+
component_key = f"{comp_type}:{comp_name}"
|
|
232
|
+
|
|
233
|
+
if component_key in resolved:
|
|
234
|
+
return [] # Already resolved
|
|
235
|
+
|
|
236
|
+
resolved.add(component_key)
|
|
237
|
+
|
|
238
|
+
metadata = load_component_metadata(comp_type, comp_name, repo_root)
|
|
239
|
+
if metadata is None:
|
|
240
|
+
return []
|
|
241
|
+
|
|
242
|
+
dependencies = []
|
|
243
|
+
|
|
244
|
+
# Process each dependency type
|
|
245
|
+
for dep_type in ["skills", "agents", "hooks", "rules"]:
|
|
246
|
+
dep_list = getattr(metadata.dependencies, dep_type, [])
|
|
247
|
+
for dep_name in dep_list:
|
|
248
|
+
dep_key = f"{dep_type}:{dep_name}"
|
|
249
|
+
|
|
250
|
+
# Check for circular dependency in current resolution path
|
|
251
|
+
if dep_key in resolved:
|
|
252
|
+
# It's only circular if we're currently resolving it
|
|
253
|
+
# (not if it was resolved in a different branch)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
dependencies.append((dep_type, dep_name))
|
|
257
|
+
|
|
258
|
+
# Recursively resolve sub-dependencies
|
|
259
|
+
sub_deps = resolve_component_dependencies(
|
|
260
|
+
dep_type, dep_name, repo_root, resolved.copy()
|
|
261
|
+
)
|
|
262
|
+
dependencies.extend(sub_deps)
|
|
263
|
+
|
|
264
|
+
return dependencies
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def get_all_dependencies_for_component(
|
|
268
|
+
comp_type: str, comp_name: str, repo_root: Path
|
|
269
|
+
) -> dict[str, list[str]]:
|
|
270
|
+
"""Get all dependencies for a component, organized by type.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
comp_type: Component type (skills, agents, hooks, rules).
|
|
274
|
+
comp_name: Component name.
|
|
275
|
+
repo_root: Path to the repo root.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Dictionary mapping component types to lists of component names.
|
|
279
|
+
"""
|
|
280
|
+
deps = resolve_component_dependencies(comp_type, comp_name, repo_root)
|
|
281
|
+
|
|
282
|
+
result: dict[str, list[str]] = {"skills": [], "agents": [], "hooks": [], "rules": []}
|
|
283
|
+
|
|
284
|
+
seen = set()
|
|
285
|
+
for dep_type, dep_name in deps:
|
|
286
|
+
key = f"{dep_type}:{dep_name}"
|
|
287
|
+
if key not in seen:
|
|
288
|
+
seen.add(key)
|
|
289
|
+
result[dep_type].append(dep_name)
|
|
290
|
+
|
|
291
|
+
return result
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Pydantic schemas for CLDPM configuration files."""
|
|
2
|
+
|
|
3
|
+
from .cldpm import CldpmConfig
|
|
4
|
+
from .component import ComponentDependencies, ComponentMetadata
|
|
5
|
+
from .project import ProjectConfig, ProjectDependencies
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CldpmConfig",
|
|
9
|
+
"ComponentDependencies",
|
|
10
|
+
"ComponentMetadata",
|
|
11
|
+
"ProjectConfig",
|
|
12
|
+
"ProjectDependencies",
|
|
13
|
+
]
|
cldpm/schemas/cldpm.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Pydantic model for cldpm.json (root configuration)."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CldpmConfig(BaseModel):
|
|
7
|
+
"""Root configuration for a CLDPM mono repo."""
|
|
8
|
+
|
|
9
|
+
name: str = Field(..., description="Name of the mono repo")
|
|
10
|
+
version: str = Field(default="1.0.0", description="Version of the mono repo")
|
|
11
|
+
projects_dir: str = Field(
|
|
12
|
+
default="projects",
|
|
13
|
+
alias="projectsDir",
|
|
14
|
+
description="Directory containing projects",
|
|
15
|
+
)
|
|
16
|
+
shared_dir: str = Field(
|
|
17
|
+
default="shared",
|
|
18
|
+
alias="sharedDir",
|
|
19
|
+
description="Directory containing shared components",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
model_config = {
|
|
23
|
+
"populate_by_name": True,
|
|
24
|
+
"json_schema_extra": {
|
|
25
|
+
"example": {
|
|
26
|
+
"name": "my-monorepo",
|
|
27
|
+
"version": "1.0.0",
|
|
28
|
+
"projectsDir": "projects",
|
|
29
|
+
"sharedDir": "shared",
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Pydantic schema for component metadata."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ComponentDependencies(BaseModel):
|
|
9
|
+
"""Dependencies for a component."""
|
|
10
|
+
|
|
11
|
+
skills: list[str] = Field(default_factory=list)
|
|
12
|
+
agents: list[str] = Field(default_factory=list)
|
|
13
|
+
hooks: list[str] = Field(default_factory=list)
|
|
14
|
+
rules: list[str] = Field(default_factory=list)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ComponentMetadata(BaseModel):
|
|
18
|
+
"""Metadata for a shared component (skill, agent, hook, rule)."""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(extra="allow")
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
description: Optional[str] = None
|
|
24
|
+
dependencies: ComponentDependencies = Field(default_factory=ComponentDependencies)
|
cldpm/schemas/project.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Pydantic model for project.json (project configuration)."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProjectDependencies(BaseModel):
|
|
9
|
+
"""Dependencies for a project."""
|
|
10
|
+
|
|
11
|
+
skills: list[str] = Field(default_factory=list, description="List of skill names")
|
|
12
|
+
agents: list[str] = Field(default_factory=list, description="List of agent names")
|
|
13
|
+
hooks: list[str] = Field(default_factory=list, description="List of hook names")
|
|
14
|
+
rules: list[str] = Field(default_factory=list, description="List of rule names")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProjectConfig(BaseModel):
|
|
18
|
+
"""Configuration for a CLDPM project."""
|
|
19
|
+
|
|
20
|
+
name: str = Field(..., description="Name of the project")
|
|
21
|
+
description: Optional[str] = Field(
|
|
22
|
+
default=None, description="Description of the project"
|
|
23
|
+
)
|
|
24
|
+
dependencies: ProjectDependencies = Field(
|
|
25
|
+
default_factory=ProjectDependencies,
|
|
26
|
+
description="Project dependencies on shared components",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
model_config = {
|
|
30
|
+
"json_schema_extra": {
|
|
31
|
+
"example": {
|
|
32
|
+
"name": "my-project",
|
|
33
|
+
"description": "Project description",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"skills": ["my-skill"],
|
|
36
|
+
"agents": ["my-agent"],
|
|
37
|
+
"hooks": ["my-hook"],
|
|
38
|
+
"rules": ["my-rule"],
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
{% if description %}
|
|
4
|
+
{{ description }}
|
|
5
|
+
{% endif %}
|
|
6
|
+
|
|
7
|
+
## Project Overview
|
|
8
|
+
|
|
9
|
+
This is a CLDPM-managed project. Dependencies are managed via `project.json` and symlinked from the shared directory.
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
1. Review the project configuration in `project.json`
|
|
14
|
+
2. Check available skills in `.claude/skills/`
|
|
15
|
+
3. Check available agents in `.claude/agents/`
|
|
16
|
+
|
|
17
|
+
## Project Structure
|
|
18
|
+
|
|
19
|
+
- `project.json` - Project configuration and dependencies
|
|
20
|
+
- `CLAUDE.md` - This file, project instructions
|
|
21
|
+
- `.claude/` - Claude Code configuration and components
|
|
22
|
+
- `outputs/` - Project outputs and artifacts
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# {{ repo_name }}
|
|
2
|
+
|
|
3
|
+
This is a CLDPM-managed mono repo containing multiple Claude Code projects.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
- `projects/` - Individual projects
|
|
8
|
+
- `shared/` - Shared components (skills, agents, hooks, rules)
|
|
9
|
+
- `cldpm.json` - Mono repo configuration
|
|
10
|
+
|
|
11
|
+
## Using CLDPM
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Create a new project
|
|
15
|
+
cldpm create project my-project
|
|
16
|
+
|
|
17
|
+
# Add shared components to a project
|
|
18
|
+
cldpm add skill:my-skill --to my-project
|
|
19
|
+
|
|
20
|
+
# Get project info
|
|
21
|
+
cldpm get my-project
|
|
22
|
+
|
|
23
|
+
# Sync symlinks after git clone
|
|
24
|
+
cldpm sync --all
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Shared Components
|
|
28
|
+
|
|
29
|
+
Shared components are stored in the `shared/` directory and can be referenced by multiple projects:
|
|
30
|
+
|
|
31
|
+
- `shared/skills/` - Reusable skills
|
|
32
|
+
- `shared/agents/` - Reusable agent configurations
|
|
33
|
+
- `shared/hooks/` - Reusable hooks
|
|
34
|
+
- `shared/rules/` - Reusable rules
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# {{ name }}
|
|
2
|
+
|
|
3
|
+
{% if description %}{{ description }}{% else %}A shared agent.{% endif %}
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Describe what this agent does and its purpose.
|
|
8
|
+
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
- Capability 1
|
|
12
|
+
- Capability 2
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
<!-- Add your agent instructions here -->
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. Step 1
|
|
21
|
+
2. Step 2
|
|
22
|
+
3. Step 3
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# CLDPM Note: Shared component symlinks are managed per-directory
|
|
2
|
+
# Each .claude/{skills,agents,hooks,rules}/ has its own .gitignore
|
|
3
|
+
# that only ignores symlinked shared components.
|
|
4
|
+
# Project-specific components in those directories ARE committed.
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
*.so
|
|
11
|
+
.Python
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
|
|
28
|
+
# Virtual environments
|
|
29
|
+
.env
|
|
30
|
+
.venv
|
|
31
|
+
env/
|
|
32
|
+
venv/
|
|
33
|
+
ENV/
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.idea/
|
|
37
|
+
.vscode/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
|
|
41
|
+
# OS
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# {{ name }}
|
|
2
|
+
|
|
3
|
+
{% if description %}{{ description }}{% else %}A shared hook.{% endif %}
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Describe when this hook triggers and what it does.
|
|
8
|
+
|
|
9
|
+
## Trigger
|
|
10
|
+
|
|
11
|
+
- Event: `<event-name>`
|
|
12
|
+
- Conditions: Describe when this hook should run
|
|
13
|
+
|
|
14
|
+
## Actions
|
|
15
|
+
|
|
16
|
+
<!-- Describe what actions this hook performs -->
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
<!-- Document any configuration options -->
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# {{ name }}
|
|
2
|
+
|
|
3
|
+
{% if description %}{{ description }}{% else %}A shared rule.{% endif %}
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Describe the purpose of this rule.
|
|
8
|
+
|
|
9
|
+
## Guidelines
|
|
10
|
+
|
|
11
|
+
### Do
|
|
12
|
+
|
|
13
|
+
- Guideline 1
|
|
14
|
+
- Guideline 2
|
|
15
|
+
|
|
16
|
+
### Don't
|
|
17
|
+
|
|
18
|
+
- Anti-pattern 1
|
|
19
|
+
- Anti-pattern 2
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
### Good
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Example of following this rule
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Bad
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Example of violating this rule
|
|
33
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{ name }}
|
|
2
|
+
|
|
3
|
+
{% if description %}{{ description }}{% else %}A shared skill.{% endif %}
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Describe what this skill does and when to use it.
|
|
8
|
+
|
|
9
|
+
## Instructions
|
|
10
|
+
|
|
11
|
+
<!-- Add your skill instructions here -->
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
<!-- Add usage examples here -->
|
cldpm/utils/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Utility modules for CLDPM."""
|
|
2
|
+
|
|
3
|
+
from .fs import find_repo_root, ensure_dir, is_symlink
|
|
4
|
+
from .git import (
|
|
5
|
+
get_github_token,
|
|
6
|
+
parse_repo_url,
|
|
7
|
+
clone_repo,
|
|
8
|
+
clone_to_temp,
|
|
9
|
+
cleanup_temp_dir,
|
|
10
|
+
)
|
|
11
|
+
from .output import console, print_error, print_success, print_warning, print_tree
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"find_repo_root",
|
|
15
|
+
"ensure_dir",
|
|
16
|
+
"is_symlink",
|
|
17
|
+
"get_github_token",
|
|
18
|
+
"parse_repo_url",
|
|
19
|
+
"clone_repo",
|
|
20
|
+
"clone_to_temp",
|
|
21
|
+
"cleanup_temp_dir",
|
|
22
|
+
"console",
|
|
23
|
+
"print_error",
|
|
24
|
+
"print_success",
|
|
25
|
+
"print_warning",
|
|
26
|
+
"print_tree",
|
|
27
|
+
]
|