srcodex 0.2.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.
- srcodex/__init__.py +0 -0
- srcodex/backend/__init__.py +0 -0
- srcodex/backend/chat.py +79 -0
- srcodex/backend/main.py +98 -0
- srcodex/backend/services/__init__.py +0 -0
- srcodex/backend/services/claude_service.py +754 -0
- srcodex/backend/services/config_loader.py +113 -0
- srcodex/backend/services/file_access_tools.py +279 -0
- srcodex/backend/services/file_tree.py +480 -0
- srcodex/backend/services/graph_tools.py +874 -0
- srcodex/backend/services/logger_setup.py +91 -0
- srcodex/backend/services/session_manager.py +81 -0
- srcodex/backend/services/status_tracker.py +91 -0
- srcodex/cli.py +255 -0
- srcodex/core/__init__.py +0 -0
- srcodex/core/config.py +113 -0
- srcodex/core/logger.py +23 -0
- srcodex/indexer/__init__.py +0 -0
- srcodex/indexer/cscope_client.py +183 -0
- srcodex/indexer/ctags_compat.py +223 -0
- srcodex/indexer/ctags_parser.py +456 -0
- srcodex/indexer/explorer.py +135 -0
- srcodex/indexer/field_access_analyzer.py +436 -0
- srcodex/indexer/indexer.py +664 -0
- srcodex/indexer/reference_ingestor.py +293 -0
- srcodex/indexer/reference_resolver.py +544 -0
- srcodex/tui/__init__.py +0 -0
- srcodex/tui/app.py +103 -0
- srcodex/tui/app.tcss +24 -0
- srcodex/tui/components/__init__.py +0 -0
- srcodex/tui/components/bars/__init__.py +0 -0
- srcodex/tui/components/bars/chat_header.py +48 -0
- srcodex/tui/components/bars/code_tab_bar.py +157 -0
- srcodex/tui/components/bars/footer_bar.py +128 -0
- srcodex/tui/components/bars/left_tab.py +54 -0
- srcodex/tui/components/logger.py +57 -0
- srcodex/tui/components/panels/__init__.py +0 -0
- srcodex/tui/components/panels/chat_panel.py +523 -0
- srcodex/tui/components/panels/code_panel.py +229 -0
- srcodex/tui/components/panels/side_panel.py +128 -0
- srcodex/tui/components/views/__init__.py +0 -0
- srcodex/tui/components/views/explorer_view.py +20 -0
- srcodex/tui/components/views/search_view.py +148 -0
- srcodex/tui/components/widgets/__init__.py +0 -0
- srcodex/tui/components/widgets/file_browser.py +16 -0
- srcodex/tui/components/widgets/find_box.py +85 -0
- srcodex-0.2.0.dist-info/METADATA +170 -0
- srcodex-0.2.0.dist-info/RECORD +52 -0
- srcodex-0.2.0.dist-info/WHEEL +5 -0
- srcodex-0.2.0.dist-info/entry_points.txt +2 -0
- srcodex-0.2.0.dist-info/licenses/LICENSE +21 -0
- srcodex-0.2.0.dist-info/top_level.txt +1 -0
srcodex/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
srcodex/backend/chat.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from fastapi import FastAPI, HTTPException
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from .services.claude_service import ClaudeService
|
|
4
|
+
from .services.logger_setup import setup_backend_logging
|
|
5
|
+
from fastapi.responses import StreamingResponse
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
setup_backend_logging()
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
app = FastAPI()
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
claude_service = ClaudeService()
|
|
17
|
+
except ValueError as e:
|
|
18
|
+
print(f"Warning: {e}")
|
|
19
|
+
claude_service = None
|
|
20
|
+
|
|
21
|
+
class ChatRequest(BaseModel):
|
|
22
|
+
"""Request body for chat endpoint"""
|
|
23
|
+
message: str
|
|
24
|
+
conversation_history: list = []
|
|
25
|
+
|
|
26
|
+
class ChatResponse(BaseModel):
|
|
27
|
+
"""Response body for chat endpoint"""
|
|
28
|
+
response: str
|
|
29
|
+
|
|
30
|
+
@app.post("/api/chat", response_model=ChatResponse)
|
|
31
|
+
async def chat(request: ChatRequest):
|
|
32
|
+
"""
|
|
33
|
+
Chat endpoint - sends message to Claude and returns response
|
|
34
|
+
Example:
|
|
35
|
+
POST /api/chat
|
|
36
|
+
{"message": "What does functionX do?"}
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
{"response": "FunctionX is a function that..."}
|
|
40
|
+
"""
|
|
41
|
+
if not claude_service:
|
|
42
|
+
raise HTTPException(status_code=500, detail="Claude service not initialized - check API key")
|
|
43
|
+
|
|
44
|
+
if not request.message.strip():
|
|
45
|
+
raise HTTPException(status_code=400, detail="Message cannot be empty")
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
logger.info(f"Chat request: {request.message[:100]}...")
|
|
49
|
+
response = claude_service.send_message_with_tools(request.message, request.conversation_history)
|
|
50
|
+
logger.info(f"Chat response: {len(response)} chars")
|
|
51
|
+
return ChatResponse(response=response)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.error(f"Claude API error: {str(e)}")
|
|
54
|
+
raise HTTPException(status_code=500, detail=f"Claude API error: {str(e)}")
|
|
55
|
+
|
|
56
|
+
@app.post("/api/chat/stream")
|
|
57
|
+
async def chat_stream(request: ChatRequest):
|
|
58
|
+
"""
|
|
59
|
+
Streaming chat endpoint - streams Claude's response in real-time
|
|
60
|
+
|
|
61
|
+
Streams newline-delimited JSON objects:
|
|
62
|
+
- Text chunks: {"type": "text", "content": "..."}
|
|
63
|
+
- Token metadata: {"type": "tokens", "input": 1234, "output": 56, "total": 1290}
|
|
64
|
+
"""
|
|
65
|
+
if not claude_service:
|
|
66
|
+
raise HTTPException(status_code=500, detail="Claude service not initialized")
|
|
67
|
+
|
|
68
|
+
if not request.message.strip():
|
|
69
|
+
raise HTTPException(status_code=400, detail="Message cannot be empty")
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
def generate():
|
|
73
|
+
for chunk in claude_service.stream_message_with_tools(request.message, request.conversation_history):
|
|
74
|
+
# Stream as newline-delimited JSON
|
|
75
|
+
yield json.dumps(chunk) + "\n"
|
|
76
|
+
|
|
77
|
+
return StreamingResponse(generate(), media_type="application/x-ndjson")
|
|
78
|
+
except Exception as e:
|
|
79
|
+
raise HTTPException(status_code=500, detail=f"Claude API error: {str(e)}")
|
srcodex/backend/main.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
srcodex Backend - Semantic Graph API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from fastapi import FastAPI
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, List, Any
|
|
8
|
+
import sys
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
10
|
+
from services.file_tree import FileTreeService
|
|
11
|
+
DB_PATH = Path(__file__).parent.parent / "data" / "pmfw_main.db" ##Database path
|
|
12
|
+
|
|
13
|
+
app = FastAPI(title="srcodex API", version="0.1.0")
|
|
14
|
+
file_tree_service = FileTreeService(str(DB_PATH))
|
|
15
|
+
|
|
16
|
+
@app.get("/")
|
|
17
|
+
async def root() -> Dict[str, str]:
|
|
18
|
+
""" API Root, returns basic information """
|
|
19
|
+
return {
|
|
20
|
+
"name": "srcodex API",
|
|
21
|
+
"version": "0.1.0"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@app.get("/projects/{project_id}/root")
|
|
25
|
+
async def get_project_root(project_id: str) -> Dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Get Project root metadata
|
|
28
|
+
Example: GET /projects/pmfw_main/root
|
|
29
|
+
"""
|
|
30
|
+
if project_id != Path(DB_PATH).stem:
|
|
31
|
+
return {"error": f"Project '{project_id}' not found!"}, 404
|
|
32
|
+
|
|
33
|
+
return file_tree_service.get_root()
|
|
34
|
+
|
|
35
|
+
@app.get("/projects/{project_id}/children")
|
|
36
|
+
def get_children(project_id: str, path: str = "") -> List[Dict[str, Any]]:
|
|
37
|
+
"""
|
|
38
|
+
Get Immediate children of a directory.
|
|
39
|
+
Examples:
|
|
40
|
+
GET /projects/pmfw_main/children?path=
|
|
41
|
+
→ Returns root children: [mp1/, mpccx/, common/, test/]
|
|
42
|
+
GET /projects/pmfw_main/children?path=mp1/src/app/
|
|
43
|
+
→ Returns contents of mp1/src/app/
|
|
44
|
+
"""
|
|
45
|
+
if project_id != Path(DB_PATH).stem:
|
|
46
|
+
return {"error": f"Project '{project_id}' not found!"}, 404
|
|
47
|
+
|
|
48
|
+
return file_tree_service.get_children(path)
|
|
49
|
+
|
|
50
|
+
@app.get("/projects/{project_id}/search/files")
|
|
51
|
+
def search_file(project_id: str, q: str = "") -> List[Dict[str, Any]]:
|
|
52
|
+
"""
|
|
53
|
+
Search for files by name or path (like Ctrl+P in VSCode).
|
|
54
|
+
Examples:
|
|
55
|
+
GET /projects/pmfw_main/search/files?q=msg
|
|
56
|
+
→ Returns files matching "msg": [msg.c, msg.h, ...]
|
|
57
|
+
GET /projects/pmfw_main/search/files?q=app/pow
|
|
58
|
+
→ Returns files in "app" matching "pow": [mp1/src/app/power.c, ...]
|
|
59
|
+
"""
|
|
60
|
+
if project_id != Path(DB_PATH).stem:
|
|
61
|
+
return {"error": f"Project '{project_id}' not found!"}, 404
|
|
62
|
+
|
|
63
|
+
return file_tree_service.search_file(q)
|
|
64
|
+
|
|
65
|
+
@app.get("/projects/{project_id}/search/symbols")
|
|
66
|
+
def search_symbol_global(project_id: str, q: str = "") -> List[Dict[str, Any]]:
|
|
67
|
+
"""
|
|
68
|
+
Search for symbols globally across entire codebase (like Ctrl+Shift+F in VSCode).
|
|
69
|
+
Examples:
|
|
70
|
+
GET /projects/pmfw_main/search/symbols?q=voltage
|
|
71
|
+
→ Returns all voltage-related symbols (variables, macros, structs, ...)
|
|
72
|
+
GET /projects/pmfw_main/search/symbols?q=Init
|
|
73
|
+
→ Returns all initialization functions
|
|
74
|
+
"""
|
|
75
|
+
if project_id != Path(DB_PATH).stem:
|
|
76
|
+
return {"error": f"Project '{project_id}' not found!"}, 404
|
|
77
|
+
|
|
78
|
+
return file_tree_service.search_symbol_global(q)
|
|
79
|
+
|
|
80
|
+
@app.get("/projects/{project_id}/files/{file_path:path}/symbols")
|
|
81
|
+
def search_symbol_infile(project_id: str, file_path: str, q: str = "") -> List[Dict[str, Any]]:
|
|
82
|
+
"""
|
|
83
|
+
Search symbols within a specific file (like Ctrl+F in VSCode on open file).
|
|
84
|
+
Examples:
|
|
85
|
+
GET /projects/pmfw_main/files/mp1/src/app/msg.c/symbols?q=Isr
|
|
86
|
+
→ Returns all symbols matching "Isr" in msg.c: [IsrHostMsg, IsrBiosMsg, ...]
|
|
87
|
+
GET /projects/pmfw_main/files/mp1/src/app/power.c/symbols?q=Init
|
|
88
|
+
→ Returns all Init functions in power.c
|
|
89
|
+
"""
|
|
90
|
+
if project_id != Path(DB_PATH).stem:
|
|
91
|
+
return {"error": f"Project '{project_id}' not found!"}, 404
|
|
92
|
+
|
|
93
|
+
return file_tree_service.search_symbol_infile(q, file_path)
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
import uvicorn
|
|
97
|
+
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
|
98
|
+
|
|
File without changes
|