doit-toolkit-cli 0.1.9__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.
- doit_cli/__init__.py +1356 -0
- doit_cli/cli/__init__.py +26 -0
- doit_cli/cli/analytics_command.py +616 -0
- doit_cli/cli/context_command.py +213 -0
- doit_cli/cli/diagram_command.py +304 -0
- doit_cli/cli/fixit_command.py +641 -0
- doit_cli/cli/hooks_command.py +211 -0
- doit_cli/cli/init_command.py +613 -0
- doit_cli/cli/memory_command.py +293 -0
- doit_cli/cli/status_command.py +117 -0
- doit_cli/cli/sync_prompts_command.py +248 -0
- doit_cli/cli/validate_command.py +196 -0
- doit_cli/cli/verify_command.py +204 -0
- doit_cli/cli/workflow_mixin.py +224 -0
- doit_cli/cli/xref_command.py +555 -0
- doit_cli/formatters/__init__.py +8 -0
- doit_cli/formatters/base.py +38 -0
- doit_cli/formatters/json_formatter.py +126 -0
- doit_cli/formatters/markdown_formatter.py +97 -0
- doit_cli/formatters/rich_formatter.py +257 -0
- doit_cli/main.py +49 -0
- doit_cli/models/__init__.py +139 -0
- doit_cli/models/agent.py +74 -0
- doit_cli/models/analytics_models.py +384 -0
- doit_cli/models/context_config.py +464 -0
- doit_cli/models/crossref_models.py +182 -0
- doit_cli/models/diagram_models.py +363 -0
- doit_cli/models/fixit_models.py +355 -0
- doit_cli/models/hook_config.py +125 -0
- doit_cli/models/project.py +91 -0
- doit_cli/models/results.py +121 -0
- doit_cli/models/search_models.py +228 -0
- doit_cli/models/status_models.py +195 -0
- doit_cli/models/sync_models.py +146 -0
- doit_cli/models/template.py +77 -0
- doit_cli/models/validation_models.py +175 -0
- doit_cli/models/workflow_models.py +319 -0
- doit_cli/prompts/__init__.py +5 -0
- doit_cli/prompts/fixit_prompts.py +344 -0
- doit_cli/prompts/interactive.py +390 -0
- doit_cli/rules/__init__.py +5 -0
- doit_cli/rules/builtin_rules.py +160 -0
- doit_cli/services/__init__.py +79 -0
- doit_cli/services/agent_detector.py +168 -0
- doit_cli/services/analytics_service.py +218 -0
- doit_cli/services/architecture_generator.py +290 -0
- doit_cli/services/backup_service.py +204 -0
- doit_cli/services/config_loader.py +113 -0
- doit_cli/services/context_loader.py +1121 -0
- doit_cli/services/coverage_calculator.py +142 -0
- doit_cli/services/crossref_service.py +237 -0
- doit_cli/services/cycle_time_calculator.py +134 -0
- doit_cli/services/date_inferrer.py +349 -0
- doit_cli/services/diagram_service.py +337 -0
- doit_cli/services/drift_detector.py +109 -0
- doit_cli/services/entity_parser.py +301 -0
- doit_cli/services/er_diagram_generator.py +197 -0
- doit_cli/services/fixit_service.py +699 -0
- doit_cli/services/github_service.py +192 -0
- doit_cli/services/hook_manager.py +258 -0
- doit_cli/services/hook_validator.py +528 -0
- doit_cli/services/input_validator.py +322 -0
- doit_cli/services/memory_search.py +527 -0
- doit_cli/services/mermaid_validator.py +334 -0
- doit_cli/services/prompt_transformer.py +91 -0
- doit_cli/services/prompt_writer.py +133 -0
- doit_cli/services/query_interpreter.py +428 -0
- doit_cli/services/report_exporter.py +219 -0
- doit_cli/services/report_generator.py +256 -0
- doit_cli/services/requirement_parser.py +112 -0
- doit_cli/services/roadmap_summarizer.py +209 -0
- doit_cli/services/rule_engine.py +443 -0
- doit_cli/services/scaffolder.py +215 -0
- doit_cli/services/score_calculator.py +172 -0
- doit_cli/services/section_parser.py +204 -0
- doit_cli/services/spec_scanner.py +327 -0
- doit_cli/services/state_manager.py +355 -0
- doit_cli/services/status_reporter.py +143 -0
- doit_cli/services/task_parser.py +347 -0
- doit_cli/services/template_manager.py +710 -0
- doit_cli/services/template_reader.py +158 -0
- doit_cli/services/user_journey_generator.py +214 -0
- doit_cli/services/user_story_parser.py +232 -0
- doit_cli/services/validation_service.py +188 -0
- doit_cli/services/validator.py +232 -0
- doit_cli/services/velocity_tracker.py +173 -0
- doit_cli/services/workflow_engine.py +405 -0
- doit_cli/templates/agent-file-template.md +28 -0
- doit_cli/templates/checklist-template.md +39 -0
- doit_cli/templates/commands/doit.checkin.md +363 -0
- doit_cli/templates/commands/doit.constitution.md +187 -0
- doit_cli/templates/commands/doit.documentit.md +485 -0
- doit_cli/templates/commands/doit.fixit.md +181 -0
- doit_cli/templates/commands/doit.implementit.md +265 -0
- doit_cli/templates/commands/doit.planit.md +262 -0
- doit_cli/templates/commands/doit.reviewit.md +355 -0
- doit_cli/templates/commands/doit.roadmapit.md +368 -0
- doit_cli/templates/commands/doit.scaffoldit.md +458 -0
- doit_cli/templates/commands/doit.specit.md +521 -0
- doit_cli/templates/commands/doit.taskit.md +304 -0
- doit_cli/templates/commands/doit.testit.md +277 -0
- doit_cli/templates/config/context.yaml +134 -0
- doit_cli/templates/config/hooks.yaml +93 -0
- doit_cli/templates/config/validation-rules.yaml +64 -0
- doit_cli/templates/github-issue-templates/epic.yml +78 -0
- doit_cli/templates/github-issue-templates/feature.yml +116 -0
- doit_cli/templates/github-issue-templates/task.yml +129 -0
- doit_cli/templates/hooks/.gitkeep +0 -0
- doit_cli/templates/hooks/post-commit.sh +25 -0
- doit_cli/templates/hooks/post-merge.sh +75 -0
- doit_cli/templates/hooks/pre-commit.sh +17 -0
- doit_cli/templates/hooks/pre-push.sh +18 -0
- doit_cli/templates/memory/completed_roadmap.md +50 -0
- doit_cli/templates/memory/constitution.md +125 -0
- doit_cli/templates/memory/roadmap.md +61 -0
- doit_cli/templates/plan-template.md +146 -0
- doit_cli/templates/scripts/bash/check-prerequisites.sh +166 -0
- doit_cli/templates/scripts/bash/common.sh +156 -0
- doit_cli/templates/scripts/bash/create-new-feature.sh +297 -0
- doit_cli/templates/scripts/bash/setup-plan.sh +61 -0
- doit_cli/templates/scripts/bash/update-agent-context.sh +675 -0
- doit_cli/templates/scripts/powershell/check-prerequisites.ps1 +148 -0
- doit_cli/templates/scripts/powershell/common.ps1 +137 -0
- doit_cli/templates/scripts/powershell/create-new-feature.ps1 +283 -0
- doit_cli/templates/scripts/powershell/setup-plan.ps1 +61 -0
- doit_cli/templates/scripts/powershell/update-agent-context.ps1 +406 -0
- doit_cli/templates/spec-template.md +159 -0
- doit_cli/templates/tasks-template.md +313 -0
- doit_cli/templates/vscode-settings.json +14 -0
- doit_toolkit_cli-0.1.9.dist-info/METADATA +324 -0
- doit_toolkit_cli-0.1.9.dist-info/RECORD +134 -0
- doit_toolkit_cli-0.1.9.dist-info/WHEEL +4 -0
- doit_toolkit_cli-0.1.9.dist-info/entry_points.txt +2 -0
- doit_toolkit_cli-0.1.9.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""Models for automatic Mermaid diagram generation."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DiagramType(str, Enum):
|
|
10
|
+
"""Type of Mermaid diagram to generate.
|
|
11
|
+
|
|
12
|
+
Values:
|
|
13
|
+
USER_JOURNEY: Flowchart showing user story flows
|
|
14
|
+
ER_DIAGRAM: Entity relationship diagram from Key Entities
|
|
15
|
+
ARCHITECTURE: Architecture diagram from plan.md
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
USER_JOURNEY = "user-journey"
|
|
19
|
+
ER_DIAGRAM = "er-diagram"
|
|
20
|
+
ARCHITECTURE = "architecture"
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_string(cls, value: str) -> "DiagramType":
|
|
24
|
+
"""Parse a diagram type string.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
value: Type string (e.g., "user-journey", "er-diagram")
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Corresponding DiagramType enum value.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If value doesn't match any type.
|
|
34
|
+
"""
|
|
35
|
+
normalized = value.lower().strip().replace("_", "-")
|
|
36
|
+
mapping = {
|
|
37
|
+
"user-journey": cls.USER_JOURNEY,
|
|
38
|
+
"userjourney": cls.USER_JOURNEY,
|
|
39
|
+
"flowchart": cls.USER_JOURNEY,
|
|
40
|
+
"er-diagram": cls.ER_DIAGRAM,
|
|
41
|
+
"erdiagram": cls.ER_DIAGRAM,
|
|
42
|
+
"er": cls.ER_DIAGRAM,
|
|
43
|
+
"architecture": cls.ARCHITECTURE,
|
|
44
|
+
"arch": cls.ARCHITECTURE,
|
|
45
|
+
}
|
|
46
|
+
if normalized not in mapping:
|
|
47
|
+
raise ValueError(f"Unknown diagram type: {value}")
|
|
48
|
+
return mapping[normalized]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Cardinality(str, Enum):
|
|
52
|
+
"""ER diagram relationship cardinality.
|
|
53
|
+
|
|
54
|
+
Values map to Mermaid ER diagram notation.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
ONE_TO_ONE = "||--||"
|
|
58
|
+
ONE_TO_MANY = "||--o{"
|
|
59
|
+
MANY_TO_ONE = "}o--||"
|
|
60
|
+
MANY_TO_MANY = "}o--o{"
|
|
61
|
+
ZERO_OR_ONE_TO_ONE = "|o--||"
|
|
62
|
+
ONE_TO_ZERO_OR_ONE = "||--o|"
|
|
63
|
+
ZERO_OR_ONE_TO_MANY = "|o--o{"
|
|
64
|
+
ZERO_OR_MANY_TO_ONE = "}o--o|"
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def from_keywords(cls, text: str) -> "Cardinality":
|
|
68
|
+
"""Infer cardinality from natural language keywords.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
text: Description text containing relationship keywords
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Inferred Cardinality (defaults to ONE_TO_MANY if unclear)
|
|
75
|
+
"""
|
|
76
|
+
text_lower = text.lower()
|
|
77
|
+
|
|
78
|
+
# Check for many-to-many first
|
|
79
|
+
if "many-to-many" in text_lower or "many to many" in text_lower:
|
|
80
|
+
return cls.MANY_TO_MANY
|
|
81
|
+
|
|
82
|
+
# Check for one-to-one
|
|
83
|
+
if "has one" in text_lower or "one-to-one" in text_lower:
|
|
84
|
+
return cls.ONE_TO_ONE
|
|
85
|
+
|
|
86
|
+
# Check for belongs to (reverse relationship)
|
|
87
|
+
if "belongs to" in text_lower:
|
|
88
|
+
return cls.MANY_TO_ONE
|
|
89
|
+
|
|
90
|
+
# Default for "has many", "contains", etc.
|
|
91
|
+
if "has many" in text_lower or "contains" in text_lower or "owns" in text_lower:
|
|
92
|
+
return cls.ONE_TO_MANY
|
|
93
|
+
|
|
94
|
+
# Default to one-to-many for unrecognized patterns
|
|
95
|
+
return cls.ONE_TO_MANY
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def mermaid_notation(self) -> str:
|
|
99
|
+
"""Return the Mermaid ER diagram notation."""
|
|
100
|
+
return self.value
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class AcceptanceScenario:
|
|
105
|
+
"""Represents a Given/When/Then acceptance scenario.
|
|
106
|
+
|
|
107
|
+
Attributes:
|
|
108
|
+
id: Unique ID (e.g., "US1_S1")
|
|
109
|
+
scenario_number: Scenario number within story (1, 2, 3...)
|
|
110
|
+
given_clause: Initial state condition
|
|
111
|
+
when_clause: Action or trigger
|
|
112
|
+
then_clause: Expected outcome
|
|
113
|
+
raw_text: Original text from spec
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
id: str
|
|
117
|
+
scenario_number: int
|
|
118
|
+
given_clause: str
|
|
119
|
+
when_clause: str
|
|
120
|
+
then_clause: str
|
|
121
|
+
raw_text: str = ""
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def node_label(self) -> str:
|
|
125
|
+
"""Generate a label suitable for flowchart node."""
|
|
126
|
+
# Truncate to reasonable length for display
|
|
127
|
+
max_len = 40
|
|
128
|
+
given_short = (
|
|
129
|
+
self.given_clause[:max_len] + "..."
|
|
130
|
+
if len(self.given_clause) > max_len
|
|
131
|
+
else self.given_clause
|
|
132
|
+
)
|
|
133
|
+
return given_short
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class ParsedUserStory:
|
|
138
|
+
"""Structured representation of a user story extracted from spec.
|
|
139
|
+
|
|
140
|
+
Attributes:
|
|
141
|
+
id: Unique ID (e.g., "US1")
|
|
142
|
+
story_number: Story number (1, 2, 3...)
|
|
143
|
+
title: Brief title from header
|
|
144
|
+
priority: Priority level (P1, P2, P3, P4)
|
|
145
|
+
description: Full user story description
|
|
146
|
+
scenarios: List of acceptance scenarios
|
|
147
|
+
raw_text: Original markdown text
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
id: str
|
|
151
|
+
story_number: int
|
|
152
|
+
title: str
|
|
153
|
+
priority: str
|
|
154
|
+
description: str = ""
|
|
155
|
+
scenarios: list[AcceptanceScenario] = field(default_factory=list)
|
|
156
|
+
raw_text: str = ""
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def subgraph_id(self) -> str:
|
|
160
|
+
"""Generate unique subgraph ID for Mermaid flowchart."""
|
|
161
|
+
return f"US{self.story_number}"
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def subgraph_label(self) -> str:
|
|
165
|
+
"""Generate subgraph label for Mermaid flowchart."""
|
|
166
|
+
return f"US{self.story_number} - {self.title}"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class EntityAttribute:
|
|
171
|
+
"""Attribute within an entity definition.
|
|
172
|
+
|
|
173
|
+
Attributes:
|
|
174
|
+
name: Attribute name
|
|
175
|
+
attr_type: Data type (string, int, uuid, etc.)
|
|
176
|
+
is_pk: Primary key flag
|
|
177
|
+
is_fk: Foreign key flag
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
name: str
|
|
181
|
+
attr_type: str = "string"
|
|
182
|
+
is_pk: bool = False
|
|
183
|
+
is_fk: bool = False
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def mermaid_line(self) -> str:
|
|
187
|
+
"""Generate Mermaid ER attribute line."""
|
|
188
|
+
pk_marker = " PK" if self.is_pk else ""
|
|
189
|
+
fk_marker = " FK" if self.is_fk else ""
|
|
190
|
+
return f" {self.attr_type} {self.name}{pk_marker}{fk_marker}"
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@dataclass
|
|
194
|
+
class EntityRelationship:
|
|
195
|
+
"""Relationship between two entities.
|
|
196
|
+
|
|
197
|
+
Attributes:
|
|
198
|
+
source_entity: Source entity name
|
|
199
|
+
target_entity: Target entity name
|
|
200
|
+
cardinality: Relationship cardinality
|
|
201
|
+
label: Relationship label (verb phrase)
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
source_entity: str
|
|
205
|
+
target_entity: str
|
|
206
|
+
cardinality: Cardinality
|
|
207
|
+
label: str = ""
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def mermaid_line(self) -> str:
|
|
211
|
+
"""Generate Mermaid ER relationship line."""
|
|
212
|
+
label_part = f' : "{self.label}"' if self.label else ""
|
|
213
|
+
return f" {self.source_entity} {self.cardinality.mermaid_notation} {self.target_entity}{label_part}"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@dataclass
|
|
217
|
+
class ParsedEntity:
|
|
218
|
+
"""Structured representation of an entity from Key Entities section.
|
|
219
|
+
|
|
220
|
+
Attributes:
|
|
221
|
+
name: Entity name (e.g., "User")
|
|
222
|
+
description: What the entity represents
|
|
223
|
+
raw_text: Original markdown text
|
|
224
|
+
attributes: Parsed attributes
|
|
225
|
+
relationships: Relationships to other entities
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
name: str
|
|
229
|
+
description: str = ""
|
|
230
|
+
raw_text: str = ""
|
|
231
|
+
attributes: list[EntityAttribute] = field(default_factory=list)
|
|
232
|
+
relationships: list[EntityRelationship] = field(default_factory=list)
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def mermaid_entity_block(self) -> str:
|
|
236
|
+
"""Generate Mermaid ER entity block."""
|
|
237
|
+
if not self.attributes:
|
|
238
|
+
return f" {self.name}"
|
|
239
|
+
|
|
240
|
+
lines = [f" {self.name} {{"]
|
|
241
|
+
for attr in self.attributes:
|
|
242
|
+
lines.append(attr.mermaid_line)
|
|
243
|
+
lines.append(" }")
|
|
244
|
+
return "\n".join(lines)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class DiagramSection:
|
|
249
|
+
"""Represents an AUTO-GENERATED section in a file.
|
|
250
|
+
|
|
251
|
+
Attributes:
|
|
252
|
+
section_name: Section identifier (e.g., "user-journey")
|
|
253
|
+
start_line: Start line number (BEGIN marker)
|
|
254
|
+
end_line: End line number (END marker)
|
|
255
|
+
content: Content between markers
|
|
256
|
+
"""
|
|
257
|
+
|
|
258
|
+
section_name: str
|
|
259
|
+
start_line: int
|
|
260
|
+
end_line: int
|
|
261
|
+
content: str = ""
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def begin_marker(self) -> str:
|
|
265
|
+
"""Generate the BEGIN marker comment."""
|
|
266
|
+
return f'<!-- BEGIN:AUTO-GENERATED section="{self.section_name}" -->'
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def end_marker(self) -> str:
|
|
270
|
+
"""Generate the END marker comment."""
|
|
271
|
+
return "<!-- END:AUTO-GENERATED -->"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@dataclass
|
|
275
|
+
class ValidationResult:
|
|
276
|
+
"""Result of Mermaid syntax validation.
|
|
277
|
+
|
|
278
|
+
Attributes:
|
|
279
|
+
passed: Overall pass/fail
|
|
280
|
+
errors: Syntax errors found
|
|
281
|
+
warnings: Non-blocking warnings
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
passed: bool
|
|
285
|
+
errors: list[str] = field(default_factory=list)
|
|
286
|
+
warnings: list[str] = field(default_factory=list)
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def error_count(self) -> int:
|
|
290
|
+
"""Number of errors."""
|
|
291
|
+
return len(self.errors)
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def warning_count(self) -> int:
|
|
295
|
+
"""Number of warnings."""
|
|
296
|
+
return len(self.warnings)
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def is_valid(self) -> bool:
|
|
300
|
+
"""Alias for passed."""
|
|
301
|
+
return self.passed
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@dataclass
|
|
305
|
+
class GeneratedDiagram:
|
|
306
|
+
"""Result of diagram generation.
|
|
307
|
+
|
|
308
|
+
Attributes:
|
|
309
|
+
id: Unique diagram ID
|
|
310
|
+
diagram_type: Type of diagram generated
|
|
311
|
+
mermaid_content: Generated Mermaid syntax
|
|
312
|
+
is_valid: Whether syntax validation passed
|
|
313
|
+
validation: Detailed validation result
|
|
314
|
+
node_count: Number of nodes/entities in diagram
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
id: str
|
|
318
|
+
diagram_type: DiagramType
|
|
319
|
+
mermaid_content: str
|
|
320
|
+
is_valid: bool = True
|
|
321
|
+
validation: Optional[ValidationResult] = None
|
|
322
|
+
node_count: int = 0
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def wrapped_content(self) -> str:
|
|
326
|
+
"""Return content wrapped in Mermaid code fence."""
|
|
327
|
+
return f"```mermaid\n{self.mermaid_content}\n```"
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@dataclass
|
|
331
|
+
class DiagramResult:
|
|
332
|
+
"""Result of diagram generation operation.
|
|
333
|
+
|
|
334
|
+
Attributes:
|
|
335
|
+
file_path: Path to the file that was processed
|
|
336
|
+
diagrams: List of generated diagrams
|
|
337
|
+
sections_found: AUTO-GENERATED sections found in file
|
|
338
|
+
sections_updated: Sections that were updated
|
|
339
|
+
success: Overall success status
|
|
340
|
+
error: Error message if failed
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
file_path: Path
|
|
344
|
+
diagrams: list[GeneratedDiagram] = field(default_factory=list)
|
|
345
|
+
sections_found: list[DiagramSection] = field(default_factory=list)
|
|
346
|
+
sections_updated: list[str] = field(default_factory=list)
|
|
347
|
+
success: bool = True
|
|
348
|
+
error: Optional[str] = None
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def total_diagrams(self) -> int:
|
|
352
|
+
"""Total diagrams generated."""
|
|
353
|
+
return len(self.diagrams)
|
|
354
|
+
|
|
355
|
+
@property
|
|
356
|
+
def valid_diagrams(self) -> int:
|
|
357
|
+
"""Count of valid diagrams."""
|
|
358
|
+
return sum(1 for d in self.diagrams if d.is_valid)
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def total_nodes(self) -> int:
|
|
362
|
+
"""Total nodes across all diagrams."""
|
|
363
|
+
return sum(d.node_count for d in self.diagrams)
|