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
elspais/mcp/__init__.py CHANGED
@@ -1,42 +1,60 @@
1
- """
2
- elspais.mcp - MCP (Model Context Protocol) server for elspais.
1
+ """elspais.mcp - Model Context Protocol server for elspais.
3
2
 
4
3
  This module provides an MCP server that exposes elspais functionality
5
- to AI agents and LLMs. Requires the optional 'mcp' dependency:
6
-
7
- pip install elspais[mcp]
4
+ to AI agents. The server is a pure interface layer that consumes
5
+ TraceGraph directly (REQ-p00060-B).
8
6
 
9
7
  Usage:
10
- elspais mcp serve # Start with stdio transport
11
- python -m elspais.mcp # Alternative entry point
8
+ # Check if MCP is available
9
+ from elspais.mcp import MCP_AVAILABLE
10
+
11
+ if MCP_AVAILABLE:
12
+ from elspais.mcp import create_server, run_server
13
+
14
+ # Create server
15
+ server = create_server()
16
+
17
+ # Or run directly
18
+ run_server()
12
19
  """
13
20
 
14
- from elspais.mcp.context import WorkspaceContext
15
- from elspais.mcp.serializers import (
16
- serialize_assertion,
17
- serialize_content_rule,
18
- serialize_requirement,
19
- serialize_requirement_summary,
20
- serialize_violation,
21
- )
21
+ try:
22
+ from mcp.server.fastmcp import FastMCP
23
+
24
+ MCP_AVAILABLE = True
25
+ except ImportError:
26
+ MCP_AVAILABLE = False
27
+ FastMCP = None
22
28
 
23
- __all__ = [
24
- "WorkspaceContext",
25
- "serialize_assertion",
26
- "serialize_content_rule",
27
- "serialize_requirement",
28
- "serialize_requirement_summary",
29
- "serialize_violation",
30
- ]
31
29
 
30
+ def create_server(*args, **kwargs):
31
+ """Create the MCP server.
32
32
 
33
- def create_server(working_dir=None):
34
- """Create MCP server instance."""
33
+ Raises:
34
+ ImportError: If MCP dependencies are not installed.
35
+ """
36
+ if not MCP_AVAILABLE:
37
+ raise ImportError("MCP dependencies not installed. Install with: pip install elspais[mcp]")
35
38
  from elspais.mcp.server import create_server as _create
36
- return _create(working_dir)
37
39
 
40
+ return _create(*args, **kwargs)
38
41
 
39
- def run_server(working_dir=None, transport="stdio"):
40
- """Run MCP server."""
42
+
43
+ def run_server(*args, **kwargs):
44
+ """Run the MCP server.
45
+
46
+ Raises:
47
+ ImportError: If MCP dependencies are not installed.
48
+ """
49
+ if not MCP_AVAILABLE:
50
+ raise ImportError("MCP dependencies not installed. Install with: pip install elspais[mcp]")
41
51
  from elspais.mcp.server import run_server as _run
42
- return _run(working_dir, transport)
52
+
53
+ return _run(*args, **kwargs)
54
+
55
+
56
+ __all__ = [
57
+ "MCP_AVAILABLE",
58
+ "create_server",
59
+ "run_server",
60
+ ]
elspais/mcp/__main__.py CHANGED
@@ -1,4 +1,8 @@
1
- """Allow running as: python -m elspais.mcp"""
1
+ """Entry point for running elspais MCP server directly.
2
+
3
+ Usage:
4
+ python -m elspais.mcp
5
+ """
2
6
 
3
7
  from elspais.mcp.server import run_server
4
8
 
@@ -0,0 +1,138 @@
1
+ """File mutation helpers for CLI commands.
2
+
3
+ Provides functions to safely modify spec files on disk:
4
+ - update_hash_in_file: Update the hash in a requirement's End marker
5
+ - add_status_to_file: Add a missing Status field to metadata
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ from pathlib import Path
12
+
13
+
14
+ def update_hash_in_file(
15
+ file_path: Path,
16
+ req_id: str,
17
+ new_hash: str,
18
+ ) -> bool:
19
+ """Update the hash in a spec file for a requirement.
20
+
21
+ Finds the end marker line: *End* *Title* | **Hash**: old_hash
22
+ And updates to: *End* *Title* | **Hash**: new_hash
23
+
24
+ Args:
25
+ file_path: Path to the spec file.
26
+ req_id: The requirement ID (e.g., 'REQ-p00001').
27
+ new_hash: The new hash value to set.
28
+
29
+ Returns:
30
+ True if the hash was updated, False if the requirement was not found.
31
+ """
32
+ file_path = Path(file_path)
33
+ content = file_path.read_text(encoding="utf-8")
34
+
35
+ # Find the requirement block to locate its End marker
36
+ # First find the header line: ## REQ-xxx: Title
37
+ header_pattern = re.compile(
38
+ rf"^(##+ {re.escape(req_id)}:\s*(.+?)\s*)$",
39
+ re.MULTILINE,
40
+ )
41
+ header_match = header_pattern.search(content)
42
+ if not header_match:
43
+ return False
44
+
45
+ # Now find the End marker after this header
46
+ # Pattern: *End* *Title* | **Hash**: xxxxxxxx
47
+ # The title in the End marker should match the requirement's title
48
+ start_pos = header_match.end()
49
+
50
+ # Look for the End marker pattern anywhere after the header
51
+ # It can have various title formats, so we just look for *End* ... **Hash**:
52
+ end_pattern = re.compile(
53
+ r"(\*End\*\s+\*.+?\*\s*\|\s*\*\*Hash\*\*:\s*)([a-fA-F0-9]+)",
54
+ )
55
+
56
+ # Search from the header position
57
+ end_match = end_pattern.search(content, pos=start_pos)
58
+ if not end_match:
59
+ return False
60
+
61
+ # Check if this End marker is still part of our requirement block
62
+ # (i.e., no new requirement header between our header and this End marker)
63
+ next_header_pattern = re.compile(r"^##+ REQ-", re.MULTILINE)
64
+ next_header_match = next_header_pattern.search(content, pos=start_pos)
65
+
66
+ if next_header_match and next_header_match.start() < end_match.start():
67
+ # This End marker belongs to a different requirement
68
+ return False
69
+
70
+ # Replace just the hash value
71
+ new_content = content[: end_match.start(2)] + new_hash + content[end_match.end(2) :]
72
+
73
+ file_path.write_text(new_content, encoding="utf-8")
74
+ return True
75
+
76
+
77
+ def add_status_to_file(
78
+ file_path: Path,
79
+ req_id: str,
80
+ status: str,
81
+ ) -> bool:
82
+ """Add a Status field to a requirement's metadata line.
83
+
84
+ Finds the metadata line: **Level**: XXX | **Implements**: YYY
85
+ And adds Status: **Level**: XXX | **Status**: ZZZ | **Implements**: YYY
86
+
87
+ Args:
88
+ file_path: Path to the spec file.
89
+ req_id: The requirement ID (e.g., 'REQ-p00001').
90
+ status: The status value to add (e.g., 'Active').
91
+
92
+ Returns:
93
+ True if status was added, False if req not found or already has status.
94
+ """
95
+ file_path = Path(file_path)
96
+ content = file_path.read_text(encoding="utf-8")
97
+
98
+ # Find the requirement header
99
+ header_pattern = re.compile(
100
+ rf"^(##+ {re.escape(req_id)}:\s*.+?)$",
101
+ re.MULTILINE,
102
+ )
103
+ header_match = header_pattern.search(content)
104
+ if not header_match:
105
+ return False
106
+
107
+ start_pos = header_match.end()
108
+
109
+ # Find the next requirement header or end of file to bound our search
110
+ next_header_pattern = re.compile(r"^##+ REQ-", re.MULTILINE)
111
+ next_header_match = next_header_pattern.search(content, pos=start_pos)
112
+ end_pos = next_header_match.start() if next_header_match else len(content)
113
+
114
+ # Search for metadata line within this requirement block
115
+ block = content[start_pos:end_pos]
116
+
117
+ # Check if status already exists
118
+ if re.search(r"\*\*Status\*\*:", block):
119
+ return False
120
+
121
+ # Find the Level line: **Level**: XXX | ...
122
+ level_pattern = re.compile(
123
+ r"(\*\*Level\*\*:\s*\w+)(\s*\|)",
124
+ )
125
+ level_match = level_pattern.search(block)
126
+ if not level_match:
127
+ return False
128
+
129
+ # Insert status after Level
130
+ new_block = (
131
+ block[: level_match.end(1)] + f" | **Status**: {status}" + block[level_match.start(2) :]
132
+ )
133
+
134
+ # Rebuild the full content
135
+ new_content = content[:start_pos] + new_block + content[end_pos:]
136
+
137
+ file_path.write_text(new_content, encoding="utf-8")
138
+ return True