devflow-toolkit 0.3.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.
Files changed (63) hide show
  1. devflow/__init__.py +0 -0
  2. devflow/adapters/__init__.py +47 -0
  3. devflow/adapters/agents.py +49 -0
  4. devflow/adapters/base.py +81 -0
  5. devflow/adapters/claude_code.py +120 -0
  6. devflow/adapters/codex.py +111 -0
  7. devflow/adapters/detector.py +118 -0
  8. devflow/adapters/instructions.py +263 -0
  9. devflow/adapters/kiro.py +211 -0
  10. devflow/adapters/opencode.py +237 -0
  11. devflow/adapters/registry.py +41 -0
  12. devflow/cli.py +39 -0
  13. devflow/commands/__init__.py +6 -0
  14. devflow/commands/approve.py +98 -0
  15. devflow/commands/harness.py +98 -0
  16. devflow/commands/init.py +544 -0
  17. devflow/commands/spec.py +514 -0
  18. devflow/commands/status.py +156 -0
  19. devflow/core/__init__.py +15 -0
  20. devflow/core/architecture.py +469 -0
  21. devflow/core/artifacts/__init__.py +29 -0
  22. devflow/core/artifacts/errors.py +19 -0
  23. devflow/core/artifacts/models.py +82 -0
  24. devflow/core/artifacts/store.py +130 -0
  25. devflow/core/clock.py +67 -0
  26. devflow/core/config.py +706 -0
  27. devflow/core/constitution.py +166 -0
  28. devflow/core/discovery.py +1304 -0
  29. devflow/core/ports.py +159 -0
  30. devflow/core/profile_manager.py +311 -0
  31. devflow/core/project.py +533 -0
  32. devflow/core/spec_workflow.py +932 -0
  33. devflow/core/specification.py +2443 -0
  34. devflow/core/state/__init__.py +75 -0
  35. devflow/core/state/errors.py +39 -0
  36. devflow/core/state/events.py +725 -0
  37. devflow/core/state/locator.py +116 -0
  38. devflow/core/state/lock.py +69 -0
  39. devflow/core/state/manager.py +960 -0
  40. devflow/core/state/models.py +164 -0
  41. devflow/core/state/transitions.py +114 -0
  42. devflow/models/__init__.py +6 -0
  43. devflow/models/profile.py +63 -0
  44. devflow/models/project.py +47 -0
  45. devflow/profiles/ai-rag/constitution/ai-rag-standards.md +49 -0
  46. devflow/profiles/ai-rag/profile.yaml +28 -0
  47. devflow/profiles/base/constitution/architecture.md +58 -0
  48. devflow/profiles/base/constitution/code-standards.md +85 -0
  49. devflow/profiles/base/constitution/constitution.md +85 -0
  50. devflow/profiles/base/profile.yaml +15 -0
  51. devflow/profiles/django-backend/constitution/django-standards.md +64 -0
  52. devflow/profiles/django-backend/profile.yaml +24 -0
  53. devflow/profiles/fastapi-backend/constitution/fastapi-standards.md +59 -0
  54. devflow/profiles/fastapi-backend/profile.yaml +23 -0
  55. devflow/prompts/__init__.py +200 -0
  56. devflow/prompts/discovery.md +119 -0
  57. devflow/prompts/spec.greenfield.md +149 -0
  58. devflow/prompts/spec.md +116 -0
  59. devflow_toolkit-0.3.0.dist-info/METADATA +628 -0
  60. devflow_toolkit-0.3.0.dist-info/RECORD +63 -0
  61. devflow_toolkit-0.3.0.dist-info/WHEEL +4 -0
  62. devflow_toolkit-0.3.0.dist-info/entry_points.txt +2 -0
  63. devflow_toolkit-0.3.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,164 @@
1
+ """Workflow state models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum
7
+ from typing import Any
8
+
9
+ from devflow.core.artifacts.models import ArtifactRecord
10
+
11
+
12
+ class WorkflowStatus(str, Enum):
13
+ """State of a change in the DevFlow lifecycle."""
14
+
15
+ DRAFT = "draft"
16
+ AWAITING_CLARIFICATION = "awaiting_clarification"
17
+ SPEC_APPROVED = "spec_approved"
18
+ PLANNED = "planned"
19
+ PLAN_APPROVED = "plan_approved"
20
+ IMPLEMENTING = "implementing"
21
+ IMPLEMENTED = "implemented"
22
+ REVIEWED = "reviewed"
23
+ REVIEW_APPROVED = "review_approved"
24
+ CHANGES_REQUESTED = "changes_requested"
25
+ DONE = "done"
26
+
27
+
28
+ WORKFLOW_STATUSES = tuple(status.value for status in WorkflowStatus)
29
+
30
+ STAGE_TARGETS = {
31
+ "spec": WorkflowStatus.SPEC_APPROVED.value,
32
+ "plan": WorkflowStatus.PLAN_APPROVED.value,
33
+ "review": WorkflowStatus.REVIEW_APPROVED.value,
34
+ }
35
+
36
+ STAGE_ARTIFACTS = {
37
+ "spec": ("spec",),
38
+ "discovery": ("discovery",),
39
+ "plan": ("plan", "tasks"),
40
+ "review": ("review",),
41
+ }
42
+
43
+ # Discovery is approved together with the specification it informed.
44
+ # Legacy changes created before Discovery have no such artifact.
45
+ STAGE_OPTIONAL_ARTIFACTS = {
46
+ "spec": ("discovery",),
47
+ }
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class ApprovalRecord:
52
+ """A human approval bound to one or more artifact digests."""
53
+
54
+ stage: str
55
+ by: str
56
+ at: str
57
+ artifacts: dict[str, str] = field(default_factory=dict)
58
+ architecture_digest: str = ""
59
+
60
+ def to_dict(self) -> dict[str, Any]:
61
+ return {
62
+ "by": self.by,
63
+ "at": self.at,
64
+ "artifacts": dict(self.artifacts),
65
+ "architecture_digest": self.architecture_digest,
66
+ }
67
+
68
+ @classmethod
69
+ def from_dict(
70
+ cls,
71
+ stage: str,
72
+ data: dict[str, Any],
73
+ ) -> ApprovalRecord:
74
+ artifacts = data.get("artifacts") or {}
75
+ if not isinstance(artifacts, dict):
76
+ artifacts = {}
77
+ return cls(
78
+ stage=stage,
79
+ by=str(data.get("by", "user")),
80
+ at=str(data.get("at", "")),
81
+ artifacts={
82
+ str(key): str(value)
83
+ for key, value in artifacts.items()
84
+ },
85
+ architecture_digest=str(data.get("architecture_digest", "")),
86
+ )
87
+
88
+
89
+ @dataclass
90
+ class WorkflowState:
91
+ """Current projection of a change workflow."""
92
+
93
+ version: int = 1
94
+ spec_id: str = ""
95
+ change: str = ""
96
+ status: str = WorkflowStatus.DRAFT.value
97
+ created_at: str = ""
98
+ updated_at: str = ""
99
+ last_event_seq: int = 0
100
+ artifacts: dict[str, ArtifactRecord] = field(default_factory=dict)
101
+ approvals: dict[str, ApprovalRecord] = field(default_factory=dict)
102
+ current_implementation: str | None = None
103
+ current_review: str | None = None
104
+
105
+ def to_dict(self) -> dict[str, Any]:
106
+ return {
107
+ "version": self.version,
108
+ "spec_id": self.spec_id,
109
+ "change": self.change,
110
+ "status": self.status,
111
+ "created_at": self.created_at,
112
+ "updated_at": self.updated_at,
113
+ "last_event_seq": self.last_event_seq,
114
+ "artifacts": {
115
+ key: record.to_dict()
116
+ for key, record in sorted(self.artifacts.items())
117
+ },
118
+ "approvals": {
119
+ key: approval.to_dict()
120
+ for key, approval in sorted(self.approvals.items())
121
+ },
122
+ "current_implementation": self.current_implementation,
123
+ "current_review": self.current_review,
124
+ }
125
+
126
+ @classmethod
127
+ def from_dict(cls, data: dict[str, Any]) -> WorkflowState:
128
+ artifacts_data = data.get("artifacts") or {}
129
+ approvals_data = data.get("approvals") or {}
130
+ artifacts: dict[str, ArtifactRecord] = {}
131
+ approvals: dict[str, ApprovalRecord] = {}
132
+ if isinstance(artifacts_data, dict):
133
+ for key, value in artifacts_data.items():
134
+ if isinstance(value, dict):
135
+ artifacts[str(key)] = ArtifactRecord.from_dict(value)
136
+ if isinstance(approvals_data, dict):
137
+ for key, value in approvals_data.items():
138
+ if isinstance(value, dict):
139
+ approvals[str(key)] = ApprovalRecord.from_dict(
140
+ str(key),
141
+ value,
142
+ )
143
+ return cls(
144
+ version=int(data.get("version", 1)),
145
+ spec_id=str(data.get("spec_id", "")),
146
+ change=str(data.get("change", "")),
147
+ status=str(data.get("status", WorkflowStatus.DRAFT.value)),
148
+ created_at=str(data.get("created_at", "")),
149
+ updated_at=str(data.get("updated_at", "")),
150
+ last_event_seq=int(data.get("last_event_seq", 0)),
151
+ artifacts=artifacts,
152
+ approvals=approvals,
153
+ current_implementation=_optional_string(
154
+ data.get("current_implementation")
155
+ ),
156
+ current_review=_optional_string(data.get("current_review")),
157
+ )
158
+
159
+
160
+ def _optional_string(value: Any) -> str | None:
161
+ if value is None:
162
+ return None
163
+ text = str(value)
164
+ return text or None
@@ -0,0 +1,114 @@
1
+ """Allowed workflow transitions and their artifact requirements."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from devflow.core.artifacts.models import ArtifactStatus
6
+ from devflow.core.state.errors import InvalidTransitionError
7
+ from devflow.core.state.models import WorkflowState, WorkflowStatus
8
+
9
+
10
+ ALLOWED_TRANSITIONS: dict[str, frozenset[str]] = {
11
+ WorkflowStatus.DRAFT.value: frozenset(
12
+ {
13
+ WorkflowStatus.AWAITING_CLARIFICATION.value,
14
+ WorkflowStatus.SPEC_APPROVED.value,
15
+ }
16
+ ),
17
+ WorkflowStatus.AWAITING_CLARIFICATION.value: frozenset(
18
+ {WorkflowStatus.DRAFT.value}
19
+ ),
20
+ WorkflowStatus.SPEC_APPROVED.value: frozenset(
21
+ {WorkflowStatus.PLANNED.value}
22
+ ),
23
+ WorkflowStatus.PLANNED.value: frozenset(
24
+ {WorkflowStatus.PLAN_APPROVED.value}
25
+ ),
26
+ WorkflowStatus.PLAN_APPROVED.value: frozenset(
27
+ {WorkflowStatus.IMPLEMENTING.value}
28
+ ),
29
+ WorkflowStatus.IMPLEMENTING.value: frozenset(
30
+ {
31
+ WorkflowStatus.IMPLEMENTED.value,
32
+ WorkflowStatus.CHANGES_REQUESTED.value,
33
+ }
34
+ ),
35
+ WorkflowStatus.IMPLEMENTED.value: frozenset(
36
+ {WorkflowStatus.REVIEWED.value}
37
+ ),
38
+ WorkflowStatus.REVIEWED.value: frozenset(
39
+ {
40
+ WorkflowStatus.REVIEW_APPROVED.value,
41
+ WorkflowStatus.CHANGES_REQUESTED.value,
42
+ }
43
+ ),
44
+ WorkflowStatus.REVIEW_APPROVED.value: frozenset(
45
+ {WorkflowStatus.DONE.value}
46
+ ),
47
+ WorkflowStatus.CHANGES_REQUESTED.value: frozenset(
48
+ {WorkflowStatus.IMPLEMENTING.value}
49
+ ),
50
+ WorkflowStatus.DONE.value: frozenset(),
51
+ }
52
+
53
+ TARGET_ARTIFACTS: dict[str, tuple[str, ...]] = {
54
+ WorkflowStatus.SPEC_APPROVED.value: ("spec",),
55
+ WorkflowStatus.PLANNED.value: ("spec",),
56
+ WorkflowStatus.PLAN_APPROVED.value: ("spec", "plan"),
57
+ WorkflowStatus.IMPLEMENTING.value: ("spec", "plan"),
58
+ WorkflowStatus.IMPLEMENTED.value: ("spec", "plan"),
59
+ WorkflowStatus.REVIEWED.value: ("spec", "plan"),
60
+ WorkflowStatus.REVIEW_APPROVED.value: ("spec", "plan", "review"),
61
+ WorkflowStatus.CHANGES_REQUESTED.value: ("spec", "plan"),
62
+ WorkflowStatus.DONE.value: ("spec", "plan", "review"),
63
+ }
64
+
65
+
66
+ def can_transition(current: str, target: str) -> bool:
67
+ """Return whether a workflow may move from one state to another."""
68
+ if current == target:
69
+ return True
70
+ return target in ALLOWED_TRANSITIONS.get(current, frozenset())
71
+
72
+
73
+ def ensure_transition(current: str, target: str) -> None:
74
+ """Raise when a workflow transition is not allowed."""
75
+ if can_transition(current, target):
76
+ return
77
+ msg = (
78
+ f"Invalid workflow transition: {current} -> {target}. "
79
+ "Resolve the current stage before advancing."
80
+ )
81
+ raise InvalidTransitionError(msg)
82
+
83
+
84
+ def required_artifacts(target: str) -> tuple[str, ...]:
85
+ """Return artifact kinds required before entering a target state."""
86
+ return TARGET_ARTIFACTS.get(target, ())
87
+
88
+
89
+ def artifact_blockers(
90
+ state: WorkflowState,
91
+ target: str,
92
+ ) -> tuple[str, ...]:
93
+ """Return artifact kinds that are not approved for a target state."""
94
+ blockers: list[str] = []
95
+ for kind in required_artifacts(target):
96
+ record = state.artifacts.get(kind)
97
+ if record is None:
98
+ blockers.append(f"{kind} (missing)")
99
+ elif record.status != ArtifactStatus.APPROVED.value:
100
+ blockers.append(f"{kind} ({record.status})")
101
+ return tuple(blockers)
102
+
103
+
104
+ def ensure_artifacts_ready(state: WorkflowState, target: str) -> None:
105
+ """Raise when required artifacts are not approved."""
106
+ blockers = artifact_blockers(state, target)
107
+ if not blockers:
108
+ return
109
+ msg = (
110
+ f"Cannot enter {target}: required artifacts are not approved: "
111
+ + ", ".join(blockers)
112
+ + "."
113
+ )
114
+ raise InvalidTransitionError(msg)
@@ -0,0 +1,6 @@
1
+ """Data models used by DevFlow."""
2
+
3
+ from devflow.models.profile import Profile
4
+ from devflow.models.project import Project
5
+
6
+ __all__ = ["Profile", "Project"]
@@ -0,0 +1,63 @@
1
+ """Project profile data model."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class Profile:
10
+ """A reusable project profile bundled with DevFlow."""
11
+
12
+ name: str
13
+ version: str
14
+ description: str
15
+ path: Path
16
+ constitution: tuple[str, ...] = field(default_factory=tuple)
17
+ templates: tuple[str, ...] = field(default_factory=tuple)
18
+ technologies: tuple[str, ...] = field(default_factory=tuple)
19
+ detection_files: tuple[str, ...] = field(default_factory=tuple)
20
+ detection_dependencies: tuple[str, ...] = field(default_factory=tuple)
21
+ detection_indicators: tuple[str, ...] = field(default_factory=tuple)
22
+
23
+ @classmethod
24
+ def from_manifest(
25
+ cls,
26
+ path: Path,
27
+ data: dict[str, Any],
28
+ ) -> "Profile":
29
+ """Build a profile from a profile.yaml manifest."""
30
+ detection = data.get("detection") or {}
31
+ if not isinstance(detection, dict):
32
+ msg = "Profile detection must be a mapping."
33
+ raise ValueError(msg)
34
+
35
+ name = data.get("name")
36
+ if not isinstance(name, str) or not name:
37
+ msg = "Profile manifest must define a non-empty name."
38
+ raise ValueError(msg)
39
+
40
+ def string_tuple(value: Any) -> tuple[str, ...]:
41
+ if value is None:
42
+ return ()
43
+ if not isinstance(value, list):
44
+ msg = f"Profile field must be a list: {value!r}"
45
+ raise ValueError(msg)
46
+ return tuple(str(item) for item in value)
47
+
48
+ return cls(
49
+ name=name,
50
+ version=str(data.get("version", "0.0.0")),
51
+ description=str(data.get("description", "")),
52
+ path=path,
53
+ constitution=string_tuple(data.get("constitution")),
54
+ templates=string_tuple(data.get("templates")),
55
+ technologies=string_tuple(data.get("technologies")),
56
+ detection_files=string_tuple(detection.get("files")),
57
+ detection_dependencies=string_tuple(
58
+ detection.get("dependencies")
59
+ ),
60
+ detection_indicators=string_tuple(
61
+ detection.get("indicators")
62
+ ),
63
+ )
@@ -0,0 +1,47 @@
1
+ """Detected project data model and project modes."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import Enum
5
+ from pathlib import Path
6
+
7
+
8
+ class ProjectMode(str, Enum):
9
+ """Context used to prepare one specification."""
10
+
11
+ EXISTING = "existing"
12
+ GREENFIELD = "greenfield"
13
+
14
+
15
+ PROJECT_MODES = tuple(mode.value for mode in ProjectMode)
16
+
17
+
18
+ def canonical_project_mode(value: str) -> str:
19
+ """Normalize a project mode name."""
20
+ return value.strip().lower().replace("_", "-")
21
+
22
+
23
+ @dataclass
24
+ class Project:
25
+ """A software project inspected by DevFlow."""
26
+
27
+ root: Path
28
+ name: str
29
+ project_type: str
30
+ languages: list[str] = field(default_factory=list)
31
+ frameworks: list[str] = field(default_factory=list)
32
+ databases: list[str] = field(default_factory=list)
33
+ dependencies: set[str] = field(default_factory=set)
34
+ manifests: list[str] = field(default_factory=list)
35
+ source_files: list[str] = field(default_factory=list)
36
+ profiles: list[str] = field(default_factory=list)
37
+
38
+ def to_config(self, profiles: list[str]) -> dict:
39
+ """Return the persistent project section for config.yaml."""
40
+ return {
41
+ "name": self.name,
42
+ "type": self.project_type,
43
+ "languages": self.languages,
44
+ "frameworks": self.frameworks,
45
+ "databases": self.databases,
46
+ "profiles": profiles,
47
+ }
@@ -0,0 +1,49 @@
1
+ # AI and RAG Development Standards
2
+
3
+ ## 1. Retrieval Quality
4
+
5
+ - Define measurable retrieval quality expectations.
6
+ - Preserve document metadata used for filtering and citation.
7
+ - Evaluate changes to chunking, embeddings and ranking.
8
+ - Avoid mixing incompatible embedding models or dimensions.
9
+ - Test retrieval with representative queries and edge cases.
10
+
11
+ ## 2. Data and Ingestion
12
+
13
+ - Make ingestion idempotent where possible.
14
+ - Track document identity, source and version.
15
+ - Handle malformed, empty and duplicate documents.
16
+ - Respect data privacy, retention and access rules.
17
+ - Avoid logging sensitive document content by default.
18
+
19
+ ## 3. Prompts and Generation
20
+
21
+ - Keep prompts versioned and reviewed.
22
+ - Separate system instructions from user content.
23
+ - Treat retrieved content as untrusted input.
24
+ - Require citations when answers depend on retrieved context.
25
+ - Define behavior when no relevant context is found.
26
+
27
+ ## 4. Vector Stores
28
+
29
+ - Use the existing vector store abstraction.
30
+ - Consider collection isolation and access control.
31
+ - Define index creation and migration procedures.
32
+ - Handle unavailable or partially available stores.
33
+ - Monitor latency, recall and storage growth.
34
+
35
+ ## 5. Model Integrations
36
+
37
+ - Isolate providers behind adapters or service boundaries.
38
+ - Configure timeouts, retries and fallback behavior.
39
+ - Avoid hard-coding model names, keys or endpoints.
40
+ - Track token usage and cost where appropriate.
41
+ - Handle provider errors without exposing secrets.
42
+
43
+ ## 6. Evaluation and Testing
44
+
45
+ - Add offline evaluation for retrieval and answer quality.
46
+ - Use deterministic fixtures for unit tests.
47
+ - Separate evaluation data from production data.
48
+ - Track regressions when changing models or prompts.
49
+ - Document assumptions and known limitations.
@@ -0,0 +1,28 @@
1
+ name: ai-rag
2
+ version: "1.0.0"
3
+ description: AI and retrieval-augmented generation profile.
4
+
5
+ detection:
6
+ files: []
7
+ dependencies:
8
+ - langchain
9
+ - llama-index
10
+ - chromadb
11
+ - faiss-cpu
12
+ - qdrant-client
13
+ - sentence-transformers
14
+ indicators:
15
+ - vector_database
16
+ - embeddings
17
+ - retrieval_pipeline
18
+
19
+ constitution:
20
+ - ai-rag-standards.md
21
+
22
+ templates: []
23
+
24
+ technologies:
25
+ - python
26
+ - artificial-intelligence
27
+ - rag
28
+ - vector-databases
@@ -0,0 +1,58 @@
1
+ # Architecture Guidelines
2
+
3
+ ## 1. Design Principles
4
+
5
+ - Apply SOLID principles when they provide clear value.
6
+ - Maintain separation of concerns.
7
+ - Prefer modular and loosely coupled components.
8
+ - Keep business logic independent from infrastructure
9
+ when appropriate.
10
+ - Avoid unnecessary abstractions and complexity.
11
+
12
+ ## 2. Business Logic
13
+
14
+ - Place business logic in the component responsible
15
+ for the corresponding domain.
16
+ - Avoid duplicating business rules.
17
+ - Reuse existing services and domain components
18
+ whenever appropriate.
19
+ - Keep controllers and API views focused on
20
+ request handling and response generation.
21
+
22
+ ## 3. Data Access
23
+
24
+ - Optimize database access according to the actual
25
+ query requirements.
26
+ - Avoid N+1 query problems.
27
+ - Retrieve only the data required by the operation.
28
+ - Use appropriate database indexes and constraints.
29
+ - Consider transaction boundaries and data integrity.
30
+
31
+ ## 4. Integration and Communication
32
+
33
+ - Isolate external integrations behind appropriate
34
+ interfaces or service components.
35
+ - Handle timeouts, connection errors and unexpected
36
+ responses.
37
+ - Consider retries and recovery mechanisms when needed.
38
+ - Avoid tightly coupling internal business logic
39
+ to external API implementations.
40
+
41
+ ## 5. Reliability
42
+
43
+ - Consider partial failures and unexpected execution
44
+ states.
45
+ - Preserve data consistency.
46
+ - Avoid duplicate processing where operations can
47
+ be repeated.
48
+ - Implement observability and traceability when
49
+ appropriate.
50
+
51
+ ## 6. Performance
52
+
53
+ - Identify actual performance bottlenecks before
54
+ optimizing.
55
+ - Avoid unnecessary database queries and processing.
56
+ - Prefer efficient data access patterns.
57
+ - Consider asynchronous processing for workloads
58
+ that justify it.
@@ -0,0 +1,85 @@
1
+ # Code Standards
2
+
3
+ ## 1. General Principles
4
+
5
+ - Follow clean code principles and language conventions.
6
+ - Prioritize readability and maintainability.
7
+ - Use descriptive names for variables, functions and classes.
8
+ - Avoid unnecessary abbreviations.
9
+ - Avoid over-engineering.
10
+
11
+ ## 2. Formatting
12
+
13
+ - Maximum line length: 79 characters.
14
+ - Use 2 spaces for indentation.
15
+ - Never use tabs.
16
+ - Keep formatting consistent across the project.
17
+ - If a statement exceeds the maximum line length,
18
+ split it into multiple lines.
19
+
20
+ Note: The 2-space indentation is a project-specific
21
+ convention and overrides the default PEP 8 indentation
22
+ recommendation.
23
+
24
+ ## 3. Imports
25
+
26
+ - Use absolute imports within the project.
27
+ - Never use relative imports between project modules.
28
+ - Follow this import order:
29
+ 1. Python standard library.
30
+ 2. Third-party libraries.
31
+ 3. Framework imports, when applicable.
32
+ 4. Local project imports.
33
+ - Sort imports alphabetically within each group.
34
+
35
+ Example:
36
+
37
+ import logging
38
+ from typing import Any
39
+
40
+ import requests
41
+
42
+ from framework import transaction
43
+
44
+ from project.models.entity import Entity
45
+
46
+ ## 4. Naming Conventions
47
+
48
+ - Use descriptive names for variables and functions.
49
+ - Use PascalCase for classes.
50
+ - Use snake_case for variables, functions and modules.
51
+ - Boolean variables should use prefixes such as:
52
+ is_, has_, can_ or should_.
53
+ - Avoid ambiguous or overly abbreviated names.
54
+
55
+ ## 5. Type Hints
56
+
57
+ - Add type hints to function parameters.
58
+ - Add return type annotations to functions.
59
+ - Use typing for complex types when appropriate.
60
+ - Use Any from the typing module when a dynamic
61
+ type is genuinely necessary.
62
+ - Avoid using Any when a more precise type is possible.
63
+
64
+ ## 6. Comments and Documentation
65
+
66
+ - Write code, comments, docstrings and technical
67
+ documentation in English.
68
+ - Use Spanish only for user-facing content when needed.
69
+ - Avoid comments that merely repeat the code.
70
+ - Explain complex logic, non-obvious decisions
71
+ and important edge cases.
72
+ - Add docstrings to public APIs, complex functions
73
+ and non-obvious classes.
74
+ - Use Google-style docstrings.
75
+ - Avoid unnecessary docstrings for simple functions
76
+ and standard framework patterns.
77
+
78
+ ## 7. Function Design
79
+
80
+ - Keep functions focused on a single responsibility.
81
+ - Avoid unnecessarily large functions.
82
+ - When a function has more than two parameters,
83
+ place each parameter on a separate line.
84
+ - Apply the same multiline formatting when a function
85
+ declaration exceeds the maximum line length.