elspais 0.11.1__py3-none-any.whl → 0.43.5__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 (148) hide show
  1. elspais/__init__.py +2 -11
  2. elspais/{sponsors/__init__.py → associates.py} +102 -58
  3. elspais/cli.py +395 -79
  4. elspais/commands/__init__.py +9 -3
  5. elspais/commands/analyze.py +121 -173
  6. elspais/commands/changed.py +15 -30
  7. elspais/commands/config_cmd.py +13 -16
  8. elspais/commands/edit.py +60 -44
  9. elspais/commands/example_cmd.py +319 -0
  10. elspais/commands/hash_cmd.py +167 -183
  11. elspais/commands/health.py +1177 -0
  12. elspais/commands/index.py +98 -114
  13. elspais/commands/init.py +103 -26
  14. elspais/commands/reformat_cmd.py +41 -444
  15. elspais/commands/rules_cmd.py +7 -3
  16. elspais/commands/trace.py +444 -321
  17. elspais/commands/validate.py +195 -415
  18. elspais/config/__init__.py +799 -5
  19. elspais/{core/content_rules.py → content_rules.py} +20 -3
  20. elspais/docs/cli/assertions.md +67 -0
  21. elspais/docs/cli/commands.md +304 -0
  22. elspais/docs/cli/config.md +262 -0
  23. elspais/docs/cli/format.md +66 -0
  24. elspais/docs/cli/git.md +45 -0
  25. elspais/docs/cli/health.md +190 -0
  26. elspais/docs/cli/hierarchy.md +60 -0
  27. elspais/docs/cli/ignore.md +72 -0
  28. elspais/docs/cli/mcp.md +245 -0
  29. elspais/docs/cli/quickstart.md +58 -0
  30. elspais/docs/cli/traceability.md +89 -0
  31. elspais/docs/cli/validation.md +96 -0
  32. elspais/graph/GraphNode.py +383 -0
  33. elspais/graph/__init__.py +40 -0
  34. elspais/graph/annotators.py +927 -0
  35. elspais/graph/builder.py +1886 -0
  36. elspais/graph/deserializer.py +248 -0
  37. elspais/graph/factory.py +284 -0
  38. elspais/graph/metrics.py +127 -0
  39. elspais/graph/mutations.py +161 -0
  40. elspais/graph/parsers/__init__.py +156 -0
  41. elspais/graph/parsers/code.py +213 -0
  42. elspais/graph/parsers/comments.py +112 -0
  43. elspais/graph/parsers/config_helpers.py +29 -0
  44. elspais/graph/parsers/heredocs.py +225 -0
  45. elspais/graph/parsers/journey.py +131 -0
  46. elspais/graph/parsers/remainder.py +79 -0
  47. elspais/graph/parsers/requirement.py +347 -0
  48. elspais/graph/parsers/results/__init__.py +6 -0
  49. elspais/graph/parsers/results/junit_xml.py +229 -0
  50. elspais/graph/parsers/results/pytest_json.py +313 -0
  51. elspais/graph/parsers/test.py +305 -0
  52. elspais/graph/relations.py +78 -0
  53. elspais/graph/serialize.py +216 -0
  54. elspais/html/__init__.py +8 -0
  55. elspais/html/generator.py +731 -0
  56. elspais/html/templates/trace_view.html.j2 +2151 -0
  57. elspais/mcp/__init__.py +47 -29
  58. elspais/mcp/__main__.py +5 -1
  59. elspais/mcp/file_mutations.py +138 -0
  60. elspais/mcp/server.py +2016 -247
  61. elspais/testing/__init__.py +4 -4
  62. elspais/testing/config.py +3 -0
  63. elspais/testing/mapper.py +1 -1
  64. elspais/testing/result_parser.py +25 -21
  65. elspais/testing/scanner.py +301 -12
  66. elspais/utilities/__init__.py +1 -0
  67. elspais/utilities/docs_loader.py +115 -0
  68. elspais/utilities/git.py +607 -0
  69. elspais/{core → utilities}/hasher.py +8 -22
  70. elspais/utilities/md_renderer.py +189 -0
  71. elspais/{core → utilities}/patterns.py +58 -57
  72. elspais/utilities/reference_config.py +626 -0
  73. elspais/validation/__init__.py +19 -0
  74. elspais/validation/format.py +264 -0
  75. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
  76. elspais-0.43.5.dist-info/RECORD +80 -0
  77. elspais/config/defaults.py +0 -173
  78. elspais/config/loader.py +0 -494
  79. elspais/core/__init__.py +0 -21
  80. elspais/core/git.py +0 -352
  81. elspais/core/models.py +0 -320
  82. elspais/core/parser.py +0 -640
  83. elspais/core/rules.py +0 -514
  84. elspais/mcp/context.py +0 -171
  85. elspais/mcp/serializers.py +0 -112
  86. elspais/reformat/__init__.py +0 -50
  87. elspais/reformat/detector.py +0 -119
  88. elspais/reformat/hierarchy.py +0 -246
  89. elspais/reformat/line_breaks.py +0 -220
  90. elspais/reformat/prompts.py +0 -123
  91. elspais/reformat/transformer.py +0 -264
  92. elspais/trace_view/__init__.py +0 -54
  93. elspais/trace_view/coverage.py +0 -183
  94. elspais/trace_view/generators/__init__.py +0 -12
  95. elspais/trace_view/generators/base.py +0 -329
  96. elspais/trace_view/generators/csv.py +0 -122
  97. elspais/trace_view/generators/markdown.py +0 -175
  98. elspais/trace_view/html/__init__.py +0 -31
  99. elspais/trace_view/html/generator.py +0 -1006
  100. elspais/trace_view/html/templates/base.html +0 -283
  101. elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
  102. elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
  103. elspais/trace_view/html/templates/components/legend_modal.html +0 -69
  104. elspais/trace_view/html/templates/components/review_panel.html +0 -118
  105. elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
  106. elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
  107. elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
  108. elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
  109. elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
  110. elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
  111. elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
  112. elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
  113. elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
  114. elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
  115. elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
  116. elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
  117. elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
  118. elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
  119. elspais/trace_view/html/templates/partials/scripts.js +0 -1741
  120. elspais/trace_view/html/templates/partials/styles.css +0 -1756
  121. elspais/trace_view/models.py +0 -353
  122. elspais/trace_view/review/__init__.py +0 -60
  123. elspais/trace_view/review/branches.py +0 -1149
  124. elspais/trace_view/review/models.py +0 -1205
  125. elspais/trace_view/review/position.py +0 -609
  126. elspais/trace_view/review/server.py +0 -1056
  127. elspais/trace_view/review/status.py +0 -470
  128. elspais/trace_view/review/storage.py +0 -1367
  129. elspais/trace_view/scanning.py +0 -213
  130. elspais/trace_view/specs/README.md +0 -84
  131. elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
  132. elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
  133. elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
  134. elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
  135. elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
  136. elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
  137. elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
  138. elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
  139. elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
  140. elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
  141. elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
  142. elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
  143. elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
  144. elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
  145. elspais-0.11.1.dist-info/RECORD +0 -101
  146. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
  147. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
  148. {elspais-0.11.1.dist-info → elspais-0.43.5.dist-info}/licenses/LICENSE +0 -0
@@ -1,353 +0,0 @@
1
- # Implements: REQ-int-d00004 (Model Adapter)
2
- """
3
- elspais.trace_view.models - Data models for trace-view.
4
-
5
- Provides TraceViewRequirement which wraps core Requirement with:
6
- - Git state tracking (uncommitted, modified, moved)
7
- - Test coverage information
8
- - Implementation file references
9
- """
10
-
11
- from dataclasses import dataclass, field
12
- from pathlib import Path
13
- from typing import Dict, List, Optional, Set, Tuple
14
-
15
- from elspais.core.models import Requirement as CoreRequirement
16
-
17
-
18
- @dataclass
19
- class TestInfo:
20
- """Represents test coverage for a requirement.
21
-
22
- Attributes:
23
- test_count: Number of automated tests
24
- manual_test_count: Number of manual tests
25
- test_status: Status ('not_tested', 'passed', 'failed', 'error', 'skipped')
26
- test_details: List of test result details
27
- notes: Additional notes about testing
28
- """
29
-
30
- test_count: int = 0
31
- manual_test_count: int = 0
32
- test_status: str = "not_tested"
33
- test_details: List[Dict] = field(default_factory=list)
34
- notes: str = ""
35
-
36
-
37
- @dataclass
38
- class GitChangeInfo:
39
- """Container for git change state.
40
-
41
- Injected into TraceViewRequirement rather than using a global singleton.
42
- This allows for better testing and explicit dependency management.
43
-
44
- Attributes:
45
- uncommitted_files: Set of spec-relative paths with uncommitted changes
46
- untracked_files: Set of spec-relative paths that are untracked (new)
47
- branch_changed_files: Set of spec-relative paths changed vs main branch
48
- committed_req_locations: Map of requirement ID to committed file path
49
- """
50
-
51
- uncommitted_files: Set[str] = field(default_factory=set)
52
- untracked_files: Set[str] = field(default_factory=set)
53
- branch_changed_files: Set[str] = field(default_factory=set)
54
- committed_req_locations: Dict[str, str] = field(default_factory=dict)
55
-
56
-
57
- @dataclass
58
- class TraceViewRequirement:
59
- """Requirement enriched with trace-view data.
60
-
61
- Wraps a core Requirement and adds:
62
- - Git state properties (is_uncommitted, is_branch_changed, is_moved)
63
- - Test coverage information
64
- - Implementation file references
65
-
66
- Implements: REQ-int-d00004-A (wraps elspais.core.models.Requirement)
67
- Implements: REQ-int-d00004-B (git state injected, not global)
68
- Implements: REQ-int-d00004-C (implementation files stored per-requirement)
69
- """
70
-
71
- core: CoreRequirement
72
- test_info: Optional[TestInfo] = None
73
- implementation_files: List[Tuple[str, int]] = field(default_factory=list)
74
- git_info: Optional[GitChangeInfo] = None
75
- external_spec_path: Optional[str] = None
76
-
77
- # --- Delegated core properties ---
78
-
79
- @property
80
- def id(self) -> str:
81
- """Requirement ID without REQ- prefix for display."""
82
- return self.core.id.replace("REQ-", "")
83
-
84
- @property
85
- def full_id(self) -> str:
86
- """Full requirement ID including REQ- prefix."""
87
- return self.core.id
88
-
89
- @property
90
- def title(self) -> str:
91
- return self.core.title
92
-
93
- @property
94
- def level(self) -> str:
95
- """Level normalized to uppercase (PRD, OPS, DEV)."""
96
- return self.core.level.upper()
97
-
98
- @property
99
- def status(self) -> str:
100
- return self.core.status
101
-
102
- @property
103
- def implements(self) -> List[str]:
104
- return self.core.implements
105
-
106
- @property
107
- def file_path(self) -> Path:
108
- return self.core.file_path or Path("unknown")
109
-
110
- @property
111
- def line_number(self) -> int:
112
- return self.core.line_number or 0
113
-
114
- @property
115
- def hash(self) -> str:
116
- return self.core.hash or ""
117
-
118
- @property
119
- def body(self) -> str:
120
- return self.core.body
121
-
122
- @property
123
- def rationale(self) -> str:
124
- return self.core.rationale or ""
125
-
126
- @property
127
- def is_roadmap(self) -> bool:
128
- return self.core.is_roadmap
129
-
130
- @property
131
- def is_conflict(self) -> bool:
132
- return self.core.is_conflict
133
-
134
- @property
135
- def conflict_with(self) -> str:
136
- return self.core.conflict_with
137
-
138
- @property
139
- def subdir(self) -> str:
140
- return self.core.subdir
141
-
142
- # --- Computed properties ---
143
-
144
- @property
145
- def repo_prefix(self) -> Optional[str]:
146
- """Extract repo prefix from ID (e.g., 'CAL-d00005' → 'CAL').
147
-
148
- Associated repo requirements have format PREFIX-{level}{number}.
149
- Core repo requirements have format {level}{number} (returns None).
150
- """
151
- import re
152
-
153
- # Match: optional prefix (uppercase letters), then level (p/o/d) and 5 digits
154
- match = re.match(r"^([A-Z]+-)?([pod]\d{5})$", self.id, re.IGNORECASE)
155
- if match and match.group(1):
156
- return match.group(1).rstrip("-")
157
- return None
158
-
159
- @property
160
- def display_filename(self) -> str:
161
- """Get displayable filename with repo prefix for external repos.
162
-
163
- Returns 'CAL/filename.md' for external repos, 'filename.md' for core.
164
- """
165
- prefix = self.repo_prefix
166
- if prefix:
167
- return f"{prefix}/{self.file_path.name}"
168
- return self.file_path.name
169
-
170
- # --- Git state properties ---
171
-
172
- def _get_spec_relative_path(self) -> str:
173
- """Get the spec-relative path for this requirement's file."""
174
- if self.is_roadmap:
175
- return f"spec/roadmap/{self.file_path.name}"
176
- if self.subdir:
177
- return f"spec/{self.subdir}/{self.file_path.name}"
178
- return f"spec/{self.file_path.name}"
179
-
180
- def _is_in_untracked_file(self) -> bool:
181
- """Check if requirement is in an untracked (new) file."""
182
- if not self.git_info:
183
- return False
184
- rel_path = self._get_spec_relative_path()
185
- return rel_path in self.git_info.untracked_files
186
-
187
- def _check_modified_in_fileset(self, file_set: Set[str]) -> bool:
188
- """Check if requirement is modified based on a set of changed files."""
189
- if not self.git_info:
190
- return False
191
-
192
- rel_path = self._get_spec_relative_path()
193
-
194
- # Check if file is untracked (new) - all REQs in new files are considered modified
195
- if rel_path in self.git_info.untracked_files:
196
- return True
197
-
198
- # Check if file is in the modified set
199
- return rel_path in file_set
200
-
201
- @property
202
- def is_uncommitted(self) -> bool:
203
- """Check if requirement has uncommitted changes."""
204
- if not self.git_info:
205
- return False
206
- return self._check_modified_in_fileset(self.git_info.uncommitted_files)
207
-
208
- @property
209
- def is_branch_changed(self) -> bool:
210
- """Check if requirement changed vs main branch."""
211
- if not self.git_info:
212
- return False
213
- return self._check_modified_in_fileset(self.git_info.branch_changed_files)
214
-
215
- @property
216
- def is_new(self) -> bool:
217
- """Check if requirement is in a new (untracked) file."""
218
- return self._is_in_untracked_file()
219
-
220
- @property
221
- def is_modified(self) -> bool:
222
- """Check if requirement has modified content but is not in a new file."""
223
- if self._is_in_untracked_file():
224
- return False # New files are "new", not "modified"
225
- return self.is_uncommitted
226
-
227
- @property
228
- def is_moved(self) -> bool:
229
- """Check if requirement was moved from a different file.
230
-
231
- A requirement is considered moved if:
232
- - It exists in the committed state in a different file, OR
233
- - It's in a new file but has a non-TBD hash (suggesting it was copied/moved)
234
- """
235
- if not self.git_info:
236
- return False
237
-
238
- current_path = self._get_spec_relative_path()
239
- committed_path = self.git_info.committed_req_locations.get(self.id)
240
-
241
- if committed_path is not None:
242
- # REQ existed in committed state - check if path changed
243
- return committed_path != current_path
244
-
245
- # REQ doesn't exist in committed state
246
- # If it's in a new file but has a real hash, it was likely moved/copied
247
- if self._is_in_untracked_file() and self.hash and self.hash != "TBD":
248
- return True
249
-
250
- return False
251
-
252
- # --- Factory methods ---
253
-
254
- @classmethod
255
- def from_core(
256
- cls,
257
- core_req: CoreRequirement,
258
- git_info: Optional[GitChangeInfo] = None,
259
- ) -> "TraceViewRequirement":
260
- """Create TraceViewRequirement from core Requirement.
261
-
262
- Args:
263
- core_req: The core Requirement to wrap
264
- git_info: Optional git change state (inject for testability)
265
-
266
- Returns:
267
- TraceViewRequirement instance
268
- """
269
- # Detect external repo paths (absolute paths)
270
- external_spec_path = None
271
- if core_req.file_path and core_req.file_path.is_absolute():
272
- external_spec_path = str(core_req.file_path)
273
-
274
- return cls(
275
- core=core_req,
276
- git_info=git_info,
277
- external_spec_path=external_spec_path,
278
- )
279
-
280
- @classmethod
281
- def from_dict(
282
- cls,
283
- req_id: str,
284
- data: Dict,
285
- git_info: Optional[GitChangeInfo] = None,
286
- ) -> "TraceViewRequirement":
287
- """Create TraceViewRequirement from elspais validate --json output.
288
-
289
- This provides backward compatibility with code that expects to create
290
- requirements from JSON data.
291
-
292
- Args:
293
- req_id: Full requirement ID (e.g., 'REQ-d00027')
294
- data: Dict from elspais JSON output
295
- git_info: Optional git change state
296
-
297
- Returns:
298
- TraceViewRequirement instance
299
- """
300
- # Map level to uppercase for consistency
301
- level_map = {
302
- "PRD": "PRD",
303
- "Ops": "OPS",
304
- "Dev": "DEV",
305
- "prd": "PRD",
306
- "ops": "OPS",
307
- "dev": "DEV",
308
- }
309
- level = data.get("level", "")
310
- subdir = data.get("subdir", "")
311
-
312
- # Create core requirement
313
- file_path_str = data.get("filePath", data.get("file", ""))
314
- file_path = Path(file_path_str) if file_path_str else None
315
-
316
- core_req = CoreRequirement(
317
- id=req_id,
318
- title=data.get("title", ""),
319
- level=level_map.get(level, level.upper()),
320
- status=data.get("status", "Active"),
321
- body=data.get("body", ""),
322
- implements=data.get("implements", []),
323
- rationale=data.get("rationale"),
324
- hash=data.get("hash"),
325
- file_path=file_path,
326
- line_number=data.get("line", 0),
327
- subdir=subdir,
328
- is_conflict=data.get("isConflict", False),
329
- conflict_with=data.get("conflictWith", "") or "",
330
- )
331
-
332
- # Create trace-view requirement
333
- tv_req = cls.from_core(core_req, git_info=git_info)
334
-
335
- # Add test info if provided
336
- test_count = data.get("test_count", 0)
337
- if test_count > 0:
338
- test_passed = data.get("test_passed", 0)
339
- test_status = "passed" if test_passed == test_count else "failed"
340
- tv_req.test_info = TestInfo(
341
- test_count=test_count,
342
- manual_test_count=0,
343
- test_status=test_status,
344
- test_details=data.get("test_result_files", []),
345
- notes="",
346
- )
347
-
348
- return tv_req
349
-
350
-
351
- # Backward compatibility aliases
352
- Requirement = TraceViewRequirement
353
- TraceabilityRequirement = TraceViewRequirement
@@ -1,60 +0,0 @@
1
- # Implements: REQ-int-d00002-C (Review server requires flask)
2
- """
3
- elspais.trace_view.review - Collaborative review system.
4
-
5
- Requires: pip install elspais[trace-review]
6
- """
7
-
8
- def _check_flask():
9
- try:
10
- import flask # noqa: F401
11
- return True
12
- except ImportError:
13
- return False
14
-
15
-
16
- FLASK_AVAILABLE = _check_flask()
17
-
18
- # Models are always available (no flask dependency)
19
- from elspais.trace_view.review.models import (
20
- Comment,
21
- CommentPosition,
22
- Thread,
23
- ReviewFlag,
24
- StatusRequest,
25
- Approval,
26
- ReviewSession,
27
- ReviewConfig,
28
- ReviewPackage,
29
- PositionType,
30
- RequestState,
31
- ApprovalDecision,
32
- )
33
-
34
- __all__ = [
35
- "FLASK_AVAILABLE",
36
- # Models (always available)
37
- "Comment",
38
- "CommentPosition",
39
- "Thread",
40
- "ReviewFlag",
41
- "StatusRequest",
42
- "Approval",
43
- "ReviewSession",
44
- "ReviewConfig",
45
- "ReviewPackage",
46
- "PositionType",
47
- "RequestState",
48
- "ApprovalDecision",
49
- ]
50
-
51
- if FLASK_AVAILABLE:
52
- from elspais.trace_view.review.server import create_app
53
- __all__.append("create_app")
54
- else:
55
- def create_app(*args, **kwargs):
56
- """Placeholder when flask is not installed."""
57
- raise ImportError(
58
- "Review server requires Flask. "
59
- "Install with: pip install elspais[trace-review]"
60
- )