Loading...
Loading...
Deep code analysis for pplx-sdk — parse Python AST, build dependency graphs, extract knowledge graphs, detect patterns, and generate actionable insights about code structure, complexity, and relationships. Use when analyzing code quality, mapping dependencies, or building understanding of the codebase.
npx skill4agent add pv-udpv/pplx-sdk code-analysisastgrep@babel/parserts-morphimport ast
from pathlib import Path
def parse_module(filepath: str) -> dict:
"""Extract entities from a Python module via AST."""
source = Path(filepath).read_text()
tree = ast.parse(source, filename=filepath)
entities = {
"module": filepath,
"classes": [],
"functions": [],
"imports": [],
"constants": [],
}
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef):
entities["classes"].append({
"name": node.name,
"bases": [ast.dump(b) for b in node.bases],
"methods": [n.name for n in node.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))],
"decorators": [ast.dump(d) for d in node.decorator_list],
"lineno": node.lineno,
})
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
if not any(isinstance(parent, ast.ClassDef) for parent in ast.walk(tree)):
entities["functions"].append({
"name": node.name,
"args": [arg.arg for arg in node.args.args],
"returns": ast.dump(node.returns) if node.returns else None,
"is_async": isinstance(node, ast.AsyncFunctionDef),
"lineno": node.lineno,
})
elif isinstance(node, ast.Import):
for alias in node.names:
entities["imports"].append({"module": alias.name, "alias": alias.asname})
elif isinstance(node, ast.ImportFrom):
entities["imports"].append({
"module": node.module,
"names": [alias.name for alias in node.names],
"level": node.level,
})
return entities# Quick import graph using grep
grep -rn "from pplx_sdk" pplx_sdk/ --include="*.py" | \
awk -F: '{print $1 " -> " $2}' | \
sed 's|pplx_sdk/||g' | sort -u
# Or using Python AST for precision
python3 -c "
import ast, os, json
graph = {}
for root, dirs, files in os.walk('pplx_sdk'):
for f in files:
if f.endswith('.py'):
path = os.path.join(root, f)
module = path.replace('/', '.').replace('.py', '')
tree = ast.parse(open(path).read())
deps = set()
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
if node.module.startswith('pplx_sdk'):
deps.add(node.module)
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name.startswith('pplx_sdk'):
deps.add(alias.name)
if deps:
graph[module] = sorted(deps)
print(json.dumps(graph, indent=2))
"graph TD
subgraph Valid["✅ Valid Dependencies"]
client --> domain
client --> transport
client --> shared
domain --> transport
domain --> shared
domain --> core
transport --> shared
transport --> core
shared --> core
end
subgraph Invalid["❌ Layer Violations"]
core -.->|VIOLATION| shared
core -.->|VIOLATION| transport
shared -.->|VIOLATION| transport
transport -.->|VIOLATION| domain
end
style Valid fill:#e8f5e9
style Invalid fill:#ffebee| Entity | Source | Example |
|---|---|---|
| File path | |
| AST ClassDef | |
| AST FunctionDef | |
| typing.Protocol | |
| Exception subclass | |
| TypeAlias | |
| Module-level assign | |
| Relationship | Meaning | Example |
|---|---|---|
| Module imports another | |
| Module defines entity | |
| Class extends another | |
| Class implements protocol | |
| Function calls another | |
| Function returns type | |
| Function raises exception | |
| Function uses type hint | |
| Entity belongs to layer | |
graph LR
subgraph core["core/"]
Transport[/"Transport<br/>(Protocol)"/]
PerplexitySDKError["PerplexitySDKError"]
TransportError["TransportError"]
end
subgraph transport["transport/"]
SSETransport["SSETransport"]
HttpTransport["HttpTransport"]
end
subgraph shared["shared/"]
retry["retry_with_backoff()"]
end
SSETransport -->|IMPLEMENTS| Transport
HttpTransport -->|IMPLEMENTS| Transport
TransportError -->|INHERITS| PerplexitySDKError
SSETransport -->|RAISES| TransportError
HttpTransport -->|CALLS| retry
style core fill:#e1f5fe
style transport fill:#fff3e0
style shared fill:#f3e5f5# Lines of code per module
find pplx_sdk -name "*.py" -exec wc -l {} + | sort -n
# Cyclomatic complexity (if radon is available)
pip install radon 2>/dev/null && radon cc pplx_sdk/ -s -a
# Function count per module
grep -c "def " pplx_sdk/**/*.py 2>/dev/null || \
find pplx_sdk -name "*.py" -exec grep -c "def " {} +
# Class count per module
find pplx_sdk -name "*.py" -exec grep -c "class " {} +| Check | Command | What to Look For |
|---|---|---|
| Circular imports | AST import graph cycle detection | Cycles in the dependency graph |
| Layer violations | Import direction analysis | Lower layers importing higher layers |
| Unused imports | | Imports that are never used |
| Dead code | | Functions/classes never called |
| Missing types | | Untyped functions or |
| Large functions | AST line count per function | Functions > 50 lines |
| Deep nesting | AST indent depth analysis | Nesting > 4 levels |
| Protocol conformance | Compare class methods vs Protocol | Missing protocol method implementations |
# ESM imports (import ... from '...')
grep -rn "import .* from " src/ --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" | \
sed "s/:/ → /" | sort -u
# Re-exports / barrel files
grep -rn "export .* from " src/ --include="*.ts" --include="*.tsx" | sort -u
# Dynamic imports (lazy loading / code splitting)
grep -rn "import(" src/ --include="*.ts" --include="*.tsx" | sort -u
# CommonJS requires (legacy)
grep -rn "require(" src/ --include="*.js" | sort -u# Find all React components (function components)
grep -rn "export \(default \)\?function \|export const .* = (" src/ --include="*.tsx" --include="*.jsx"
# Find component usage (JSX self-closing or opening tags)
grep -rn "<[A-Z][a-zA-Z]*[\ />\n]" src/ --include="*.tsx" --include="*.jsx" | \
grep -oP '<[A-Z][a-zA-Z]*' | sort | uniq -c | sort -rn
# Find hooks usage
grep -rn "use[A-Z][a-zA-Z]*(" src/ --include="*.ts" --include="*.tsx" | \
grep -oP 'use[A-Z][a-zA-Z]*' | sort | uniq -c | sort -rn
# Find context providers
grep -rn "createContext\|\.Provider" src/ --include="*.tsx" --include="*.ts"# Next.js App Router pages
find app/ -name "page.tsx" -o -name "page.jsx" -o -name "layout.tsx" 2>/dev/null
# Next.js Pages Router
find pages/ -name "*.tsx" -o -name "*.jsx" 2>/dev/null
# React Router route definitions
grep -rn "Route\|createBrowserRouter\|path:" src/ --include="*.tsx" --include="*.ts"graph TD
subgraph pages["Pages / Routes"]
SearchPage["SearchPage"]
ThreadPage["ThreadPage"]
end
subgraph components["Components"]
SearchBar["SearchBar"]
ResponseView["ResponseView"]
SourceCard["SourceCard"]
end
subgraph hooks["Hooks"]
useQuery["useQuery()"]
useStreaming["useStreaming()"]
useAuth["useAuth()"]
end
subgraph services["Services / API"]
apiClient["apiClient"]
sseHandler["sseHandler"]
end
SearchPage --> SearchBar
SearchPage --> useQuery
ThreadPage --> ResponseView
ThreadPage --> useStreaming
ResponseView --> SourceCard
useQuery --> apiClient
useStreaming --> sseHandler
SearchBar --> useAuth
style pages fill:#e1f5fe
style components fill:#fff3e0
style hooks fill:#f3e5f5
style services fill:#e8f5e9| Entity | Source | Example |
|---|---|---|
| Function returning JSX | |
| | |
| | |
| Page/layout file | |
| API client module | |
| State management | Zustand store, Redux slice |
| TypeScript interface/type | |
| Relationship | Meaning | Example |
|---|---|---|
| Component renders another | |
| Component uses a hook | |
| Component provides context | |
| Component consumes context | |
| Hook/service calls API endpoint | |
| Module imports another | |
| Dynamic import for code splitting | |
| Type extends another | |
## Code Analysis Report: pplx-sdk
### Module Summary
| Module | Classes | Functions | Lines | Complexity |
|--------|---------|-----------|-------|------------|
| core/protocols.py | 2 | 0 | 45 | A |
| transport/sse.py | 1 | 5 | 180 | B |
| ... | ... | ... | ... | ... |
### SPA Component Summary (when analyzing JS/TS)
| Component | Props | Hooks Used | Children | Lines |
|-----------|-------|------------|----------|-------|
| SearchPage | 2 | useQuery, useAuth | SearchBar, ResultList | 120 |
| ... | ... | ... | ... | ... |
### Dependency Graph
[Mermaid diagram]
### Knowledge Graph
- N entities, M relationships
- [Mermaid diagram]
### Layer Compliance
- ✅ No circular dependencies
- ✅ No upward layer violations
- ⚠️ 2 unused imports detected
### Complexity Hotspots
| Function | Module | CC | Lines | Recommendation |
|----------|--------|----|-------|----------------|
| `_parse_event` | transport/sse.py | 8 | 45 | Consider splitting |
### Dead Code
| Entity | Module | Last Referenced |
|--------|--------|----------------|
| ... | ... | ... |llms.txt# Check if a dependency publishes llms.txt
curl -sf https://docs.pydantic.dev/llms.txt | head -20
curl -sf https://www.python-httpx.org/llms.txt | head -20
# Check for the full version (entire docs in one file)
curl -sf https://docs.pydantic.dev/llms-full.txt | head -20
# Use the llms-txt MCP server for indexed search
# Tools: list_llm_txt, get_llm_txt, search_llm_txt# Check if a site publishes agent skills
curl -sf https://example.com/.well-known/agentskills.io/skills/ | head -20
# Look for specific SKILL.md files
curl -sf https://example.com/.well-known/agentskills.io/skills/default/SKILL.md| MCP Server | Purpose | Key Tools |
|---|---|---|
| Library docs lookup | Context-aware search by library name |
| GitHub repo documentation | |
| llms.txt file search | |
| Any URL as markdown | General-purpose URL fetching |
1. Check llms.txt at dependency's docs URL
2. Check .well-known/agentskills.io for skills
3. Query deepwiki for the dependency's GitHub repo
4. Query context7 for library-specific context
5. Fall back to fetch for raw documentation URLs| When code-analysis finds... | Delegate to... | Action |
|---|---|---|
| Layer violation | | Produce corrected dependency diagram |
| Circular import | | Review and suggest refactor |
| Missing protocol method | | Scaffold missing implementation |
| Dead code | | Confirm and remove |
| High complexity | | Review for refactor opportunity |
| New entity relationships | | Update architecture diagrams |
| SPA component tree | | Cross-reference with runtime fiber tree |
| SPA API endpoints in source | | Validate against live traffic captures |
| SPA hook dependencies | | Visualize hook → service → API chain |
| SPA barrel file cycles | | Review circular re-exports |