cc-codeconductor 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +319 -0
  3. package/dist/index.js +12741 -0
  4. package/package.json +51 -0
  5. package/policy.yml +124 -0
  6. package/presets/claude/CLAUDE.md +598 -0
  7. package/presets/claude/commands/cc/feature.md +115 -0
  8. package/presets/claude/commands/cc/fix.md +121 -0
  9. package/presets/claude/commands/cc/refactor.md +148 -0
  10. package/presets/claude/commands/cc/review.md +126 -0
  11. package/presets/claude/commands/cc/tdd-cycle.md +226 -0
  12. package/presets/claude/commands/cc/test-plan.md +138 -0
  13. package/presets/claude/settings.json +37 -0
  14. package/presets/claude/skills/api-versioning/SKILL.md +389 -0
  15. package/presets/claude/skills/django-orm/SKILL.md +455 -0
  16. package/presets/claude/skills/django-testing/SKILL.md +409 -0
  17. package/presets/claude/skills/jpa-postgres/SKILL.md +618 -0
  18. package/presets/claude/skills/python/SKILL.md +606 -0
  19. package/presets/claude/skills/python-django-stack/SKILL.md +492 -0
  20. package/presets/claude/skills/python-fastapi-stack/SKILL.md +457 -0
  21. package/presets/claude/skills/spring-boot-feature/SKILL.md +558 -0
  22. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +400 -0
  23. package/presets/claude/skills/sqlalchemy/SKILL.md +466 -0
  24. package/presets/claude/skills/testing-strategy/SKILL.md +479 -0
  25. package/presets/codex/AGENTS.md +883 -0
  26. package/presets/codex/README.md +102 -0
  27. package/presets/codex/skills/api-versioning/SKILL.md +389 -0
  28. package/presets/codex/skills/django-orm/SKILL.md +455 -0
  29. package/presets/codex/skills/django-testing/SKILL.md +409 -0
  30. package/presets/codex/skills/jpa-postgres/SKILL.md +618 -0
  31. package/presets/codex/skills/python/SKILL.md +606 -0
  32. package/presets/codex/skills/python-django-stack/SKILL.md +492 -0
  33. package/presets/codex/skills/python-fastapi-stack/SKILL.md +457 -0
  34. package/presets/codex/skills/spring-boot-feature/SKILL.md +558 -0
  35. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +400 -0
  36. package/presets/codex/skills/sqlalchemy/SKILL.md +466 -0
  37. package/presets/codex/skills/testing-strategy/SKILL.md +479 -0
  38. package/presets/opencode/README.md +191 -0
  39. package/presets/opencode/agents/architect.md +133 -0
  40. package/presets/opencode/agents/docs.md +113 -0
  41. package/presets/opencode/agents/implementer.md +116 -0
  42. package/presets/opencode/agents/orchestrator.md +248 -0
  43. package/presets/opencode/agents/repo-explorer.md +119 -0
  44. package/presets/opencode/agents/reviewer.md +130 -0
  45. package/presets/opencode/agents/task-coach.md +103 -0
  46. package/presets/opencode/agents/tester.md +269 -0
  47. package/presets/opencode/commands/cc-feature.md +114 -0
  48. package/presets/opencode/commands/cc-fix.md +123 -0
  49. package/presets/opencode/commands/cc-refactor.md +148 -0
  50. package/presets/opencode/commands/cc-review.md +141 -0
  51. package/presets/opencode/commands/cc-tdd-cycle.md +225 -0
  52. package/presets/opencode/commands/cc-test-plan.md +144 -0
  53. package/presets/opencode/opencode.jsonc +89 -0
  54. package/presets/opencode/prompts/v0.1.0/architect.md +213 -0
  55. package/presets/opencode/prompts/v0.1.0/docs.md +181 -0
  56. package/presets/opencode/prompts/v0.1.0/implementer.md +154 -0
  57. package/presets/opencode/prompts/v0.1.0/orchestrator.md +169 -0
  58. package/presets/opencode/prompts/v0.1.0/repo-explorer.md +102 -0
  59. package/presets/opencode/prompts/v0.1.0/reviewer.md +183 -0
  60. package/presets/opencode/prompts/v0.1.0/task-coach.md +142 -0
  61. package/presets/opencode/prompts/v0.1.0/tester.md +160 -0
  62. package/presets/opencode/prompts/v0.2.0/architect.md +219 -0
  63. package/presets/opencode/prompts/v0.2.0/docs.md +187 -0
  64. package/presets/opencode/prompts/v0.2.0/implementer.md +160 -0
  65. package/presets/opencode/prompts/v0.2.0/orchestrator.md +238 -0
  66. package/presets/opencode/prompts/v0.2.0/repo-explorer.md +108 -0
  67. package/presets/opencode/prompts/v0.2.0/reviewer.md +190 -0
  68. package/presets/opencode/prompts/v0.2.0/task-coach.md +153 -0
  69. package/presets/opencode/prompts/v0.2.0/tester.md +249 -0
  70. package/presets/opencode/skills/api-versioning/SKILL.md +388 -0
  71. package/presets/opencode/skills/astro/SKILL.md +313 -0
  72. package/presets/opencode/skills/code-review/SKILL.md +202 -0
  73. package/presets/opencode/skills/django-orm/SKILL.md +455 -0
  74. package/presets/opencode/skills/django-testing/SKILL.md +408 -0
  75. package/presets/opencode/skills/django-uv/SKILL.md +400 -0
  76. package/presets/opencode/skills/jpa-postgres/SKILL.md +617 -0
  77. package/presets/opencode/skills/nextjs-typescript/SKILL.md +385 -0
  78. package/presets/opencode/skills/python/SKILL.md +605 -0
  79. package/presets/opencode/skills/python-django-stack/SKILL.md +491 -0
  80. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +456 -0
  81. package/presets/opencode/skills/security/SKILL.md +376 -0
  82. package/presets/opencode/skills/spring-boot-feature/SKILL.md +557 -0
  83. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +399 -0
  84. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +470 -0
  85. package/presets/opencode/skills/sqlalchemy/SKILL.md +465 -0
  86. package/presets/opencode/skills/testing-tdd/SKILL.md +586 -0
  87. package/src/presets/council/council.yml +59 -0
  88. package/src/presets/manifests/claude.yml +23 -0
  89. package/src/presets/manifests/codex.yml +12 -0
  90. package/src/presets/manifests/opencode.yml +18 -0
  91. package/src/presets/models/claude.yml +37 -0
  92. package/src/presets/models/codex.yml +38 -0
  93. package/src/presets/models/opencode.yml +37 -0
@@ -0,0 +1,491 @@
1
+ ---
2
+ id: python-django-stack
3
+ version: 1.0.0
4
+ name: Python Django Stack
5
+ description: >
6
+ Python and Django conventions for multi-tenant SaaS POS projects:
7
+ views, services, naming, JSON APIs, PDFs, and cart patterns.
8
+ Trigger: When writing any view, service, API endpoint, or model code in apps/.
9
+
10
+ compatibility:
11
+ tools: [claude, codex, opencode]
12
+ stacks:
13
+ languages: [python]
14
+ frameworks: [django, django-tenants, django-rest-framework, reportlab]
15
+
16
+ risk:
17
+ level: medium
18
+ can_execute_shell: true
19
+ can_modify_files: true
20
+ requires_network: false
21
+
22
+ inputs:
23
+ - view files (views.py)
24
+ - service files (services.py)
25
+ - model files (models.py)
26
+ - URL configuration files (urls.py)
27
+ - existing app structure under apps/
28
+
29
+ outputs:
30
+ - FBV and CBV view implementations
31
+ - service layer classes with static methods
32
+ - JSON API responses with correct status codes
33
+ - pagination with offset/limit pattern
34
+ - PDF generation functions (reportlab)
35
+ - cart session implementations
36
+ - URL configurations with namespaces
37
+ - model definitions with indexes and choices
38
+
39
+ quality:
40
+ reviewed_by: codeconductor-core
41
+ version: 0.1.0
42
+ ---
43
+ ## When to Use
44
+
45
+ - Writing any new view, service method, or API endpoint
46
+ - Designing a new feature and deciding where logic lives
47
+ - Adding a JSON API response
48
+ - Implementing a PDF report or cart operation
49
+ - Structuring the architecture of a new app
50
+
51
+ ## Django Project Structure
52
+
53
+ ### Recommended Apps
54
+
55
+ A well-structured Django project follows modular app patterns:
56
+
57
+ ```
58
+ apps/
59
+ ├── core/ # Configuration, shared models (Store)
60
+ ├── users/ # User model, authentication
61
+ ├── employees/ # Employees (tenant)
62
+ ├── catalog/ # Products, categories (tenant)
63
+ ├── cart/ # Storefront cart (tenant)
64
+ ├── orders/ # Orders (tenant)
65
+ ├── pos/ # Point of Sale (tenant)
66
+ ├── storefront/ # Public store (tenant)
67
+ └── analytics/ # Reports and metrics (tenant)
68
+ ```
69
+
70
+ ### Per-App File Structure
71
+
72
+ ```
73
+ apps/catalog/
74
+ ├── __init__.py
75
+ ├── models.py # Models (Product, Category, ProductImage)
76
+ ├── views.py # Views
77
+ ├── urls.py # Routes
78
+ ├── services.py # Business logic
79
+ ├── serializers.py # Serialization (if using DRF)
80
+ ├── reports.py # PDF reports
81
+ ├── signals.py # Signals
82
+ ├── admin.py # Admin config
83
+ ├── tests/
84
+ │ ├── __init__.py
85
+ │ └── test_{feature}.py
86
+ └── migrations/
87
+ ```
88
+
89
+ ## View Patterns
90
+
91
+ ### FBV vs CBV
92
+
93
+ | Type | When to Use | Examples |
94
+ | ------------------------ | ---------------------------------------------------------------------- | ----------------------------------------------------- |
95
+ | **FBV** (Function-Based) | All API endpoints, all POS/manager views, any view with business logic | `api_cart_add`, `api_search_products`, `pos_checkout` |
96
+ | **CBV** (Class-Based) | Only basic storefront list/detail | `HomeView`, `CategoryListView`, `ProductDetailView` |
97
+
98
+ **Rule**: CBV for generic read-only views with simple templates. FBV for
99
+ everything else (APIs, complex logic, POS).
100
+
101
+ ```python
102
+ # FBV — always for APIs
103
+ @require_pos_access
104
+ @require_POST
105
+ def api_cart_add(request):
106
+ cart = POSCart(request)
107
+ data = json.loads(request.body)
108
+ cart.add(data["product_id"], data["quantity"])
109
+ return JsonResponse({"success": True, "cart_count": cart.count()})
110
+
111
+ # CBV — only for basic storefront
112
+ class ProductDetailView(TemplateView):
113
+ template_name = "storefront/product.html"
114
+
115
+ def get_context_data(self, **kwargs):
116
+ ctx = super().get_context_data(**kwargs)
117
+ ctx["product"] = get_object_or_404(Product, slug=self.kwargs["slug"])
118
+ return ctx
119
+ ```
120
+
121
+ ### Decorator Order
122
+
123
+ Custom access decorator outermost, HTTP method decorator innermost:
124
+
125
+ ```python
126
+ @require_pos_access # outermost — runs first, guards access
127
+ @require_POST # innermost — closest to the function
128
+ def api_cart_add(request):
129
+ ...
130
+ ```
131
+
132
+ **Always use** `@require_POST` / `@require_GET` explicitly. Never
133
+ `if request.method == "POST"` inside an API view.
134
+
135
+ ## Service Layer
136
+
137
+ Business logic lives in `services.py` modules. Services are classes with
138
+ `@staticmethod` only — no instance state, no `__init__`:
139
+
140
+ ```python
141
+ class OrderService:
142
+ @staticmethod
143
+ def create_order(order: Order, cart: Cart) -> Order:
144
+ with transaction.atomic():
145
+ order.save()
146
+ OrderItem.objects.bulk_create(items)
147
+ Product.objects.bulk_update(products, ["stock"])
148
+ cart.clear()
149
+ return order
150
+
151
+ @staticmethod
152
+ def cancel_order(order_id: int, reason: str) -> Order:
153
+ ...
154
+ ```
155
+
156
+ Views call `OrderService.create_order(...)`. **No business logic in views.**
157
+
158
+ ### When to Create a Service
159
+
160
+ - Complex business logic involving multiple models
161
+ - Transactions spanning multiple DB operations
162
+ - Logic reusable across multiple views
163
+ - Anything that isn't "delegate to another component"
164
+
165
+ ## Naming Conventions
166
+
167
+ | Pattern | Convention | Examples |
168
+ | ---------------------- | -------------------------------------------- | --------------------------------------------------------------- |
169
+ | AJAX API views | `api_{resource}_{action}` | `api_cart_add`, `api_search_products`, `api_temporal_cart_save` |
170
+ | Module-private helpers | leading `_` | `_build_pos_order`, `_sort_by_category` |
171
+ | Module constants | `UPPER_SNAKE_CASE` | `VALID_PAYMENT_METHODS = {"cash", "transfer", "card"}` |
172
+ | URL names | `{scope}_{resource}_{action}` | `api_catalog_products`, `pos_checkout` |
173
+ | Service methods | `camelCase` (PEP8 inconsistent but accepted) | `createOrder`, `calculateTotal` |
174
+ | Model methods | `snake_case` | `get_display_price()`, `is_available()` |
175
+
176
+ ## JSON API Response Shapes
177
+
178
+ ### List Endpoint with Pagination
179
+
180
+ ```python
181
+ return JsonResponse({
182
+ "results": [...],
183
+ "total": N,
184
+ "has_more": bool,
185
+ })
186
+ ```
187
+
188
+ ### Successful Mutation
189
+
190
+ ```python
191
+ return JsonResponse({"success": True, "cart_count": N})
192
+ ```
193
+
194
+ ### Error Response
195
+
196
+ ```python
197
+ return JsonResponse({"success": False, "error": "error message"}, status=400)
198
+ ```
199
+
200
+ ### HTTP Status Codes
201
+
202
+ - `200` — OK, success
203
+ - `201` — Created
204
+ - `400` — Client error (invalid data, missing fields)
205
+ - `404` — Not found
206
+ - `500` — Server error
207
+
208
+ **Never return** `200` with `"success": false`. Use the correct status code.
209
+
210
+ ## Pagination
211
+
212
+ ### Offset/Limit — No Page Numbers
213
+
214
+ Always offset + limit, never page numbers. Default 20 items, max 100:
215
+
216
+ ```python
217
+ offset = int(request.GET.get("offset", 0))
218
+ limit = min(int(request.GET.get("limit", 20)), 100)
219
+
220
+ total = qs.count()
221
+ page = qs[offset:offset + limit]
222
+
223
+ return JsonResponse({
224
+ "results": list(page.values("id", "name", "price")),
225
+ "total": total,
226
+ "has_more": (offset + limit) < total,
227
+ })
228
+ ```
229
+
230
+ ## PDF Patterns
231
+
232
+ Pure functions, no classes. Take primitive data, return `bytes`. Internal
233
+ helpers prefixed with `_`:
234
+
235
+ ```python
236
+ import io
237
+ from reportlab.lib.pagesizes import A4
238
+ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
239
+
240
+ def generate_restock_pdf(store_name: str, products_raw: list) -> bytes:
241
+ buffer = io.BytesIO()
242
+ doc = _make_doc(buffer)
243
+ story = _build_story(store_name, products_raw)
244
+ doc.build(story)
245
+ buffer.seek(0)
246
+ return buffer.read()
247
+
248
+ def _make_doc(buffer: io.BytesIO) -> SimpleDocTemplate:
249
+ return SimpleDocTemplate(
250
+ buffer,
251
+ pagesize=A4,
252
+ rightMargin=30,
253
+ leftMargin=30,
254
+ topMargin=30,
255
+ bottomMargin=30,
256
+ )
257
+
258
+ def _build_story(store_name: str, products_raw: list) -> list:
259
+ data = [["SKU", "Product", "Stock", "Min"]]
260
+ for p in products_raw:
261
+ data.append([p["sku"], p["name"], p["stock"], p["min_stock"]])
262
+ table = Table(data, colWidths=[60, 200, 50, 50])
263
+ table.setStyle(TableStyle([...]))
264
+ return [table]
265
+ ```
266
+
267
+ The view wraps the result in `HttpResponse`:
268
+
269
+ ```python
270
+ def api_restock_pdf(request):
271
+ products = Product.objects.filter(stock__lte=F("min_stock"))
272
+ pdf_bytes = generate_restock_pdf(request.store.name, list(products.values()))
273
+ response = HttpResponse(pdf_bytes, content_type="application/pdf")
274
+ response["Content-Disposition"] = 'inline; filename="restock.pdf"'
275
+ return response
276
+ ```
277
+
278
+ ## Cart Patterns
279
+
280
+ Two independent carts coexist per request. Session key is the only separator:
281
+
282
+ | Cart | Session Key | Module |
283
+ | ---------- | ----------- | ---------------------- |
284
+ | Storefront | `cart` | `apps/cart/cart.py` |
285
+ | POS | `pos_cart` | `apps/pos/pos_cart.py` |
286
+
287
+ Both follow the same contract:
288
+
289
+ - Initialize with `request`
290
+ - Store as `{str(product_id): {"quantity": int, "price": str}}`
291
+ - Price stored as `str` (Decimal is not JSON-serializable)
292
+ - Always call `self.session.modified = True` in `save()`
293
+
294
+ ```python
295
+ class POSCart:
296
+ def __init__(self, request):
297
+ self.session = request.session.get("pos_cart", {})
298
+
299
+ def add(self, product_id, quantity):
300
+ key = str(product_id)
301
+ if key in self.session:
302
+ self.session[key]["quantity"] += quantity
303
+ else:
304
+ self.session[key] = {"quantity": quantity, "price": "0.00"}
305
+ self.save()
306
+
307
+ def save(self):
308
+ self.session["pos_cart"] = self.session
309
+ self.session.modified = True
310
+ ```
311
+
312
+ ## Type Hints
313
+
314
+ **Minimal**: Only on public service/utility function signatures, not on views or
315
+ helpers:
316
+
317
+ ```python
318
+ # Service — annotate
319
+ def generate_restock_pdf(store_name: str, products_raw: list) -> bytes:
320
+ ...
321
+
322
+ # View — no annotations
323
+ def api_cart_add(request):
324
+ ...
325
+
326
+ # Private helpers — no annotations
327
+ def _build_cart_item(product, quantity):
328
+ ...
329
+ ```
330
+
331
+ ## Access Decorators
332
+
333
+ ### require_pos_access
334
+
335
+ Verifies the employee has POS access:
336
+
337
+ ```python
338
+ def require_pos_access(view):
339
+ def wrapper(request, *args, **kwargs):
340
+ if not request.user.is_authenticated:
341
+ return JsonResponse({"error": "Auth required"}, status=401)
342
+ if not request.user.is_superuser:
343
+ emp = EmployeeProfile.objects.get(user=request.user)
344
+ if not emp.has_pos_access:
345
+ return JsonResponse({"error": "No POS access"}, status=403)
346
+ return view(request, *args, **kwargs)
347
+ return wrapper
348
+ ```
349
+
350
+ ### require_manager_access
351
+
352
+ Verifies the employee is a manager:
353
+
354
+ ```python
355
+ def require_manager_access(view):
356
+ def wrapper(request, *args, **kwargs):
357
+ if not request.user.is_authenticated:
358
+ return JsonResponse({"error": "Auth required"}, status=401)
359
+ if not request.user.is_superuser:
360
+ emp = EmployeeProfile.objects.get(user=request.user)
361
+ if not emp.is_manager:
362
+ return JsonResponse({"error": "Manager required"}, status=403)
363
+ return view(request, *args, **kwargs)
364
+ return wrapper
365
+ ```
366
+
367
+ ## URLs and Routing
368
+
369
+ ### URL Structure
370
+
371
+ ```python
372
+ # apps/catalog/urls.py
373
+ from django.urls import path
374
+
375
+ urlpatterns = [
376
+ # API
377
+ path("api/products/", api_catalog_products, name="api_catalog_products"),
378
+ path("api/products/<int:pk>/", api_product_detail, name="api_product_detail"),
379
+ # Web
380
+ path("", product_list, name="catalog_product_list"),
381
+ path("<slug:slug>/", product_detail, name="catalog_product_detail"),
382
+ ]
383
+ ```
384
+
385
+ ### Namespaces
386
+
387
+ ```python
388
+ # config/urls.py
389
+ urlpatterns = [
390
+ path("catalog/", include("apps.catalog.urls", namespace="catalog")),
391
+ path("pos/", include("apps.pos.urls", namespace="pos")),
392
+ ]
393
+ ```
394
+
395
+ ## Models — Best Practices
396
+
397
+ ### Model Fields
398
+
399
+ ```python
400
+ class Product(models.Model):
401
+ name = models.CharField(max_length=255)
402
+ slug = models.SlugField(max_length=255, unique=True)
403
+ description = models.TextField(blank=True)
404
+ price = models.DecimalField(max_digits=10, decimal_places=2)
405
+ stock = models.PositiveIntegerField(default=0)
406
+ is_active = models.BooleanField(default=True)
407
+ created_at = models.DateTimeField(auto_now_add=True)
408
+ updated_at = models.DateTimeField(auto_now=True)
409
+
410
+ class Meta:
411
+ ordering = ["name"]
412
+ indexes = [
413
+ models.Index(fields=["is_active", "stock"]),
414
+ models.Index(fields=["slug"]),
415
+ ]
416
+
417
+ def __str__(self):
418
+ return self.name
419
+
420
+ @property
421
+ def is_available(self):
422
+ return self.is_active and (self.is_service or self.stock > 0)
423
+ ```
424
+
425
+ ### Choices
426
+
427
+ ```python
428
+ class Order(models.Model):
429
+ class Status(models.TextChoices):
430
+ PENDING = "pending", "Pending"
431
+ PAID = "paid", "Paid"
432
+ SHIPPED = "shipped", "Shipped"
433
+ CANCELLED = "cancelled", "Cancelled"
434
+
435
+ status = models.CharField(
436
+ max_length=20,
437
+ choices=Status.choices,
438
+ default=Status.PENDING,
439
+ )
440
+ ```
441
+
442
+ ## Code Quality
443
+
444
+ ### Line Length
445
+
446
+ 88 characters (Black + Ruff). Run before every commit:
447
+
448
+ ```bash
449
+ make format # black + ruff --fix
450
+ make lint # ruff check
451
+ uv run djlint . --reformat # templates
452
+ ```
453
+
454
+ Pre-commit hooks enforce Black and Ruff automatically.
455
+
456
+ ### Imports
457
+
458
+ ```python
459
+ # Standard library
460
+ import os
461
+ import json
462
+ from datetime import datetime
463
+
464
+ # Third party
465
+ from django.db import models
466
+ from django.http import JsonResponse
467
+ from django.views.decorators.http import require_POST
468
+
469
+ # Local
470
+ from apps.catalog.models import Product
471
+ from apps.pos.services import CartService
472
+ ```
473
+
474
+ ## Commands
475
+
476
+ ```bash
477
+ make run # dev server on 0.0.0.0:8000
478
+ make lint # ruff check
479
+ make format # black + ruff fix
480
+ make tests # pytest (--reuse-db --nomigrations by default)
481
+ make migrate # migrate_schemas --shared + --tenant
482
+ ```
483
+
484
+ ## Resources
485
+
486
+ - **FBV API examples**: `apps/pos/views.py`
487
+ - **Service layer**: `apps/orders/services.py`
488
+ - **PDF pattern**: `apps/catalog/reports.py`
489
+ - **Cart pattern**: `apps/pos/pos_cart.py`, `apps/cart/cart.py`
490
+ - **Pagination**: `apps/catalog/views.py` (api_catalog_products)
491
+ - **Django docs**: <https://docs.djangoproject.com/en/5.2/>