elspais 0.11.2__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 (147) hide show
  1. elspais/__init__.py +1 -10
  2. elspais/{sponsors/__init__.py → associates.py} +102 -56
  3. elspais/cli.py +366 -69
  4. elspais/commands/__init__.py +9 -3
  5. elspais/commands/analyze.py +118 -169
  6. elspais/commands/changed.py +12 -23
  7. elspais/commands/config_cmd.py +10 -13
  8. elspais/commands/edit.py +33 -13
  9. elspais/commands/example_cmd.py +319 -0
  10. elspais/commands/hash_cmd.py +161 -183
  11. elspais/commands/health.py +1177 -0
  12. elspais/commands/index.py +98 -115
  13. elspais/commands/init.py +99 -22
  14. elspais/commands/reformat_cmd.py +41 -433
  15. elspais/commands/rules_cmd.py +2 -2
  16. elspais/commands/trace.py +443 -324
  17. elspais/commands/validate.py +193 -411
  18. elspais/config/__init__.py +799 -5
  19. elspais/{core/content_rules.py → content_rules.py} +20 -2
  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 +45 -29
  58. elspais/mcp/__main__.py +5 -1
  59. elspais/mcp/file_mutations.py +138 -0
  60. elspais/mcp/server.py +1998 -244
  61. elspais/testing/__init__.py +3 -3
  62. elspais/testing/config.py +3 -0
  63. elspais/testing/mapper.py +1 -1
  64. elspais/testing/scanner.py +301 -12
  65. elspais/utilities/__init__.py +1 -0
  66. elspais/utilities/docs_loader.py +115 -0
  67. elspais/utilities/git.py +607 -0
  68. elspais/{core → utilities}/hasher.py +8 -22
  69. elspais/utilities/md_renderer.py +189 -0
  70. elspais/{core → utilities}/patterns.py +56 -51
  71. elspais/utilities/reference_config.py +626 -0
  72. elspais/validation/__init__.py +19 -0
  73. elspais/validation/format.py +264 -0
  74. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
  75. elspais-0.43.5.dist-info/RECORD +80 -0
  76. elspais/config/defaults.py +0 -179
  77. elspais/config/loader.py +0 -494
  78. elspais/core/__init__.py +0 -21
  79. elspais/core/git.py +0 -346
  80. elspais/core/models.py +0 -320
  81. elspais/core/parser.py +0 -639
  82. elspais/core/rules.py +0 -509
  83. elspais/mcp/context.py +0 -172
  84. elspais/mcp/serializers.py +0 -112
  85. elspais/reformat/__init__.py +0 -50
  86. elspais/reformat/detector.py +0 -112
  87. elspais/reformat/hierarchy.py +0 -247
  88. elspais/reformat/line_breaks.py +0 -218
  89. elspais/reformat/prompts.py +0 -133
  90. elspais/reformat/transformer.py +0 -266
  91. elspais/trace_view/__init__.py +0 -55
  92. elspais/trace_view/coverage.py +0 -183
  93. elspais/trace_view/generators/__init__.py +0 -12
  94. elspais/trace_view/generators/base.py +0 -334
  95. elspais/trace_view/generators/csv.py +0 -118
  96. elspais/trace_view/generators/markdown.py +0 -170
  97. elspais/trace_view/html/__init__.py +0 -33
  98. elspais/trace_view/html/generator.py +0 -1140
  99. elspais/trace_view/html/templates/base.html +0 -283
  100. elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
  101. elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
  102. elspais/trace_view/html/templates/components/legend_modal.html +0 -69
  103. elspais/trace_view/html/templates/components/review_panel.html +0 -118
  104. elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
  105. elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
  106. elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
  107. elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
  108. elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
  109. elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
  110. elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
  111. elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
  112. elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
  113. elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
  114. elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
  115. elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
  116. elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
  117. elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
  118. elspais/trace_view/html/templates/partials/scripts.js +0 -1741
  119. elspais/trace_view/html/templates/partials/styles.css +0 -1756
  120. elspais/trace_view/models.py +0 -378
  121. elspais/trace_view/review/__init__.py +0 -63
  122. elspais/trace_view/review/branches.py +0 -1142
  123. elspais/trace_view/review/models.py +0 -1200
  124. elspais/trace_view/review/position.py +0 -591
  125. elspais/trace_view/review/server.py +0 -1032
  126. elspais/trace_view/review/status.py +0 -455
  127. elspais/trace_view/review/storage.py +0 -1343
  128. elspais/trace_view/scanning.py +0 -213
  129. elspais/trace_view/specs/README.md +0 -84
  130. elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
  131. elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
  132. elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
  133. elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
  134. elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
  135. elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
  136. elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
  137. elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
  138. elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
  139. elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
  140. elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
  141. elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
  142. elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
  143. elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
  144. elspais-0.11.2.dist-info/RECORD +0 -101
  145. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
  146. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
  147. {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/licenses/LICENSE +0 -0
elspais/mcp/__init__.py CHANGED
@@ -1,44 +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
22
23
 
23
- __all__ = [
24
- "WorkspaceContext",
25
- "serialize_assertion",
26
- "serialize_content_rule",
27
- "serialize_requirement",
28
- "serialize_requirement_summary",
29
- "serialize_violation",
30
- ]
24
+ MCP_AVAILABLE = True
25
+ except ImportError:
26
+ MCP_AVAILABLE = False
27
+ FastMCP = None
31
28
 
32
29
 
33
- def create_server(working_dir=None):
34
- """Create MCP server instance."""
30
+ def create_server(*args, **kwargs):
31
+ """Create the MCP server.
32
+
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
39
 
37
- return _create(working_dir)
40
+ return _create(*args, **kwargs)
41
+
38
42
 
43
+ def run_server(*args, **kwargs):
44
+ """Run the MCP server.
39
45
 
40
- def run_server(working_dir=None, transport="stdio"):
41
- """Run MCP server."""
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]")
42
51
  from elspais.mcp.server import run_server as _run
43
52
 
44
- return _run(working_dir, transport)
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