devhive 0.1.24__tar.gz → 0.1.26__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.
- {devhive-0.1.24/devhive.egg-info → devhive-0.1.26}/PKG-INFO +4 -1
- {devhive-0.1.24 → devhive-0.1.26}/README.md +3 -0
- {devhive-0.1.24 → devhive-0.1.26/devhive.egg-info}/PKG-INFO +4 -1
- {devhive-0.1.24 → devhive-0.1.26}/devhive.egg-info/SOURCES.txt +2 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive_cli/main.py +34 -0
- devhive-0.1.26/devhive_cli/templates_backend.py +383 -0
- {devhive-0.1.24 → devhive-0.1.26}/setup.py +1 -1
- devhive-0.1.26/templates/AGENTS.backend.md +54 -0
- {devhive-0.1.24 → devhive-0.1.26}/LICENSE +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/MANIFEST.in +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/architecture-patterns/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/architecture-patterns/resources/implementation-playbook.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/backend-arthitect/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/backend-security-coder/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/docker-expert/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/frontend-design/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/frontend-developer/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/theme-showcase.pdf +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/arctic-frost.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/botanical-garden.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/desert-rose.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/forest-canopy.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/golden-hour.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/midnight-galaxy.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/modern-minimalist.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/ocean-depths.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/sunset-boulevard.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/tech-innovation.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive.egg-info/dependency_links.txt +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive.egg-info/entry_points.txt +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive.egg-info/requires.txt +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive.egg-info/top_level.txt +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/devhive_cli/__init__.py +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/setup.cfg +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_architect/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_auditor/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_backender/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_dba/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_designer/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_devops/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_explorer/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_frontender/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_orchestrator/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_perf/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_proposal/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_qa/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_releaser/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_sast/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_taskplanner/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_techwriter/SKILL.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/skills/devhive_workflow.md +0 -0
- {devhive-0.1.24 → devhive-0.1.26}/templates/AGENTS.frontend.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devhive
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.26
|
|
4
4
|
Summary: CLI to manage DevHive skills and autonomous AI agents for OpenCode.
|
|
5
5
|
Home-page: https://github.com/ramBlanco/devhive
|
|
6
6
|
Author: ramBlanco
|
|
@@ -68,6 +68,9 @@ devhive update-skill
|
|
|
68
68
|
|
|
69
69
|
# Initialize a modern Next.js/React frontend AI guidelines file in your current project
|
|
70
70
|
devhive init-frontend
|
|
71
|
+
|
|
72
|
+
# Initialize a Hexagonal Architecture backend AI guidelines file (Interactive Prompt)
|
|
73
|
+
devhive init-backend
|
|
71
74
|
```
|
|
72
75
|
|
|
73
76
|
## Usage (The `@devhive` Agent)
|
|
@@ -45,6 +45,9 @@ devhive update-skill
|
|
|
45
45
|
|
|
46
46
|
# Initialize a modern Next.js/React frontend AI guidelines file in your current project
|
|
47
47
|
devhive init-frontend
|
|
48
|
+
|
|
49
|
+
# Initialize a Hexagonal Architecture backend AI guidelines file (Interactive Prompt)
|
|
50
|
+
devhive init-backend
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
## Usage (The `@devhive` Agent)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devhive
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.26
|
|
4
4
|
Summary: CLI to manage DevHive skills and autonomous AI agents for OpenCode.
|
|
5
5
|
Home-page: https://github.com/ramBlanco/devhive
|
|
6
6
|
Author: ramBlanco
|
|
@@ -68,6 +68,9 @@ devhive update-skill
|
|
|
68
68
|
|
|
69
69
|
# Initialize a modern Next.js/React frontend AI guidelines file in your current project
|
|
70
70
|
devhive init-frontend
|
|
71
|
+
|
|
72
|
+
# Initialize a Hexagonal Architecture backend AI guidelines file (Interactive Prompt)
|
|
73
|
+
devhive init-backend
|
|
71
74
|
```
|
|
72
75
|
|
|
73
76
|
## Usage (The `@devhive` Agent)
|
|
@@ -29,6 +29,7 @@ devhive.egg-info/requires.txt
|
|
|
29
29
|
devhive.egg-info/top_level.txt
|
|
30
30
|
devhive_cli/__init__.py
|
|
31
31
|
devhive_cli/main.py
|
|
32
|
+
devhive_cli/templates_backend.py
|
|
32
33
|
skills/devhive_workflow.md
|
|
33
34
|
skills/devhive_architect/SKILL.md
|
|
34
35
|
skills/devhive_auditor/SKILL.md
|
|
@@ -46,4 +47,5 @@ skills/devhive_releaser/SKILL.md
|
|
|
46
47
|
skills/devhive_sast/SKILL.md
|
|
47
48
|
skills/devhive_taskplanner/SKILL.md
|
|
48
49
|
skills/devhive_techwriter/SKILL.md
|
|
50
|
+
templates/AGENTS.backend.md
|
|
49
51
|
templates/AGENTS.frontend.md
|
|
@@ -3,6 +3,7 @@ import shutil
|
|
|
3
3
|
import subprocess
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import click
|
|
6
|
+
from devhive_cli.templates_backend import generate_backend_agents_md
|
|
6
7
|
|
|
7
8
|
# Paths configuration
|
|
8
9
|
CLI_DIR = Path(__file__).resolve().parent
|
|
@@ -93,5 +94,38 @@ def init_frontend():
|
|
|
93
94
|
shutil.copy2(template_path, dest_path)
|
|
94
95
|
click.secho("✅ Successfully created AGENTS.md with modern Next.js/React frontend guidelines!", fg="green", bold=True)
|
|
95
96
|
|
|
97
|
+
@cli.command()
|
|
98
|
+
def init_backend():
|
|
99
|
+
"""Initialize a modern backend AGENTS.md template with Hexagonal Architecture."""
|
|
100
|
+
dest_path = Path.cwd() / "AGENTS.md"
|
|
101
|
+
|
|
102
|
+
if dest_path.exists():
|
|
103
|
+
click.secho("AGENTS.md already exists in this directory. Aborting to prevent overwrite.", fg="yellow")
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
language = click.prompt("Which backend language?", type=click.Choice(['python', 'node']))
|
|
107
|
+
|
|
108
|
+
use_di = False
|
|
109
|
+
if language == 'python':
|
|
110
|
+
framework = click.prompt("Which framework?", type=click.Choice(['fastapi', 'django', 'none']))
|
|
111
|
+
else:
|
|
112
|
+
framework = click.prompt("Which framework?", type=click.Choice(['express', 'nestjs', 'none']))
|
|
113
|
+
if framework != 'nestjs':
|
|
114
|
+
use_di = click.confirm("Do you want to use InversifyJS for Dependency Injection?")
|
|
115
|
+
|
|
116
|
+
iac = click.prompt("Which IaC tool are you using?", type=click.Choice(['cdk', 'terraform', 'serverless']))
|
|
117
|
+
|
|
118
|
+
template_path = TEMPLATES_DIR / "AGENTS.backend.md"
|
|
119
|
+
if not template_path.exists():
|
|
120
|
+
click.secho("Error: Backend template not found in the devhive package.", fg="red")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
md_content = generate_backend_agents_md(template_path, language, framework, iac, use_di)
|
|
124
|
+
|
|
125
|
+
with open(dest_path, "w", encoding="utf-8") as f:
|
|
126
|
+
f.write(md_content)
|
|
127
|
+
|
|
128
|
+
click.secho("✅ Successfully created AGENTS.md with Hexagonal Architecture guidelines!", fg="green", bold=True)
|
|
129
|
+
|
|
96
130
|
if __name__ == "__main__":
|
|
97
131
|
cli()
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
def generate_backend_agents_md(template_path: Path, language: str, framework: str, iac: str, use_di: bool) -> str:
|
|
5
|
+
stack_list = []
|
|
6
|
+
if language == 'python':
|
|
7
|
+
stack_list.append("- **Language**: Python 3.10+ (Strict Type Hinting)")
|
|
8
|
+
if framework == 'fastapi':
|
|
9
|
+
stack_list.append("- **Framework**: FastAPI")
|
|
10
|
+
elif framework == 'django':
|
|
11
|
+
stack_list.append("- **Framework**: Django")
|
|
12
|
+
else:
|
|
13
|
+
stack_list.append("- **Framework**: None (Pure Python / Lambdas)")
|
|
14
|
+
else:
|
|
15
|
+
stack_list.append("- **Language**: Node.js + TypeScript (Strict Mode)")
|
|
16
|
+
if framework == 'express':
|
|
17
|
+
stack_list.append("- **Framework**: Express")
|
|
18
|
+
elif framework == 'nestjs':
|
|
19
|
+
stack_list.append("- **Framework**: NestJS (Built-in DI)")
|
|
20
|
+
use_di = False # NestJS has its own DI
|
|
21
|
+
else:
|
|
22
|
+
stack_list.append("- **Framework**: None (Pure Node.js / Lambdas)")
|
|
23
|
+
|
|
24
|
+
if use_di:
|
|
25
|
+
stack_list.append("- **Dependency Injection**: InversifyJS")
|
|
26
|
+
|
|
27
|
+
if iac == 'cdk':
|
|
28
|
+
stack_list.append("- **Infrastructure as Code**: AWS CDK")
|
|
29
|
+
elif iac == 'terraform':
|
|
30
|
+
stack_list.append("- **Infrastructure as Code**: Terraform")
|
|
31
|
+
else:
|
|
32
|
+
stack_list.append("- **Infrastructure as Code**: Serverless Framework")
|
|
33
|
+
|
|
34
|
+
stack_section = "\n".join(stack_list)
|
|
35
|
+
iac_tool_upper = iac.upper()
|
|
36
|
+
|
|
37
|
+
di_rules = ""
|
|
38
|
+
if language == 'node' and use_di:
|
|
39
|
+
di_rules = """
|
|
40
|
+
4. **InversifyJS Usage**:
|
|
41
|
+
- Decorate concrete adapters in `infrastructure/` with `@injectable()`.
|
|
42
|
+
- Bind interfaces to symbols in an `inversify.config.ts` container file.
|
|
43
|
+
- Use the container in the `presentation/` layer to resolve use cases.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
code_examples = ""
|
|
47
|
+
if language == 'python':
|
|
48
|
+
if framework == 'fastapi':
|
|
49
|
+
code_examples = """### Example: FastAPI + Hexagonal Architecture
|
|
50
|
+
|
|
51
|
+
**1. Port (Application Layer)**
|
|
52
|
+
*File: `src/application/ports/user_repository.py`*
|
|
53
|
+
```python
|
|
54
|
+
from typing import Protocol
|
|
55
|
+
from src.domain.entities.user import User
|
|
56
|
+
|
|
57
|
+
class UserRepository(Protocol):
|
|
58
|
+
def get_by_id(self, user_id: str) -> User | None:
|
|
59
|
+
...
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**2. Adapter (Infrastructure Layer)**
|
|
63
|
+
*File: `src/infrastructure/database/postgres_user_repository.py`*
|
|
64
|
+
```python
|
|
65
|
+
from src.application.ports.user_repository import UserRepository
|
|
66
|
+
from src.domain.entities.user import User
|
|
67
|
+
|
|
68
|
+
class PostgresUserRepository:
|
|
69
|
+
def __init__(self, db_session):
|
|
70
|
+
self.db = db_session
|
|
71
|
+
|
|
72
|
+
def get_by_id(self, user_id: str) -> User | None:
|
|
73
|
+
# DB logic here returning Domain Entity
|
|
74
|
+
return User(id=user_id, name="Alice")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**3. Use Case (Application Layer)**
|
|
78
|
+
*File: `src/application/use_cases/get_user.py`*
|
|
79
|
+
```python
|
|
80
|
+
from src.application.ports.user_repository import UserRepository
|
|
81
|
+
from src.domain.entities.user import User
|
|
82
|
+
from src.domain.exceptions import UserNotFoundError
|
|
83
|
+
|
|
84
|
+
class GetUserUseCase:
|
|
85
|
+
def __init__(self, user_repo: UserRepository):
|
|
86
|
+
self.user_repo = user_repo
|
|
87
|
+
|
|
88
|
+
def execute(self, user_id: str) -> User:
|
|
89
|
+
user = self.user_repo.get_by_id(user_id)
|
|
90
|
+
if not user:
|
|
91
|
+
raise UserNotFoundError(f"User {user_id} not found")
|
|
92
|
+
return user
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**4. Controller (Presentation Layer)**
|
|
96
|
+
*File: `src/presentation/controllers/user_controller.py`*
|
|
97
|
+
```python
|
|
98
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
99
|
+
from src.application.use_cases.get_user import GetUserUseCase
|
|
100
|
+
from src.infrastructure.database.postgres_user_repository import PostgresUserRepository
|
|
101
|
+
from src.domain.exceptions import UserNotFoundError
|
|
102
|
+
|
|
103
|
+
router = APIRouter()
|
|
104
|
+
|
|
105
|
+
# Poor man's DI for FastAPI
|
|
106
|
+
def get_user_use_case() -> GetUserUseCase:
|
|
107
|
+
repo = PostgresUserRepository(db_session=None)
|
|
108
|
+
return GetUserUseCase(user_repo=repo)
|
|
109
|
+
|
|
110
|
+
@router.get("/users/{user_id}")
|
|
111
|
+
def get_user(user_id: str, use_case: GetUserUseCase = Depends(get_user_use_case)):
|
|
112
|
+
try:
|
|
113
|
+
return use_case.execute(user_id)
|
|
114
|
+
except UserNotFoundError as e:
|
|
115
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
116
|
+
```
|
|
117
|
+
"""
|
|
118
|
+
elif framework == 'django':
|
|
119
|
+
code_examples = """### Example: Django + Hexagonal Architecture
|
|
120
|
+
|
|
121
|
+
**Note on Django**: Do NOT put business logic in Django Views or Models. Use Django ONLY as the `infrastructure/` and `presentation/` layers.
|
|
122
|
+
|
|
123
|
+
**1. Port (Application Layer)**
|
|
124
|
+
*File: `src/application/ports/user_repository.py`*
|
|
125
|
+
```python
|
|
126
|
+
import abc
|
|
127
|
+
from src.domain.entities.user import User
|
|
128
|
+
|
|
129
|
+
class UserRepository(abc.ABC):
|
|
130
|
+
@abc.abstractmethod
|
|
131
|
+
def get_by_id(self, user_id: str) -> User | None:
|
|
132
|
+
pass
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**2. Adapter (Infrastructure Layer)**
|
|
136
|
+
*File: `src/infrastructure/database/django_user_repository.py`*
|
|
137
|
+
```python
|
|
138
|
+
from src.application.ports.user_repository import UserRepository
|
|
139
|
+
from src.domain.entities.user import User
|
|
140
|
+
from myapp.models import DjangoUserModel # Django ORM Model
|
|
141
|
+
|
|
142
|
+
class DjangoUserRepository(UserRepository):
|
|
143
|
+
def get_by_id(self, user_id: str) -> User | None:
|
|
144
|
+
try:
|
|
145
|
+
orm_user = DjangoUserModel.objects.get(id=user_id)
|
|
146
|
+
return User(id=str(orm_user.id), name=orm_user.name) # Map ORM to Domain
|
|
147
|
+
except DjangoUserModel.DoesNotExist:
|
|
148
|
+
return None
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**3. Controller (Presentation Layer)**
|
|
152
|
+
*File: `src/presentation/views/user_views.py`*
|
|
153
|
+
```python
|
|
154
|
+
from django.http import JsonResponse
|
|
155
|
+
from src.application.use_cases.get_user import GetUserUseCase
|
|
156
|
+
from src.infrastructure.database.django_user_repository import DjangoUserRepository
|
|
157
|
+
from src.domain.exceptions import UserNotFoundError
|
|
158
|
+
|
|
159
|
+
def get_user_view(request, user_id: str):
|
|
160
|
+
repo = DjangoUserRepository()
|
|
161
|
+
use_case = GetUserUseCase(user_repo=repo)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
user = use_case.execute(user_id)
|
|
165
|
+
return JsonResponse({"id": user.id, "name": user.name})
|
|
166
|
+
except UserNotFoundError as e:
|
|
167
|
+
return JsonResponse({"error": str(e)}, status=404)
|
|
168
|
+
```
|
|
169
|
+
"""
|
|
170
|
+
else:
|
|
171
|
+
code_examples = """### Example: Pure Python + Hexagonal Architecture
|
|
172
|
+
|
|
173
|
+
**1. Port (Application Layer)**
|
|
174
|
+
*File: `src/application/ports/user_repository.py`*
|
|
175
|
+
```python
|
|
176
|
+
from typing import Protocol
|
|
177
|
+
from src.domain.entities.user import User
|
|
178
|
+
|
|
179
|
+
class UserRepository(Protocol):
|
|
180
|
+
def get_by_id(self, user_id: str) -> User | None:
|
|
181
|
+
...
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**2. Use Case (Application Layer)**
|
|
185
|
+
*File: `src/application/use_cases/get_user.py`*
|
|
186
|
+
```python
|
|
187
|
+
from src.application.ports.user_repository import UserRepository
|
|
188
|
+
from src.domain.entities.user import User
|
|
189
|
+
|
|
190
|
+
class GetUserUseCase:
|
|
191
|
+
def __init__(self, user_repo: UserRepository):
|
|
192
|
+
self.user_repo = user_repo
|
|
193
|
+
|
|
194
|
+
def execute(self, user_id: str) -> User:
|
|
195
|
+
return self.user_repo.get_by_id(user_id)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**3. Handler (Presentation Layer - Lambda)**
|
|
199
|
+
*File: `src/presentation/handlers/user_handler.py`*
|
|
200
|
+
```python
|
|
201
|
+
from src.application.use_cases.get_user import GetUserUseCase
|
|
202
|
+
from src.infrastructure.database.dynamo_user_repository import DynamoUserRepository
|
|
203
|
+
|
|
204
|
+
# Initialize dependencies at the module level for Lambda cold starts
|
|
205
|
+
repo = DynamoUserRepository()
|
|
206
|
+
use_case = GetUserUseCase(user_repo=repo)
|
|
207
|
+
|
|
208
|
+
def lambda_handler(event, context):
|
|
209
|
+
user_id = event.get('pathParameters', {}).get('userId')
|
|
210
|
+
user = use_case.execute(user_id)
|
|
211
|
+
return {"statusCode": 200, "body": user.to_json()}
|
|
212
|
+
```
|
|
213
|
+
"""
|
|
214
|
+
else: # Node.js
|
|
215
|
+
if use_di:
|
|
216
|
+
code_examples = """### Example: Node.js + TypeScript + InversifyJS
|
|
217
|
+
|
|
218
|
+
**1. Port (Application Layer)**
|
|
219
|
+
*File: `src/application/ports/IUserRepository.ts`*
|
|
220
|
+
```typescript
|
|
221
|
+
import { User } from "../../domain/entities/User";
|
|
222
|
+
|
|
223
|
+
export const TYPES = {
|
|
224
|
+
IUserRepository: Symbol.for("IUserRepository"),
|
|
225
|
+
GetUserUseCase: Symbol.for("GetUserUseCase"),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export interface IUserRepository {
|
|
229
|
+
getById(userId: string): Promise<User | null>;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**2. Adapter (Infrastructure Layer)**
|
|
234
|
+
*File: `src/infrastructure/database/PostgresUserRepository.ts`*
|
|
235
|
+
```typescript
|
|
236
|
+
import { injectable } from "inversify";
|
|
237
|
+
import "reflect-metadata";
|
|
238
|
+
import { IUserRepository } from "../../application/ports/IUserRepository";
|
|
239
|
+
import { User } from "../../domain/entities/User";
|
|
240
|
+
|
|
241
|
+
@injectable()
|
|
242
|
+
export class PostgresUserRepository implements IUserRepository {
|
|
243
|
+
async getById(userId: string): Promise<User | null> {
|
|
244
|
+
// DB Logic returning Domain Entity
|
|
245
|
+
return new User(userId, "Alice");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**3. Use Case (Application Layer)**
|
|
251
|
+
*File: `src/application/useCases/GetUserUseCase.ts`*
|
|
252
|
+
```typescript
|
|
253
|
+
import { inject, injectable } from "inversify";
|
|
254
|
+
import "reflect-metadata";
|
|
255
|
+
import { IUserRepository, TYPES } from "../ports/IUserRepository";
|
|
256
|
+
import { User } from "../../domain/entities/User";
|
|
257
|
+
|
|
258
|
+
@injectable()
|
|
259
|
+
export class GetUserUseCase {
|
|
260
|
+
constructor(
|
|
261
|
+
@inject(TYPES.IUserRepository) private userRepository: IUserRepository
|
|
262
|
+
) {}
|
|
263
|
+
|
|
264
|
+
async execute(userId: string): Promise<User> {
|
|
265
|
+
const user = await this.userRepository.getById(userId);
|
|
266
|
+
if (!user) throw new Error("User not found");
|
|
267
|
+
return user;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**4. DI Container Setup (Infrastructure Layer)**
|
|
273
|
+
*File: `src/infrastructure/di/container.ts`*
|
|
274
|
+
```typescript
|
|
275
|
+
import { Container } from "inversify";
|
|
276
|
+
import { TYPES, IUserRepository } from "../../application/ports/IUserRepository";
|
|
277
|
+
import { PostgresUserRepository } from "../database/PostgresUserRepository";
|
|
278
|
+
import { GetUserUseCase } from "../../application/useCases/GetUserUseCase";
|
|
279
|
+
|
|
280
|
+
const container = new Container();
|
|
281
|
+
|
|
282
|
+
container.bind<IUserRepository>(TYPES.IUserRepository).to(PostgresUserRepository);
|
|
283
|
+
container.bind<GetUserUseCase>(TYPES.GetUserUseCase).to(GetUserUseCase);
|
|
284
|
+
|
|
285
|
+
export { container };
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**5. Controller (Presentation Layer)**
|
|
289
|
+
*File: `src/presentation/controllers/UserController.ts`*
|
|
290
|
+
```typescript
|
|
291
|
+
import { Request, Response } from "express";
|
|
292
|
+
import { container } from "../../infrastructure/di/container";
|
|
293
|
+
import { TYPES } from "../../application/ports/IUserRepository";
|
|
294
|
+
import { GetUserUseCase } from "../../application/useCases/GetUserUseCase";
|
|
295
|
+
|
|
296
|
+
export class UserController {
|
|
297
|
+
async getUser(req: Request, res: Response) {
|
|
298
|
+
const useCase = container.get<GetUserUseCase>(TYPES.GetUserUseCase);
|
|
299
|
+
try {
|
|
300
|
+
const user = await useCase.execute(req.params.id);
|
|
301
|
+
res.json(user);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
res.status(404).json({ error: error.message });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
"""
|
|
309
|
+
else:
|
|
310
|
+
code_examples = """### Example: Node.js + TypeScript (Manual DI)
|
|
311
|
+
|
|
312
|
+
**1. Port (Application Layer)**
|
|
313
|
+
*File: `src/application/ports/IUserRepository.ts`*
|
|
314
|
+
```typescript
|
|
315
|
+
import { User } from "../../domain/entities/User";
|
|
316
|
+
|
|
317
|
+
export interface IUserRepository {
|
|
318
|
+
getById(userId: string): Promise<User | null>;
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**2. Adapter (Infrastructure Layer)**
|
|
323
|
+
*File: `src/infrastructure/database/PostgresUserRepository.ts`*
|
|
324
|
+
```typescript
|
|
325
|
+
import { IUserRepository } from "../../application/ports/IUserRepository";
|
|
326
|
+
import { User } from "../../domain/entities/User";
|
|
327
|
+
|
|
328
|
+
export class PostgresUserRepository implements IUserRepository {
|
|
329
|
+
async getById(userId: string): Promise<User | null> {
|
|
330
|
+
// DB Logic returning Domain Entity
|
|
331
|
+
return new User(userId, "Alice");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**3. Use Case (Application Layer)**
|
|
337
|
+
*File: `src/application/useCases/GetUserUseCase.ts`*
|
|
338
|
+
```typescript
|
|
339
|
+
import { IUserRepository } from "../ports/IUserRepository";
|
|
340
|
+
import { User } from "../../domain/entities/User";
|
|
341
|
+
|
|
342
|
+
export class GetUserUseCase {
|
|
343
|
+
constructor(private userRepository: IUserRepository) {}
|
|
344
|
+
|
|
345
|
+
async execute(userId: string): Promise<User> {
|
|
346
|
+
const user = await this.userRepository.getById(userId);
|
|
347
|
+
if (!user) throw new Error("User not found");
|
|
348
|
+
return user;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**4. Controller (Presentation Layer)**
|
|
354
|
+
*File: `src/presentation/controllers/UserController.ts`*
|
|
355
|
+
```typescript
|
|
356
|
+
import { Request, Response } from "express";
|
|
357
|
+
import { GetUserUseCase } from "../../application/useCases/GetUserUseCase";
|
|
358
|
+
import { PostgresUserRepository } from "../../infrastructure/database/PostgresUserRepository";
|
|
359
|
+
|
|
360
|
+
export class UserController {
|
|
361
|
+
async getUser(req: Request, res: Response) {
|
|
362
|
+
const repo = new PostgresUserRepository();
|
|
363
|
+
const useCase = new GetUserUseCase(repo);
|
|
364
|
+
try {
|
|
365
|
+
const user = await useCase.execute(req.params.id);
|
|
366
|
+
res.json(user);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
res.status(404).json({ error: error.message });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
with open(template_path, 'r', encoding='utf-8') as f:
|
|
376
|
+
md = f.read()
|
|
377
|
+
|
|
378
|
+
md = md.replace("{{TECH_STACK}}", stack_section)
|
|
379
|
+
md = md.replace("{{IAC_TOOL}}", iac_tool_upper)
|
|
380
|
+
md = md.replace("{{DI_RULES}}", di_rules)
|
|
381
|
+
md = md.replace("{{CODE_EXAMPLES}}", code_examples)
|
|
382
|
+
|
|
383
|
+
return md
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Backend Architecture & AI Agent Guidelines
|
|
2
|
+
|
|
3
|
+
This `AGENTS.md` (or `GUIDELINES.md`) file serves as the strict instruction manual for OpenCode and DevHive agents. It defines the technical stack, architectural patterns, and coding conventions for this backend project.
|
|
4
|
+
|
|
5
|
+
## 🛠️ 1. Tech Stack
|
|
6
|
+
{{TECH_STACK}}
|
|
7
|
+
|
|
8
|
+
## 🧠 2. OpenCode Skills to Load
|
|
9
|
+
Agents **MUST** load the following specialized skills before executing tasks:
|
|
10
|
+
- `architecture-patterns`: For maintaining clean separation of concerns and Hexagonal Architecture.
|
|
11
|
+
- `backend-architect`: For scalable API design and domain modeling.
|
|
12
|
+
- `backend-security-coder`: For secure coding practices, input validation, and auth.
|
|
13
|
+
- `devhive-devops`: For {{IAC_TOOL}} infrastructure setup.
|
|
14
|
+
|
|
15
|
+
## 📂 3. Project Structure (Hexagonal Architecture)
|
|
16
|
+
For maximum scalability, testability, and maintainability, the project MUST strictly follow Hexagonal Architecture (Ports & Adapters).
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
src/
|
|
20
|
+
├── domain/ # ENTERPRISE BUSINESS RULES (No external dependencies)
|
|
21
|
+
│ ├── entities/ # Pure domain models
|
|
22
|
+
│ └── exceptions/ # Domain-specific errors
|
|
23
|
+
├── application/ # APPLICATION BUSINESS RULES (Use Cases & Ports)
|
|
24
|
+
│ ├── use_cases/ # Orchestrates domain logic
|
|
25
|
+
│ └── ports/ # Interfaces for outbound communication (Repositories, External APIs)
|
|
26
|
+
├── infrastructure/ # ADAPTERS (Database, External Services, Third-party libs)
|
|
27
|
+
│ ├── database/ # Concrete implementation of repository ports
|
|
28
|
+
│ └── di/ # Dependency Injection container/setup
|
|
29
|
+
└── presentation/ # ENTRY POINTS (Controllers, REST APIs, GraphQL, Lambdas)
|
|
30
|
+
└── controllers/ # Parses HTTP requests and calls use cases
|
|
31
|
+
|
|
32
|
+
infrastructure/ # IaC DEFINITIONS (Outside src/)
|
|
33
|
+
└── # {{IAC_TOOL}} configurations
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 📐 4. Architectural Rules & Clean Code (CRITICAL)
|
|
37
|
+
|
|
38
|
+
1. **The Dependency Rule**: Source code dependencies MUST only point inward, toward the `domain/`.
|
|
39
|
+
- `domain/` knows nothing about anything else.
|
|
40
|
+
- `application/` depends only on `domain/`.
|
|
41
|
+
- `infrastructure/` and `presentation/` depend on `application/` and `domain/`.
|
|
42
|
+
- **Never import an infrastructure detail (like a DB model) into a use case.**
|
|
43
|
+
|
|
44
|
+
2. **Ports and Adapters**:
|
|
45
|
+
- Outbound communication (like DB queries) MUST be defined as an interface (Port) in the `application/` layer.
|
|
46
|
+
- The actual database logic MUST be implemented as an Adapter in the `infrastructure/` layer.
|
|
47
|
+
|
|
48
|
+
3. **Dependency Injection**:
|
|
49
|
+
- Use Cases must receive their dependencies (Ports) via constructor injection.
|
|
50
|
+
- The Presentation layer resolves these dependencies and passes them to the Use Cases.
|
|
51
|
+
{{DI_RULES}}
|
|
52
|
+
## 💻 5. Code Examples
|
|
53
|
+
|
|
54
|
+
{{CODE_EXAMPLES}}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devhive-0.1.24 → devhive-0.1.26}/community_skills/theme-factory/themes/modern-minimalist.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|