suitable-loop 0.1.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.
@@ -0,0 +1,53 @@
1
+ """MCP tool handlers for log analysis."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from mcp.server.fastmcp import FastMCP
8
+
9
+ from ..analyzers.log_analyzer import LogAnalyzer
10
+ from ..config import SuitableLoopConfig
11
+ from ..db import Database
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def register_log_tools(mcp: FastMCP, db: Database, config: SuitableLoopConfig):
17
+ analyzer = LogAnalyzer(db, config)
18
+
19
+ @mcp.tool()
20
+ def ingest_logs(path: str) -> dict:
21
+ """Parse and store a log file or directory of log files. Auto-detects format
22
+ (Python stdlib logging or JSON-per-line). Extracts errors, groups them by
23
+ signature, and maps stack frames to indexed code."""
24
+ return analyzer.ingest_logs(path)
25
+
26
+ @mcp.tool()
27
+ def get_error_groups(limit: int = 20) -> dict:
28
+ """List all distinct error groups sorted by frequency. Each group includes
29
+ exception type, occurrence count, and links to affected code."""
30
+ groups = analyzer.get_error_groups(limit=limit)
31
+ return {"error_group_count": len(groups), "error_groups": groups}
32
+
33
+ @mcp.tool()
34
+ def error_detail(error_group_id: int) -> dict:
35
+ """Get full detail on an error group: traceback, affected functions,
36
+ sample log entries, and timeline."""
37
+ detail = analyzer.error_detail(error_group_id)
38
+ if not detail:
39
+ return {"error": f"Error group {error_group_id} not found."}
40
+ return detail
41
+
42
+ @mcp.tool()
43
+ def correlate_error(error_text: str) -> dict:
44
+ """Given a raw error or traceback text, find the relevant code paths in the
45
+ indexed codebase. Useful for investigating production errors."""
46
+ return analyzer.correlate_error(error_text)
47
+
48
+ @mcp.tool()
49
+ def error_timeline(days: int = 7) -> dict:
50
+ """Show error frequency over time, grouped by error type. Useful for spotting
51
+ trends and regressions."""
52
+ timeline = analyzer.error_timeline(days=days)
53
+ return {"days": days, "timeline": timeline}
@@ -0,0 +1,49 @@
1
+ """MCP tool handlers for utility operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import time
7
+
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ from ..analyzers.code_analyzer import CodeAnalyzer
11
+ from ..config import SuitableLoopConfig
12
+ from ..db import Database
13
+ from ..graph.engine import GraphEngine
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ def register_util_tools(mcp: FastMCP, db: Database, config: SuitableLoopConfig):
19
+ analyzer = CodeAnalyzer(db, config)
20
+ graph = GraphEngine(db)
21
+
22
+ @mcp.tool()
23
+ def status() -> dict:
24
+ """Get Suitable Loop server status: indexed projects, database size, entity counts,
25
+ and last index time."""
26
+ stats = db.get_stats()
27
+ return {
28
+ "status": "running",
29
+ "db_path": str(config.db_path),
30
+ "db_size_bytes": stats.get("db_size_bytes", 0),
31
+ "entities": {
32
+ "files": stats.get("files", 0),
33
+ "functions": stats.get("functions", 0),
34
+ "classes": stats.get("classes", 0),
35
+ "call_edges": stats.get("call_edges", 0),
36
+ "commits": stats.get("commits", 0),
37
+ "error_groups": stats.get("error_groups", 0),
38
+ },
39
+ }
40
+
41
+ @mcp.tool()
42
+ def reindex(path: str) -> dict:
43
+ """Re-index only changed files in a project (uses content hashing to detect changes).
44
+ Faster than a full index_codebase with force=True."""
45
+ start = time.time()
46
+ result = analyzer.index_codebase(path, force=False)
47
+ graph.build_graph(path)
48
+ result["duration_seconds"] = round(time.time() - start, 2)
49
+ return result
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: suitable-loop
3
+ Version: 0.1.0
4
+ Summary: Local production engineering platform — semantic code analysis, git risk scoring, and log correlation via MCP
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: gitpython>=3.1
7
+ Requires-Dist: mcp>=1.0.0
8
+ Requires-Dist: networkx>=3.0
9
+ Requires-Dist: radon>=6.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
12
+ Requires-Dist: pytest>=8.0; extra == 'dev'
@@ -0,0 +1,21 @@
1
+ suitable_loop/__init__.py,sha256=f3tlDiLb6PLDEsHsQPkXb_NVu4ELDpbdAB6RXYRvwVQ,86
2
+ suitable_loop/__main__.py,sha256=SuLtzOjn4Y_YKA1BW5ZSQkqVPxTiYVt2yqUb7PFT_Yg,83
3
+ suitable_loop/config.py,sha256=ho8H-i6T6s-5PjSqT20rayNwZt2Ke7bEwFG5jT83wP4,1639
4
+ suitable_loop/db.py,sha256=k-l1Or8hA-yfgVj9BOFUBBaSVN4-eCxIBf16z71eN8c,18593
5
+ suitable_loop/models.py,sha256=bmydJcoVkrfgS64jFXzJqQYzKACUa5GG-0Jkj4YvpMg,2749
6
+ suitable_loop/server.py,sha256=38-x-2TCAfIb3s6v09MH6ke7yLI5YabBP4BPhlJtF2o,1156
7
+ suitable_loop/analyzers/__init__.py,sha256=-XHcZF2qn3Jh-NPJIX2z-m7oa5h_1-JiSHu6bHKtGsM,38
8
+ suitable_loop/analyzers/code_analyzer.py,sha256=M56qDHg4zF50_2j05t3ZCLk-GPqdpXQ8l-KXUf758Ag,23927
9
+ suitable_loop/analyzers/git_analyzer.py,sha256=oBqVMkoq5yZdY1462o_dFJT0KFTcXqQRMvpts4JqpCc,19716
10
+ suitable_loop/analyzers/log_analyzer.py,sha256=hiZxFhEgd_9q8Y-I0iM5cFkgJOY8Rh1g2Mp-l7TCGYU,24705
11
+ suitable_loop/graph/__init__.py,sha256=mo1pUt0o0cKWQAZL4kqD9mXEthcv26GOK9PcXOYydns,34
12
+ suitable_loop/graph/engine.py,sha256=Ith6YsoQdxB4V2vEsSBf8aPTVYUAlGqv6EqpTZkHjB4,12635
13
+ suitable_loop/tools/__init__.py,sha256=p8zryNWB5He5MGgQ_-3tZtrXR0eEXDsIcbgGUesQvF0,39
14
+ suitable_loop/tools/code_tools.py,sha256=E6w3c7llLZv-K-nlj5MbFMyoR_ENSliqSxSjHzhOrHc,3990
15
+ suitable_loop/tools/git_tools.py,sha256=iIRoegCwm69i5YdKt3rDBJbnWi8X4zGecx-40kW_MDk,1943
16
+ suitable_loop/tools/log_tools.py,sha256=QH_o7rLoLEbjqvSuIx08kb37Ij2EYpUozN2BLz7vT1I,2020
17
+ suitable_loop/tools/util_tools.py,sha256=N2KKYVctOv3xzlTNWZcpV_AOcl6mDI6gzZyoy4kPuYg,1647
18
+ suitable_loop-0.1.0.dist-info/METADATA,sha256=kEUcwYDx2XnvL353VTDum9XAXr3eT1X9lW6luut3IQI,428
19
+ suitable_loop-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
20
+ suitable_loop-0.1.0.dist-info/entry_points.txt,sha256=xSUkV2TXls7Gp9bcPcUOLUvWDcYuhSGPTV55b9hoA9U,60
21
+ suitable_loop-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ suitable-loop = suitable_loop.server:main