ref-agents 1.0.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.
- ref_agents/__init__.py +9 -0
- ref_agents/api_keys.json.example +8 -0
- ref_agents/auth.py +129 -0
- ref_agents/codemap/..md +62 -0
- ref_agents/codemap/CODE_MAP.md +37 -0
- ref_agents/codemap/core.md +43 -0
- ref_agents/codemap/models.md +43 -0
- ref_agents/codemap/prompts.md +40 -0
- ref_agents/codemap/security.md +45 -0
- ref_agents/codemap/tools.md +94 -0
- ref_agents/codemap/tools_browser.md +44 -0
- ref_agents/codemap/utils.md +42 -0
- ref_agents/codemap/workflow.md +42 -0
- ref_agents/config/ai_patterns.yaml +101 -0
- ref_agents/config/frameworks/angular.yaml +104 -0
- ref_agents/config/frameworks/aspnet.yaml +84 -0
- ref_agents/config/frameworks/ef_core.yaml +81 -0
- ref_agents/config/frameworks/react.yaml +111 -0
- ref_agents/config/frameworks/spring_boot.yaml +117 -0
- ref_agents/config/languages/csharp.yaml +153 -0
- ref_agents/config/languages/java.yaml +188 -0
- ref_agents/config/languages/javascript.yaml +172 -0
- ref_agents/config/languages/python.yaml +153 -0
- ref_agents/config/languages/typescript.yaml +193 -0
- ref_agents/constants.py +553 -0
- ref_agents/core/__init__.py +15 -0
- ref_agents/core/config_loader.py +160 -0
- ref_agents/core/config_models.py +167 -0
- ref_agents/core/config_parsing.py +84 -0
- ref_agents/core/language_detector.py +388 -0
- ref_agents/core/validation_models.py +66 -0
- ref_agents/core/validation_primitives.py +176 -0
- ref_agents/errors.py +34 -0
- ref_agents/license_client.py +247 -0
- ref_agents/models/__init__.py +22 -0
- ref_agents/models/gherkin.py +45 -0
- ref_agents/models/hierarchy.py +80 -0
- ref_agents/models/invest.py +59 -0
- ref_agents/models/version.py +49 -0
- ref_agents/prompts/__init__.py +9 -0
- ref_agents/prompts/start_agent.py +772 -0
- ref_agents/rules/architecture/backend_patterns.md +43 -0
- ref_agents/rules/architecture/diagramming.md +100 -0
- ref_agents/rules/architecture/frontend_patterns.md +40 -0
- ref_agents/rules/architecture/impact_analysis.md +129 -0
- ref_agents/rules/architecture/migration_strategy.md +208 -0
- ref_agents/rules/architecture/regression_protocol.md +77 -0
- ref_agents/rules/architecture/system_design.md +97 -0
- ref_agents/rules/common/codemap_standard.md +97 -0
- ref_agents/rules/common/core_protocol.md +59 -0
- ref_agents/rules/common/prompt_engineering.md +294 -0
- ref_agents/rules/development/debugging.md +32 -0
- ref_agents/rules/development/implementation.md +205 -0
- ref_agents/rules/operations/completion.md +119 -0
- ref_agents/rules/operations/cutover_protocol.md +218 -0
- ref_agents/rules/operations/discovery.md +179 -0
- ref_agents/rules/operations/fix_workflow.md +87 -0
- ref_agents/rules/operations/forensics.md +278 -0
- ref_agents/rules/operations/platform.md +263 -0
- ref_agents/rules/operations/synchronous_flow.md +25 -0
- ref_agents/rules/product/ac_validation.md +25 -0
- ref_agents/rules/product/brainstorming.md +27 -0
- ref_agents/rules/product/ref_flow.md +101 -0
- ref_agents/rules/product/requirements_std.md +114 -0
- ref_agents/rules/product/spec_writing.md +235 -0
- ref_agents/rules/product/strategy.md +96 -0
- ref_agents/rules/quality/documentation_standards.md +46 -0
- ref_agents/rules/quality/parity_testing.md +234 -0
- ref_agents/rules/quality/project_documentation.md +56 -0
- ref_agents/rules/quality/qa_lead.md +111 -0
- ref_agents/rules/quality/test_design.md +146 -0
- ref_agents/rules/quality/testing_standards.md +293 -0
- ref_agents/rules/review/pr_review.md +116 -0
- ref_agents/rules/security/security_audit.md +83 -0
- ref_agents/security/__init__.py +22 -0
- ref_agents/security/dependency_audit.py +188 -0
- ref_agents/security/file_audit.py +208 -0
- ref_agents/security/network_scan.py +179 -0
- ref_agents/security/report_generator.py +313 -0
- ref_agents/security/secret_scan.py +252 -0
- ref_agents/security/url_scan.py +240 -0
- ref_agents/security_scan.py +236 -0
- ref_agents/server.py +1586 -0
- ref_agents/session.py +100 -0
- ref_agents/tool_names.py +55 -0
- ref_agents/tools/__init__.py +8 -0
- ref_agents/tools/agents_generator.py +315 -0
- ref_agents/tools/ai_pattern_detector.py +815 -0
- ref_agents/tools/brownfield_populator.py +529 -0
- ref_agents/tools/browser/__init__.py +50 -0
- ref_agents/tools/browser/evidence_verifier.py +302 -0
- ref_agents/tools/browser/execution_logger.py +249 -0
- ref_agents/tools/browser/playwright_mcp_client.py +259 -0
- ref_agents/tools/browser/screenshot_utils.py +184 -0
- ref_agents/tools/browser/test_executor.py +537 -0
- ref_agents/tools/code_quality_scanner.py +629 -0
- ref_agents/tools/codemap/..md +93 -0
- ref_agents/tools/codemap/CODE_MAP.md +30 -0
- ref_agents/tools/codemap/browser.md +44 -0
- ref_agents/tools/codemap.py +403 -0
- ref_agents/tools/codemap_freshness.py +234 -0
- ref_agents/tools/comment_smell_scanner.py +346 -0
- ref_agents/tools/complexity.py +436 -0
- ref_agents/tools/complexity_ast.py +333 -0
- ref_agents/tools/compliance.py +246 -0
- ref_agents/tools/compliance_remediation.py +846 -0
- ref_agents/tools/context_graph.py +839 -0
- ref_agents/tools/context_manager.py +550 -0
- ref_agents/tools/context_tools.py +121 -0
- ref_agents/tools/cross_repo_linker.py +393 -0
- ref_agents/tools/dead_code_scanner.py +637 -0
- ref_agents/tools/debt_scanner.py +1092 -0
- ref_agents/tools/dependency_graph.py +272 -0
- ref_agents/tools/discovery_audit.py +372 -0
- ref_agents/tools/docs_scanner.py +600 -0
- ref_agents/tools/evaluate_gate.py +119 -0
- ref_agents/tools/external_detector.py +524 -0
- ref_agents/tools/features_generator.py +282 -0
- ref_agents/tools/flow_gap_detector.py +373 -0
- ref_agents/tools/flow_mapper.py +327 -0
- ref_agents/tools/full_suite_runner.py +740 -0
- ref_agents/tools/gherkin_parser.py +227 -0
- ref_agents/tools/guard_tools.py +139 -0
- ref_agents/tools/handoff_tools.py +282 -0
- ref_agents/tools/health_scanner.py +1211 -0
- ref_agents/tools/hierarchy_manager.py +289 -0
- ref_agents/tools/invest_scorer.py +249 -0
- ref_agents/tools/jira_confluence_export.py +306 -0
- ref_agents/tools/json_output.py +76 -0
- ref_agents/tools/migration_mapper.py +946 -0
- ref_agents/tools/migration_readiness_scanner.py +209 -0
- ref_agents/tools/pattern_learner.py +522 -0
- ref_agents/tools/report_utils.py +155 -0
- ref_agents/tools/requirements_serializer.py +225 -0
- ref_agents/tools/security_audit_tool.py +106 -0
- ref_agents/tools/sequencing_engine.py +288 -0
- ref_agents/tools/summary_generator.py +275 -0
- ref_agents/tools/symbol_resolver.py +306 -0
- ref_agents/tools/symbol_smoke_runner.py +336 -0
- ref_agents/tools/test_plan_validator.py +189 -0
- ref_agents/tools/test_smell_walker.py +902 -0
- ref_agents/tools/tier1_fixer.py +502 -0
- ref_agents/tools/validators/__init__.py +419 -0
- ref_agents/tools/validators/architect.py +268 -0
- ref_agents/tools/validators/cutover_engineer.py +167 -0
- ref_agents/tools/validators/developer.py +180 -0
- ref_agents/tools/validators/discovery.py +150 -0
- ref_agents/tools/validators/forensic_engineer.py +191 -0
- ref_agents/tools/validators/impact_architect.py +181 -0
- ref_agents/tools/validators/migration_planner.py +166 -0
- ref_agents/tools/validators/parity_tester.py +155 -0
- ref_agents/tools/validators/platform_engineer.py +134 -0
- ref_agents/tools/validators/pr_reviewer.py +129 -0
- ref_agents/tools/validators/product_manager.py +291 -0
- ref_agents/tools/validators/qa_lead.py +172 -0
- ref_agents/tools/validators/scrum_master.py +212 -0
- ref_agents/tools/validators/security_owner.py +162 -0
- ref_agents/tools/validators/specifier.py +134 -0
- ref_agents/tools/validators/strategist.py +149 -0
- ref_agents/tools/validators/tester.py +121 -0
- ref_agents/tools/version_manager.py +202 -0
- ref_agents/tools/workflow_tools.py +1549 -0
- ref_agents/utils/__init__.py +21 -0
- ref_agents/utils/git_utils.py +351 -0
- ref_agents/utils/handoff_logger.py +368 -0
- ref_agents/utils/ignore_matcher.py +270 -0
- ref_agents/workflow/__init__.py +19 -0
- ref_agents/workflow/capabilities.py +328 -0
- ref_agents/workflow/state_machine.py +708 -0
- ref_agents/workflow/transitions.py +658 -0
- ref_agents-1.0.0.dist-info/METADATA +365 -0
- ref_agents-1.0.0.dist-info/RECORD +175 -0
- ref_agents-1.0.0.dist-info/WHEEL +4 -0
- ref_agents-1.0.0.dist-info/entry_points.txt +2 -0
- ref_agents-1.0.0.dist-info/licenses/LICENSE +115 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# CODE_MAP: `.`
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Purpose
|
|
5
|
+
<!-- REF:MANUAL -->
|
|
6
|
+
[Describe the purpose of this module]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Directory Structure
|
|
11
|
+
<!-- REF:AUTO -->
|
|
12
|
+
| Item | Type | Purpose | Dependencies |
|
|
13
|
+
|------|------|---------|--------------|
|
|
14
|
+
| `browser/` | Dir | [Desc] | - |
|
|
15
|
+
| `codemap/` | Dir | [Desc] | - |
|
|
16
|
+
| `ref-reports/` | Dir | [Desc] | - |
|
|
17
|
+
| `invest_scorer.py` | File | [Desc] | - |
|
|
18
|
+
| `workflow_tools.py` | File | [Desc] | - |
|
|
19
|
+
| `test_plan_validator.py` | File | [Desc] | - |
|
|
20
|
+
| `compliance_remediation.py` | File | [Desc] | - |
|
|
21
|
+
| `jira_confluence_export.py` | File | [Desc] | - |
|
|
22
|
+
| `context_manager.py` | File | [Desc] | - |
|
|
23
|
+
| `agents_generator.py` | File | [Desc] | - |
|
|
24
|
+
| `requirements_serializer.py` | File | [Desc] | - |
|
|
25
|
+
| `debt_scanner.py` | File | [Desc] | - |
|
|
26
|
+
| `version_manager.py` | File | [Desc] | - |
|
|
27
|
+
| `report_utils.py` | File | [Desc] | - |
|
|
28
|
+
| `compliance.py` | File | [Desc] | - |
|
|
29
|
+
| `flow_mapper.py` | File | [Desc] | - |
|
|
30
|
+
| `ai_pattern_detector.py` | File | [Desc] | - |
|
|
31
|
+
| `dependency_graph.py` | File | [Desc] | - |
|
|
32
|
+
| `migration_readiness_scanner.py` | File | [Desc] | - |
|
|
33
|
+
| `complexity.py` | File | [Desc] | - |
|
|
34
|
+
| `health_scanner.py` | File | [Desc] | - |
|
|
35
|
+
| `summary_generator.py` | File | [Desc] | - |
|
|
36
|
+
| `external_detector.py` | File | [Desc] | - |
|
|
37
|
+
| `__init__.py` | File | [Desc] | - |
|
|
38
|
+
| `dead_code_scanner.py` | File | [Desc] | - |
|
|
39
|
+
| `codemap.py` | File | [Desc] | - |
|
|
40
|
+
| `tier1_fixer.py` | File | [Desc] | - |
|
|
41
|
+
| `brownfield_populator.py` | File | [Desc] | - |
|
|
42
|
+
| `pattern_learner.py` | File | [Desc] | - |
|
|
43
|
+
| `context_tools.py` | File | [Desc] | - |
|
|
44
|
+
| `context_graph.py` | File | [Desc] | - |
|
|
45
|
+
| `json_output.py` | File | [Desc] | - |
|
|
46
|
+
| `evaluate_gate.py` | File | [Desc] | - |
|
|
47
|
+
| `docs_scanner.py` | File | [Desc] | - |
|
|
48
|
+
| `guard_tools.py` | File | [Desc] | - |
|
|
49
|
+
| `gherkin_parser.py` | File | [Desc] | - |
|
|
50
|
+
| `sequencing_engine.py` | File | [Desc] | - |
|
|
51
|
+
| `symbol_resolver.py` | File | [Desc] | - |
|
|
52
|
+
| `test_smell_walker.py` | File | [Desc] | - |
|
|
53
|
+
| `code_quality_scanner.py` | File | [Desc] | - |
|
|
54
|
+
| `flow_gap_detector.py` | File | [Desc] | - |
|
|
55
|
+
| `features_generator.py` | File | [Desc] | - |
|
|
56
|
+
| `complexity_ast.py` | File | [Desc] | - |
|
|
57
|
+
| `codemap_freshness.py` | File | [Desc] | - |
|
|
58
|
+
| `discovery_audit.py` | File | [Desc] | - |
|
|
59
|
+
| `migration_mapper.py` | File | [Desc] | - |
|
|
60
|
+
| `handoff_tools.py` | File | [Desc] | - |
|
|
61
|
+
| `full_suite_runner.py` | File | [Desc] | - |
|
|
62
|
+
| `security_audit_tool.py` | File | [Desc] | - |
|
|
63
|
+
| `cross_repo_linker.py` | File | [Desc] | - |
|
|
64
|
+
| `hierarchy_manager.py` | File | [Desc] | - |
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Key Classes / Functions
|
|
68
|
+
<!-- REF:MANUAL -->
|
|
69
|
+
| Name | File | Purpose | Dependencies |
|
|
70
|
+
|------|------|---------|--------------|
|
|
71
|
+
| ... | ... | ... | ... |
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## External Dependencies
|
|
76
|
+
<!-- REF:AUTO -->
|
|
77
|
+
*Auto-detected by external_detector.py*
|
|
78
|
+
|
|
79
|
+
### Hardcoded External URLs
|
|
80
|
+
|
|
81
|
+
| Domain | File | Line | Notes |
|
|
82
|
+
|--------|------|------|-------|
|
|
83
|
+
| `cdn.jsdelivr.net` | `context_graph.py` | 713 | ⚠️ Consider env var |
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## Integration Boundaries
|
|
88
|
+
<!-- REF:MANUAL -->
|
|
89
|
+
*Where this module connects to external systems*
|
|
90
|
+
|
|
91
|
+
| Boundary Type | Local Code | External System | Data Flow | Risk Level |
|
|
92
|
+
|---------------|------------|-----------------|-----------|------------|
|
|
93
|
+
| API Call | [file.py] | [External API] | Outbound | Medium |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# CODE_MAP: Project Overview
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Purpose
|
|
5
|
+
<!-- REF:MANUAL -->
|
|
6
|
+
[Describe the overall project purpose]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Package Index
|
|
11
|
+
<!-- REF:AUTO -->
|
|
12
|
+
| Package | Path | Purpose |
|
|
13
|
+
|---------|------|---------|
|
|
14
|
+
| [tools](./..md) | `.` | [Desc] |
|
|
15
|
+
| [browser](./browser.md) | `browser` | [Desc] |
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Project Statistics
|
|
19
|
+
<!-- REF:AUTO -->
|
|
20
|
+
- **Total Packages:** 2
|
|
21
|
+
- **Generated:** Auto-updated by discovery
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Quick Navigation
|
|
25
|
+
<!-- REF:MANUAL -->
|
|
26
|
+
*Add frequently accessed modules here*
|
|
27
|
+
|
|
28
|
+
| Module | Description |
|
|
29
|
+
|--------|-------------|
|
|
30
|
+
| ... | ... |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CODE_MAP: `browser`
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Purpose
|
|
5
|
+
<!-- REF:MANUAL -->
|
|
6
|
+
[Describe the purpose of this module]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Directory Structure
|
|
11
|
+
<!-- REF:AUTO -->
|
|
12
|
+
| Item | Type | Purpose | Dependencies |
|
|
13
|
+
|------|------|---------|--------------|
|
|
14
|
+
| `screenshot_utils.py` | File | [Desc] | - |
|
|
15
|
+
| `__init__.py` | File | [Desc] | - |
|
|
16
|
+
| `evidence_verifier.py` | File | [Desc] | - |
|
|
17
|
+
| `test_executor.py` | File | [Desc] | - |
|
|
18
|
+
| `execution_logger.py` | File | [Desc] | - |
|
|
19
|
+
| `playwright_mcp_client.py` | File | [Desc] | - |
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Key Classes / Functions
|
|
23
|
+
<!-- REF:MANUAL -->
|
|
24
|
+
| Name | File | Purpose | Dependencies |
|
|
25
|
+
|------|------|---------|--------------|
|
|
26
|
+
| ... | ... | ... | ... |
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## External Dependencies
|
|
31
|
+
<!-- REF:AUTO -->
|
|
32
|
+
*Auto-detected by external_detector.py*
|
|
33
|
+
|
|
34
|
+
No external dependencies detected.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Integration Boundaries
|
|
39
|
+
<!-- REF:MANUAL -->
|
|
40
|
+
*Where this module connects to external systems*
|
|
41
|
+
|
|
42
|
+
| Boundary Type | Local Code | External System | Data Flow | Risk Level |
|
|
43
|
+
|---------------|------------|-----------------|-----------|------------|
|
|
44
|
+
| API Call | [file.py] | [External API] | Outbound | Medium |
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""REF Code Map Generation Tool.
|
|
2
|
+
|
|
3
|
+
Generates centralized CODE_MAP.md files in codemap/ directory.
|
|
4
|
+
Supports merge strategy with REF:MANUAL/REF:AUTO section markers.
|
|
5
|
+
Finds Python packages by __init__.py presence.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from ref_agents.tools.codemap import generate_codemaps
|
|
9
|
+
result = generate_codemaps("/path/to/project")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import NamedTuple
|
|
14
|
+
|
|
15
|
+
import structlog
|
|
16
|
+
|
|
17
|
+
from ref_agents.tools.external_detector import scan_directory
|
|
18
|
+
|
|
19
|
+
logger = structlog.get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
# Directories to skip during package discovery
|
|
22
|
+
SKIP_DIRS = {
|
|
23
|
+
".git",
|
|
24
|
+
".venv",
|
|
25
|
+
"venv",
|
|
26
|
+
"__pycache__",
|
|
27
|
+
"node_modules",
|
|
28
|
+
".mypy_cache",
|
|
29
|
+
".pytest_cache",
|
|
30
|
+
".ruff_cache",
|
|
31
|
+
"dist",
|
|
32
|
+
"build",
|
|
33
|
+
".tox",
|
|
34
|
+
"htmlcov",
|
|
35
|
+
".eggs",
|
|
36
|
+
"*.egg-info",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Section(NamedTuple):
|
|
41
|
+
"""Parsed markdown section."""
|
|
42
|
+
|
|
43
|
+
name: str
|
|
44
|
+
content: str
|
|
45
|
+
is_manual: bool # True if marked REF:MANUAL
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def find_python_packages(root: Path) -> list[Path]:
|
|
49
|
+
"""Find all Python packages (dirs with __init__.py).
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
root: Project root directory.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of package directories sorted by path.
|
|
56
|
+
"""
|
|
57
|
+
packages: list[Path] = []
|
|
58
|
+
|
|
59
|
+
for path in root.rglob("__init__.py"):
|
|
60
|
+
pkg_dir = path.parent
|
|
61
|
+
|
|
62
|
+
# Skip if in excluded directory
|
|
63
|
+
skip = False
|
|
64
|
+
for part in pkg_dir.parts:
|
|
65
|
+
if part in SKIP_DIRS or part.endswith(".egg-info"):
|
|
66
|
+
skip = True
|
|
67
|
+
break
|
|
68
|
+
if skip:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
packages.append(pkg_dir)
|
|
72
|
+
|
|
73
|
+
# Sort by path depth then alphabetically
|
|
74
|
+
return sorted(packages, key=lambda p: (len(p.parts), str(p)))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_sections(content: str) -> dict[str, Section]:
|
|
78
|
+
"""Parse markdown into sections by ## headers.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
content: Markdown content.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dict mapping section name to Section.
|
|
85
|
+
"""
|
|
86
|
+
sections: dict[str, Section] = {}
|
|
87
|
+
current_name = "_header"
|
|
88
|
+
current_lines: list[str] = []
|
|
89
|
+
is_manual = False
|
|
90
|
+
|
|
91
|
+
for line in content.splitlines():
|
|
92
|
+
if line.startswith("## "):
|
|
93
|
+
# Save previous section
|
|
94
|
+
if current_lines:
|
|
95
|
+
sections[current_name] = Section(
|
|
96
|
+
name=current_name,
|
|
97
|
+
content="\n".join(current_lines),
|
|
98
|
+
is_manual=is_manual,
|
|
99
|
+
)
|
|
100
|
+
current_name = line[3:].strip()
|
|
101
|
+
current_lines = [line]
|
|
102
|
+
is_manual = False
|
|
103
|
+
elif "<!-- REF:MANUAL -->" in line:
|
|
104
|
+
is_manual = True
|
|
105
|
+
current_lines.append(line)
|
|
106
|
+
else:
|
|
107
|
+
current_lines.append(line)
|
|
108
|
+
|
|
109
|
+
# Save last section
|
|
110
|
+
if current_lines:
|
|
111
|
+
sections[current_name] = Section(
|
|
112
|
+
name=current_name,
|
|
113
|
+
content="\n".join(current_lines),
|
|
114
|
+
is_manual=is_manual,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return sections
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _merge_sections(existing: str, new: str) -> str:
|
|
121
|
+
"""Merge existing and new content, preserving manual sections.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
existing: Existing file content.
|
|
125
|
+
new: Newly generated content.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Merged content.
|
|
129
|
+
"""
|
|
130
|
+
existing_sections = _parse_sections(existing)
|
|
131
|
+
new_sections = _parse_sections(new)
|
|
132
|
+
|
|
133
|
+
# Start with new content structure
|
|
134
|
+
merged_lines: list[str] = []
|
|
135
|
+
|
|
136
|
+
for section_name, new_section in new_sections.items():
|
|
137
|
+
if section_name in existing_sections:
|
|
138
|
+
existing_section = existing_sections[section_name]
|
|
139
|
+
if existing_section.is_manual:
|
|
140
|
+
# Preserve manual sections
|
|
141
|
+
merged_lines.append(existing_section.content)
|
|
142
|
+
else:
|
|
143
|
+
# Use new content for auto sections
|
|
144
|
+
merged_lines.append(new_section.content)
|
|
145
|
+
else:
|
|
146
|
+
merged_lines.append(new_section.content)
|
|
147
|
+
|
|
148
|
+
return "\n\n".join(merged_lines)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _generate_external_deps_section(directory: Path) -> str:
|
|
152
|
+
"""Generate External Dependencies section using auto-detection.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
directory: Path to scan for external dependencies.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Markdown formatted External Dependencies section.
|
|
159
|
+
"""
|
|
160
|
+
result = scan_directory(directory)
|
|
161
|
+
|
|
162
|
+
output = "## External Dependencies\n"
|
|
163
|
+
output += "<!-- REF:AUTO -->\n"
|
|
164
|
+
output += "*Auto-detected by external_detector.py*\n\n"
|
|
165
|
+
|
|
166
|
+
if not result.dependencies:
|
|
167
|
+
output += "No external dependencies detected.\n\n"
|
|
168
|
+
return output
|
|
169
|
+
|
|
170
|
+
# Environment variables
|
|
171
|
+
if result.env_vars:
|
|
172
|
+
output += "### Environment Variables (External Endpoints)\n\n"
|
|
173
|
+
output += "| Variable | File | Line | Notes |\n"
|
|
174
|
+
output += "|----------|------|------|-------|\n"
|
|
175
|
+
for dep in result.env_vars:
|
|
176
|
+
filename = Path(dep.source_file).name
|
|
177
|
+
line = dep.line_number if dep.line_number else "-"
|
|
178
|
+
output += f"| `{dep.name}` | `{filename}` | {line} | {dep.description} |\n"
|
|
179
|
+
output += "\n"
|
|
180
|
+
|
|
181
|
+
# API clients
|
|
182
|
+
if result.api_clients:
|
|
183
|
+
output += "### External API Clients\n\n"
|
|
184
|
+
output += "| Library | Service | File | Line |\n"
|
|
185
|
+
output += "|---------|---------|------|------|\n"
|
|
186
|
+
for dep in result.api_clients:
|
|
187
|
+
filename = Path(dep.source_file).name
|
|
188
|
+
line = dep.line_number if dep.line_number else "-"
|
|
189
|
+
output += f"| `{dep.name}` | {dep.description} | `{filename}` | {line} |\n"
|
|
190
|
+
output += "\n"
|
|
191
|
+
|
|
192
|
+
# Hardcoded URLs
|
|
193
|
+
if result.urls:
|
|
194
|
+
output += "### Hardcoded External URLs\n\n"
|
|
195
|
+
output += "| Domain | File | Line | Notes |\n"
|
|
196
|
+
output += "|--------|------|------|-------|\n"
|
|
197
|
+
for dep in result.urls:
|
|
198
|
+
filename = Path(dep.source_file).name
|
|
199
|
+
line = dep.line_number if dep.line_number else "-"
|
|
200
|
+
output += f"| `{dep.name}` | `{filename}` | {line} | ⚠️ Consider env var |\n"
|
|
201
|
+
output += "\n"
|
|
202
|
+
|
|
203
|
+
logger.info(
|
|
204
|
+
"external_deps_detected",
|
|
205
|
+
env_vars=len(result.env_vars),
|
|
206
|
+
api_clients=len(result.api_clients),
|
|
207
|
+
urls=len(result.urls),
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return output
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _generate_package_codemap(pkg_dir: Path, root: Path) -> str:
|
|
214
|
+
"""Generate CODE_MAP content for a Python package.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
pkg_dir: Package directory path.
|
|
218
|
+
root: Project root for relative path calculation.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Markdown content for the package codemap.
|
|
222
|
+
"""
|
|
223
|
+
rel_path = pkg_dir.relative_to(root)
|
|
224
|
+
|
|
225
|
+
files = [
|
|
226
|
+
f.name for f in pkg_dir.iterdir() if f.is_file() and not f.name.startswith(".")
|
|
227
|
+
]
|
|
228
|
+
subdirs = [
|
|
229
|
+
d.name for d in pkg_dir.iterdir() if d.is_dir() and not d.name.startswith(".")
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
template = f"""# CODE_MAP: `{rel_path}`
|
|
233
|
+
|
|
234
|
+
## Purpose
|
|
235
|
+
<!-- REF:MANUAL -->
|
|
236
|
+
[Describe the purpose of this module]
|
|
237
|
+
|
|
238
|
+
## Directory Structure
|
|
239
|
+
<!-- REF:AUTO -->
|
|
240
|
+
| Item | Type | Purpose | Dependencies |
|
|
241
|
+
|------|------|---------|--------------|
|
|
242
|
+
"""
|
|
243
|
+
for sub in subdirs:
|
|
244
|
+
if sub in SKIP_DIRS:
|
|
245
|
+
continue
|
|
246
|
+
template += f"| `{sub}/` | Dir | [Desc] | - |\n"
|
|
247
|
+
|
|
248
|
+
for f in files:
|
|
249
|
+
template += f"| `{f}` | File | [Desc] | - |\n"
|
|
250
|
+
|
|
251
|
+
template += """
|
|
252
|
+
## Key Classes / Functions
|
|
253
|
+
<!-- REF:MANUAL -->
|
|
254
|
+
| Name | File | Purpose | Dependencies |
|
|
255
|
+
|------|------|---------|--------------|
|
|
256
|
+
| ... | ... | ... | ... |
|
|
257
|
+
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
# Auto-detect external dependencies
|
|
261
|
+
template += _generate_external_deps_section(pkg_dir)
|
|
262
|
+
|
|
263
|
+
template += """
|
|
264
|
+
## Integration Boundaries
|
|
265
|
+
<!-- REF:MANUAL -->
|
|
266
|
+
*Where this module connects to external systems*
|
|
267
|
+
|
|
268
|
+
| Boundary Type | Local Code | External System | Data Flow | Risk Level |
|
|
269
|
+
|---------------|------------|-----------------|-----------|------------|
|
|
270
|
+
| API Call | [file.py] | [External API] | Outbound | Medium |
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
return template
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _generate_meta_map(packages: list[Path], root: Path, codemap_dir: Path) -> str:
|
|
277
|
+
"""Generate root CODE_MAP.md linking all package maps.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
packages: List of package directories.
|
|
281
|
+
root: Project root.
|
|
282
|
+
codemap_dir: Path to codemap/ directory.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Markdown content for meta-map.
|
|
286
|
+
"""
|
|
287
|
+
template = """# CODE_MAP: Project Overview
|
|
288
|
+
|
|
289
|
+
## Purpose
|
|
290
|
+
<!-- REF:MANUAL -->
|
|
291
|
+
[Describe the overall project purpose]
|
|
292
|
+
|
|
293
|
+
## Package Index
|
|
294
|
+
<!-- REF:AUTO -->
|
|
295
|
+
| Package | Path | Purpose |
|
|
296
|
+
|---------|------|---------|
|
|
297
|
+
"""
|
|
298
|
+
for pkg in packages:
|
|
299
|
+
rel_path = pkg.relative_to(root)
|
|
300
|
+
map_name = str(rel_path).replace("/", "_") + ".md"
|
|
301
|
+
template += f"| [{pkg.name}](./{map_name}) | `{rel_path}` | [Desc] |\n"
|
|
302
|
+
|
|
303
|
+
template += f"""
|
|
304
|
+
## Project Statistics
|
|
305
|
+
<!-- REF:AUTO -->
|
|
306
|
+
- **Total Packages:** {len(packages)}
|
|
307
|
+
- **Generated:** Auto-updated by discovery
|
|
308
|
+
|
|
309
|
+
## Quick Navigation
|
|
310
|
+
<!-- REF:MANUAL -->
|
|
311
|
+
*Add frequently accessed modules here*
|
|
312
|
+
|
|
313
|
+
| Module | Description |
|
|
314
|
+
|--------|-------------|
|
|
315
|
+
| ... | ... |
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
return template
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def generate_codemaps(directory: str) -> str:
|
|
322
|
+
"""Generate centralized CODE_MAPs for all Python packages.
|
|
323
|
+
|
|
324
|
+
Creates codemap/ directory at project root with:
|
|
325
|
+
- CODE_MAP.md (meta-map linking all packages)
|
|
326
|
+
- <package>_CODE_MAP.md for each Python package
|
|
327
|
+
|
|
328
|
+
Merges with existing files, preserving REF:MANUAL sections.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
directory: Path to project root.
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Status message with summary.
|
|
335
|
+
"""
|
|
336
|
+
root = Path(directory)
|
|
337
|
+
if not root.exists():
|
|
338
|
+
return f"Error: Directory {directory} does not exist."
|
|
339
|
+
|
|
340
|
+
# Create codemap directory
|
|
341
|
+
codemap_dir = root / "codemap"
|
|
342
|
+
codemap_dir.mkdir(exist_ok=True)
|
|
343
|
+
|
|
344
|
+
packages = find_python_packages(root)
|
|
345
|
+
|
|
346
|
+
if not packages:
|
|
347
|
+
return (
|
|
348
|
+
f"No Python packages found in {directory}. Ensure __init__.py files exist."
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
generated = 0
|
|
352
|
+
updated = 0
|
|
353
|
+
|
|
354
|
+
# Generate per-package maps
|
|
355
|
+
for pkg in packages:
|
|
356
|
+
rel_path = pkg.relative_to(root)
|
|
357
|
+
map_name = str(rel_path).replace("/", "_") + ".md"
|
|
358
|
+
map_path = codemap_dir / map_name
|
|
359
|
+
|
|
360
|
+
new_content = _generate_package_codemap(pkg, root)
|
|
361
|
+
|
|
362
|
+
if map_path.exists():
|
|
363
|
+
existing_content = map_path.read_text(encoding="utf-8")
|
|
364
|
+
merged = _merge_sections(existing_content, new_content)
|
|
365
|
+
map_path.write_text(merged, encoding="utf-8")
|
|
366
|
+
updated += 1
|
|
367
|
+
logger.info("codemap_updated", package=str(rel_path), path=str(map_path))
|
|
368
|
+
else:
|
|
369
|
+
map_path.write_text(new_content, encoding="utf-8")
|
|
370
|
+
generated += 1
|
|
371
|
+
logger.info("codemap_generated", package=str(rel_path), path=str(map_path))
|
|
372
|
+
|
|
373
|
+
# Generate meta-map
|
|
374
|
+
meta_path = codemap_dir / "CODE_MAP.md"
|
|
375
|
+
meta_content = _generate_meta_map(packages, root, codemap_dir)
|
|
376
|
+
|
|
377
|
+
if meta_path.exists():
|
|
378
|
+
existing_meta = meta_path.read_text(encoding="utf-8")
|
|
379
|
+
merged_meta = _merge_sections(existing_meta, meta_content)
|
|
380
|
+
meta_path.write_text(merged_meta, encoding="utf-8")
|
|
381
|
+
else:
|
|
382
|
+
meta_path.write_text(meta_content, encoding="utf-8")
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
f"✅ CODE_MAPs generated in `{codemap_dir}`\n\n"
|
|
386
|
+
f"**Packages:** {len(packages)}\n"
|
|
387
|
+
f"**Generated:** {generated}\n"
|
|
388
|
+
f"**Updated:** {updated}\n\n"
|
|
389
|
+
f"Meta-map: `{meta_path}`"
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# Legacy function for backward compatibility
|
|
394
|
+
def generate_codemap_tool(directory: str) -> str:
|
|
395
|
+
"""Legacy wrapper - redirects to centralized codemap generation.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
directory: Path to directory to scan.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Status message with codemap paths.
|
|
402
|
+
"""
|
|
403
|
+
return generate_codemaps(directory)
|