higpertext-cli 0.8.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.
- config/adapters_config.json +450 -0
- config/antigravity_agent_template.json +31 -0
- config/app_config.json +174 -0
- config/context_engine.json +33 -0
- config/environments/model_defaults.json +5 -0
- config/governance/branching_strategy.json +36 -0
- config/governance/deployment_gates.json +30 -0
- config/governance/guidelines_contract.json +54 -0
- config/governance/quality_gates.json +39 -0
- config/governance/section_rules.json +22 -0
- config/governance/security_guardrails.json +52 -0
- config/hooks/README.md +35 -0
- config/hooks/custom/test_output_limiter.json +9 -0
- config/hooks/global/session_prompt.json +9 -0
- config/htx_config.json +24 -0
- config/profile_learner.json +18 -0
- config/profiles/base_agent.json +40 -0
- config/profiles/base_auditor.json +19 -0
- config/profiles/base_developer.json +19 -0
- config/profiles/base_operator.json +16 -0
- config/profiles/global.json +33 -0
- config/profiles/software_developer.json +23 -0
- config/router_content.json +137 -0
- config/semantic_graph.json +66 -0
- config/workflows/ado_release_flow.json +38 -0
- config/workflows/docs-update.json +33 -0
- config/workflows/governance-check.yaml +26 -0
- config/workflows/guidelines-sync.json +40 -0
- config/workflows/higpertext-build.json +73 -0
- config/workflows/higpertext-plan.json +38 -0
- config/workflows/higpertext-review.json +41 -0
- config/workflows/pr-quality-check.json +56 -0
- config/workflows/quality-remediation.json +57 -0
- higpertext/__init__.py +18 -0
- higpertext/adapters/__init__.py +27 -0
- higpertext/adapters/adapter_utils.py +604 -0
- higpertext/adapters/claude_adapter/__init__.py +0 -0
- higpertext/adapters/claude_adapter/claude_adapter.py +154 -0
- higpertext/adapters/copilot_adapter/__init__.py +0 -0
- higpertext/adapters/copilot_adapter/copilot_adapter.py +231 -0
- higpertext/adapters/gemini_adapter/__init__.py +0 -0
- higpertext/adapters/gemini_adapter/gemini_adapter.py +211 -0
- higpertext/adapters/llm_formatter.py +46 -0
- higpertext/adapters/open_code_adapter/__init__.py +0 -0
- higpertext/adapters/open_code_adapter/open_code_adapter.py +480 -0
- higpertext/capabilities/capabilities_runner.py +216 -0
- higpertext/capabilities/common/agent-builder.json +54 -0
- higpertext/capabilities/common/agent-sync.json +34 -0
- higpertext/capabilities/common/code-skeletonizer.json +35 -0
- higpertext/capabilities/common/commit-report.json +42 -0
- higpertext/capabilities/common/context-assembler.json +37 -0
- higpertext/capabilities/common/context-budget-report.json +15 -0
- higpertext/capabilities/common/dep-manager.json +43 -0
- higpertext/capabilities/common/docs-sync.json +14 -0
- higpertext/capabilities/common/doctor.json +18 -0
- higpertext/capabilities/common/efficiency-meter.json +31 -0
- higpertext/capabilities/common/env-catalog.json +13 -0
- higpertext/capabilities/common/env-clean.json +14 -0
- higpertext/capabilities/common/env-logs.json +16 -0
- higpertext/capabilities/common/env-runner.json +23 -0
- higpertext/capabilities/common/env-status.json +13 -0
- higpertext/capabilities/common/env-stop.json +14 -0
- higpertext/capabilities/common/env-template.json +14 -0
- higpertext/capabilities/common/error-context-locator.json +23 -0
- higpertext/capabilities/common/eval-agent.json +33 -0
- higpertext/capabilities/common/file-map.json +17 -0
- higpertext/capabilities/common/governance-exception.json +54 -0
- higpertext/capabilities/common/graph-query.json +59 -0
- higpertext/capabilities/common/graph-rebuild.json +31 -0
- higpertext/capabilities/common/graph-visualize.json +37 -0
- higpertext/capabilities/common/grep-search.json +176 -0
- higpertext/capabilities/common/higpertext-tester.json +25 -0
- higpertext/capabilities/common/hook-health.json +19 -0
- higpertext/capabilities/common/hook-sync-check.json +19 -0
- higpertext/capabilities/common/hooks-manager.json +55 -0
- higpertext/capabilities/common/knowledge-asker.json +27 -0
- higpertext/capabilities/common/list-rules.json +27 -0
- higpertext/capabilities/common/llm-invoke.json +59 -0
- higpertext/capabilities/common/load-rules.json +37 -0
- higpertext/capabilities/common/memory-manager.json +65 -0
- higpertext/capabilities/common/quality-scan.json +21 -0
- higpertext/capabilities/common/quality-updater.json +35 -0
- higpertext/capabilities/common/rag-index.json +17 -0
- higpertext/capabilities/common/report-viewer.json +24 -0
- higpertext/capabilities/common/roadmap-report.json +37 -0
- higpertext/capabilities/common/scripts/_env_cli.py +65 -0
- higpertext/capabilities/common/scripts/agent_builder.py +60 -0
- higpertext/capabilities/common/scripts/agent_sync.py +56 -0
- higpertext/capabilities/common/scripts/ask_higpertext.py +38 -0
- higpertext/capabilities/common/scripts/code_skeletonizer.py +225 -0
- higpertext/capabilities/common/scripts/commit_report.py +134 -0
- higpertext/capabilities/common/scripts/context_assembler.py +70 -0
- higpertext/capabilities/common/scripts/context_budget_report.py +53 -0
- higpertext/capabilities/common/scripts/dep_manager.py +81 -0
- higpertext/capabilities/common/scripts/docs_sync.py +981 -0
- higpertext/capabilities/common/scripts/doctor.py +144 -0
- higpertext/capabilities/common/scripts/efficiency_meter.py +83 -0
- higpertext/capabilities/common/scripts/env_catalog.py +47 -0
- higpertext/capabilities/common/scripts/env_clean.py +30 -0
- higpertext/capabilities/common/scripts/env_logs.py +32 -0
- higpertext/capabilities/common/scripts/env_runner.py +53 -0
- higpertext/capabilities/common/scripts/env_status.py +38 -0
- higpertext/capabilities/common/scripts/env_stop.py +30 -0
- higpertext/capabilities/common/scripts/env_template.py +73 -0
- higpertext/capabilities/common/scripts/error_context_locator.py +138 -0
- higpertext/capabilities/common/scripts/eval_agent.py +80 -0
- higpertext/capabilities/common/scripts/file_map.py +95 -0
- higpertext/capabilities/common/scripts/governance_exception.py +116 -0
- higpertext/capabilities/common/scripts/graph_query.py +104 -0
- higpertext/capabilities/common/scripts/graph_rebuild.py +107 -0
- higpertext/capabilities/common/scripts/graph_visualize.py +76 -0
- higpertext/capabilities/common/scripts/grep_search.py +648 -0
- higpertext/capabilities/common/scripts/higpertext_tester.py +102 -0
- higpertext/capabilities/common/scripts/hook_health.py +149 -0
- higpertext/capabilities/common/scripts/hook_sync_check.py +134 -0
- higpertext/capabilities/common/scripts/hooks_manager.py +171 -0
- higpertext/capabilities/common/scripts/list_rules.py +175 -0
- higpertext/capabilities/common/scripts/llm_invoke.py +135 -0
- higpertext/capabilities/common/scripts/load_rules.py +379 -0
- higpertext/capabilities/common/scripts/memory_manager.py +210 -0
- higpertext/capabilities/common/scripts/presentation_engine.py +63 -0
- higpertext/capabilities/common/scripts/quality_scan.py +132 -0
- higpertext/capabilities/common/scripts/rag_index.py +39 -0
- higpertext/capabilities/common/scripts/report_viewer.py +106 -0
- higpertext/capabilities/common/scripts/roadmap_report.py +73 -0
- higpertext/capabilities/common/scripts/search_router.py +111 -0
- higpertext/capabilities/common/scripts/semantic_diff.py +166 -0
- higpertext/capabilities/common/scripts/semantic_search.py +43 -0
- higpertext/capabilities/common/scripts/session_control.py +136 -0
- higpertext/capabilities/common/scripts/smart_read.py +232 -0
- higpertext/capabilities/common/scripts/subagent_executor.py +143 -0
- higpertext/capabilities/common/scripts/sync_agents.py +353 -0
- higpertext/capabilities/common/scripts/task_decomposer.py +78 -0
- higpertext/capabilities/common/scripts/telemetry_report.py +36 -0
- higpertext/capabilities/common/search-router.json +24 -0
- higpertext/capabilities/common/semantic-diff.json +40 -0
- higpertext/capabilities/common/semantic-search.json +19 -0
- higpertext/capabilities/common/session-clean.json +20 -0
- higpertext/capabilities/common/session-start.json +44 -0
- higpertext/capabilities/common/smart-read.json +28 -0
- higpertext/capabilities/common/subagent-executor.json +25 -0
- higpertext/capabilities/common/sync-agents.json +32 -0
- higpertext/capabilities/common/task-decomposer.json +37 -0
- higpertext/capabilities/common/telemetry-report.json +23 -0
- higpertext/capabilities/git/__init__.py +0 -0
- higpertext/capabilities/git/committer.json +61 -0
- higpertext/capabilities/git/diff.json +33 -0
- higpertext/capabilities/git/ls-files.json +44 -0
- higpertext/capabilities/git/rm.json +27 -0
- higpertext/capabilities/git/scripts/__init__.py +0 -0
- higpertext/capabilities/git/scripts/commit_changes.py +1077 -0
- higpertext/capabilities/git/scripts/git_diff.py +171 -0
- higpertext/capabilities/git/scripts/git_ls_files.py +376 -0
- higpertext/capabilities/git/scripts/git_rm.py +62 -0
- higpertext/capabilities/security/k8s-auditor.json +33 -0
- higpertext/capabilities/security/scripts/k8s_auditor.py +307 -0
- higpertext/capabilities/security/scripts/secret_scanner.py +235 -0
- higpertext/capabilities/security/secret-scanner.json +32 -0
- higpertext/hooks/__init__.py +28 -0
- higpertext/hooks/_compat.py +27 -0
- higpertext/hooks/hook_tasks/__init__.py +1 -0
- higpertext/hooks/hook_tasks/_rules/__init__.py +0 -0
- higpertext/hooks/hook_tasks/_rules/bash_rules.py +635 -0
- higpertext/hooks/hook_tasks/_rules/context_engine_rule.py +79 -0
- higpertext/hooks/hook_tasks/_rules/context_rules.py +199 -0
- higpertext/hooks/hook_tasks/_rules/governance_adapter.py +72 -0
- higpertext/hooks/hook_tasks/_rules/profile_rules.json +25 -0
- higpertext/hooks/hook_tasks/_rules/quality_rules.py +86 -0
- higpertext/hooks/hook_tasks/_rules/security_rules.py +214 -0
- higpertext/hooks/hook_tasks/_rules/session_rules.py +316 -0
- higpertext/hooks/hook_tasks/_rules/telemetry_rules.py +121 -0
- higpertext/hooks/hook_tasks/audit_logger_hook.py +28 -0
- higpertext/hooks/hook_tasks/hook_bash_guard.py +101 -0
- higpertext/hooks/hook_tasks/hook_code_quality.py +48 -0
- higpertext/hooks/hook_tasks/hook_context_hint.py +46 -0
- higpertext/hooks/hook_tasks/hook_context_manager.py +44 -0
- higpertext/hooks/hook_tasks/hook_io.py +122 -0
- higpertext/hooks/hook_tasks/hook_loop_guard.py +182 -0
- higpertext/hooks/hook_tasks/hook_post_observer.py +54 -0
- higpertext/hooks/hook_tasks/hook_read_guard.py +85 -0
- higpertext/hooks/hook_tasks/hook_security_guard.py +81 -0
- higpertext/hooks/hook_tasks/hook_session_prompt.py +83 -0
- higpertext/hooks/hook_tasks/hook_session_stop.py +115 -0
- higpertext/hooks/hook_tasks/hook_utils.py +144 -0
- higpertext/hooks/hook_tasks/session_guard_hook.py +23 -0
- higpertext/hooks/hook_tasks/telemetry_utils.py +176 -0
- higpertext/hooks/hook_tasks/test_echo_hook.py +33 -0
- higpertext/hooks/hook_tasks/webhook_hook.py +54 -0
- higpertext/hooks/hook_tasks/workflow_runner_hook.py +49 -0
- higpertext/hooks/hooks_catalog.json +116 -0
- higpertext/kernel/__init__.py +63 -0
- higpertext/kernel/_compat.py +138 -0
- higpertext/kernel/app_config.py +117 -0
- higpertext/kernel/application/__init__.py +13 -0
- higpertext/kernel/application/agent_registry.py +102 -0
- higpertext/kernel/application/capability_manager.py +61 -0
- higpertext/kernel/application/commit_reporter.py +247 -0
- higpertext/kernel/application/context_builder.py +166 -0
- higpertext/kernel/application/context_engine.py +409 -0
- higpertext/kernel/application/engine.py +41 -0
- higpertext/kernel/application/env_runtime.py +174 -0
- higpertext/kernel/application/environment_manager.py +154 -0
- higpertext/kernel/application/governance.py +192 -0
- higpertext/kernel/application/hook_registry.py +102 -0
- higpertext/kernel/application/hook_renderer.py +720 -0
- higpertext/kernel/application/ports.py +49 -0
- higpertext/kernel/application/profile_learner.py +358 -0
- higpertext/kernel/application/profile_service.py +205 -0
- higpertext/kernel/application/profile_services.py +6 -0
- higpertext/kernel/application/profile_use_cases.py +93 -0
- higpertext/kernel/application/rag_service.py +75 -0
- higpertext/kernel/application/roadmap_reporter.py +178 -0
- higpertext/kernel/application/semantic_engine.py +258 -0
- higpertext/kernel/application/session_services.py +33 -0
- higpertext/kernel/application/skill_hook_compiler.py +85 -0
- higpertext/kernel/application/telemetry.py +326 -0
- higpertext/kernel/application/workflow_manager.py +176 -0
- higpertext/kernel/config_paths.py +66 -0
- higpertext/kernel/domain/__init__.py +12 -0
- higpertext/kernel/domain/agent_registry.py +23 -0
- higpertext/kernel/domain/commit_reporter.py +155 -0
- higpertext/kernel/domain/compilers.py +7 -0
- higpertext/kernel/domain/context_engine.py +319 -0
- higpertext/kernel/domain/entities.py +51 -0
- higpertext/kernel/domain/env_runtime.py +62 -0
- higpertext/kernel/domain/governance.py +198 -0
- higpertext/kernel/domain/hook_models.py +29 -0
- higpertext/kernel/domain/profile_learner.py +186 -0
- higpertext/kernel/domain/rag.py +70 -0
- higpertext/kernel/domain/repositories.py +8 -0
- higpertext/kernel/domain/roadmap_reporter.py +80 -0
- higpertext/kernel/domain/semantic_engine.py +107 -0
- higpertext/kernel/engine.py +42 -0
- higpertext/kernel/htx_resolver.py +69 -0
- higpertext/kernel/infrastructure/__init__.py +13 -0
- higpertext/kernel/infrastructure/agent_registry.py +40 -0
- higpertext/kernel/infrastructure/cache/capability_cache.py +319 -0
- higpertext/kernel/infrastructure/capability_helper.py +40 -0
- higpertext/kernel/infrastructure/cli/__init__.py +1 -0
- higpertext/kernel/infrastructure/cli/agent_commands.py +62 -0
- higpertext/kernel/infrastructure/cli/arguments.py +39 -0
- higpertext/kernel/infrastructure/cli/capability_command_builder.py +86 -0
- higpertext/kernel/infrastructure/cli/capability_task_service.py +234 -0
- higpertext/kernel/infrastructure/cli/cli_search.py +234 -0
- higpertext/kernel/infrastructure/cli/parameter_contracts.py +83 -0
- higpertext/kernel/infrastructure/cli/parser_builder.py +122 -0
- higpertext/kernel/infrastructure/cli/profile_commands.py +89 -0
- higpertext/kernel/infrastructure/cli/roadmap_commands.py +117 -0
- higpertext/kernel/infrastructure/cli/router.py +1110 -0
- higpertext/kernel/infrastructure/cli/session_commands.py +36 -0
- higpertext/kernel/infrastructure/cli/task_commands.py +23 -0
- higpertext/kernel/infrastructure/cli/task_result_reporter.py +56 -0
- higpertext/kernel/infrastructure/cli/workflow_commands.py +25 -0
- higpertext/kernel/infrastructure/compilers/__init__.py +3 -0
- higpertext/kernel/infrastructure/compilers/factory.py +27 -0
- higpertext/kernel/infrastructure/compilers/graph_compiler.py +20 -0
- higpertext/kernel/infrastructure/compilers/guide_compiler.py +50 -0
- higpertext/kernel/infrastructure/compilers/hook_compiler.py +69 -0
- higpertext/kernel/infrastructure/compilers/playbook_compiler.py +154 -0
- higpertext/kernel/infrastructure/context_engine.py +303 -0
- higpertext/kernel/infrastructure/database/local_vector_store.py +99 -0
- higpertext/kernel/infrastructure/deployment/__init__.py +1 -0
- higpertext/kernel/infrastructure/deployment/resource_deployer.py +283 -0
- higpertext/kernel/infrastructure/diagnostics/__init__.py +1 -0
- higpertext/kernel/infrastructure/diagnostics/health.py +191 -0
- higpertext/kernel/infrastructure/env_runtime.py +227 -0
- higpertext/kernel/infrastructure/execution/__init__.py +1 -0
- higpertext/kernel/infrastructure/execution/parallel.py +188 -0
- higpertext/kernel/infrastructure/execution/resilience.py +155 -0
- higpertext/kernel/infrastructure/file_repositories.py +213 -0
- higpertext/kernel/infrastructure/governance.py +198 -0
- higpertext/kernel/infrastructure/hook_config_loader.py +53 -0
- higpertext/kernel/infrastructure/hook_webhook_dispatcher.py +61 -0
- higpertext/kernel/infrastructure/hook_workflow_bridge.py +60 -0
- higpertext/kernel/infrastructure/llm/__init__.py +6 -0
- higpertext/kernel/infrastructure/llm/provider.py +46 -0
- higpertext/kernel/infrastructure/llm/providers/__init__.py +0 -0
- higpertext/kernel/infrastructure/llm/providers/anthropic_provider.py +94 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_embeddings.py +74 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_provider.py +101 -0
- higpertext/kernel/infrastructure/llm/providers/ollama_provider.py +110 -0
- higpertext/kernel/infrastructure/llm/providers/openai_provider.py +98 -0
- higpertext/kernel/infrastructure/llm/registry.py +81 -0
- higpertext/kernel/infrastructure/logger.py +303 -0
- higpertext/kernel/infrastructure/output_store.py +70 -0
- higpertext/kernel/infrastructure/parser/__init__.py +1 -0
- higpertext/kernel/infrastructure/parser/code_chunker.py +144 -0
- higpertext/kernel/infrastructure/parser/language/__init__.py +14 -0
- higpertext/kernel/infrastructure/parser/language/base.py +41 -0
- higpertext/kernel/infrastructure/parser/language/powershell_parser.py +35 -0
- higpertext/kernel/infrastructure/parser/language/python_parser.py +98 -0
- higpertext/kernel/infrastructure/parser/language/typescript_parser.py +91 -0
- higpertext/kernel/infrastructure/parser/semantic_graph.py +409 -0
- higpertext/kernel/infrastructure/presentation/__init__.py +1 -0
- higpertext/kernel/infrastructure/presentation/html_renderer.py +137 -0
- higpertext/kernel/infrastructure/presentation/markdown_renderer.py +84 -0
- higpertext/kernel/infrastructure/presentation/markdown_report_renderer.py +97 -0
- higpertext/kernel/infrastructure/profile_store.py +28 -0
- higpertext/kernel/infrastructure/semantic_engine.py +289 -0
- higpertext/kernel/infrastructure/telemetry_reporter.py +132 -0
- higpertext/kernel/infrastructure/validation/__init__.py +1 -0
- higpertext/kernel/infrastructure/validation/contract_validator.py +163 -0
- higpertext/kernel/pkg_resources.py +38 -0
- higpertext/kernel/session_manager.py +319 -0
- higpertext/templates/env/generic-shell.yaml +21 -0
- higpertext/templates/env/node-vitest.yaml +27 -0
- higpertext/templates/env/python-pytest.yaml +29 -0
- higpertext/templates/html/commit_body.html +20 -0
- higpertext/templates/html/commit_diff.html +4 -0
- higpertext/templates/html/commit_index.html +29 -0
- higpertext/templates/html/commit_layer.html +11 -0
- higpertext/templates/html/commit_shell.html +28 -0
- higpertext/templates/html/graph_visualize.html +86 -0
- higpertext/templates/html/roadmap_body.html +12 -0
- higpertext/templates/html/roadmap_phase.html +5 -0
- higpertext/templates/html/roadmap_shell.html +29 -0
- higpertext/templates/markdown/commit_report.md +18 -0
- higpertext/templates/markdown/efficiency_report.md +12 -0
- higpertext/templates/markdown/roadmap_report.md +25 -0
- higpertext/templates/skills/best-practices.md +7 -0
- higpertext/templates/skills/clean-code.md +8 -0
- higpertext/templates/skills/ddd-standards.md +7 -0
- higpertext/templates/skills/tdd-practices.md +7 -0
- higpertext/templates/subagents/architect.md +7 -0
- higpertext/templates/subagents/test-engineer.md +7 -0
- higpertext/templates/workflows/build.json +23 -0
- higpertext/templates/workflows/compact.json +21 -0
- higpertext/templates/workflows/plan.json +59 -0
- higpertext/templates/workflows/review.json +26 -0
- higpertext/templates/workflows/spec.json +27 -0
- higpertext_cli-0.8.0.dist-info/METADATA +35 -0
- higpertext_cli-0.8.0.dist-info/RECORD +335 -0
- higpertext_cli-0.8.0.dist-info/WHEEL +5 -0
- higpertext_cli-0.8.0.dist-info/entry_points.txt +2 -0
- higpertext_cli-0.8.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Casos de uso de perfiles y contexto de agente."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import asdict
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from higpertext.kernel.app_config import GLOBAL_PROFILE_NAMES
|
|
9
|
+
from higpertext.kernel.application.ports import (
|
|
10
|
+
ICapabilityRepository,
|
|
11
|
+
IProfileRepository,
|
|
12
|
+
IWorkflowRepository,
|
|
13
|
+
)
|
|
14
|
+
from higpertext.kernel.application.profile_service import ProfileService
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GetAgentContextUseCase:
|
|
18
|
+
"""Construye el contexto activo para un perfil de agente."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
profiles_repo: IProfileRepository,
|
|
23
|
+
caps_repo: ICapabilityRepository,
|
|
24
|
+
workflows_repo: IWorkflowRepository,
|
|
25
|
+
base_dir: Path,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.profiles_repo = profiles_repo
|
|
28
|
+
self.caps_repo = caps_repo
|
|
29
|
+
self.workflows_repo = workflows_repo
|
|
30
|
+
self.base_dir = base_dir
|
|
31
|
+
|
|
32
|
+
def execute(self, profile_name: str, env_data: dict | None = None) -> dict:
|
|
33
|
+
service = ProfileService(self.profiles_repo, self.caps_repo, self.base_dir)
|
|
34
|
+
profile = service.resolve_hierarchy(profile_name)
|
|
35
|
+
cap_ids = self._capability_ids_with_global_defaults(service, profile_name, profile.capabilities)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
"profile": asdict(profile),
|
|
39
|
+
"active_capabilities": self._load_capabilities(cap_ids),
|
|
40
|
+
"active_workflows": self._load_workflows(profile_name),
|
|
41
|
+
"environment": env_data or {},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def _capability_ids_with_global_defaults(
|
|
45
|
+
self, service: ProfileService, profile_name: str, capabilities: list[str]
|
|
46
|
+
) -> list[str]:
|
|
47
|
+
cap_ids = list(capabilities)
|
|
48
|
+
if profile_name.lower() == "global":
|
|
49
|
+
return cap_ids
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
global_profile = service.resolve_hierarchy("global")
|
|
53
|
+
for cap_id in global_profile.capabilities:
|
|
54
|
+
if cap_id not in cap_ids:
|
|
55
|
+
cap_ids.append(cap_id)
|
|
56
|
+
except (FileNotFoundError, ValueError): # nosec B110
|
|
57
|
+
pass
|
|
58
|
+
return cap_ids
|
|
59
|
+
|
|
60
|
+
def _load_capabilities(self, cap_ids: list[str]) -> list[dict]:
|
|
61
|
+
loaded_caps = []
|
|
62
|
+
for cap_id in cap_ids:
|
|
63
|
+
try:
|
|
64
|
+
loaded_caps.append(asdict(self.caps_repo.load(cap_id)))
|
|
65
|
+
except FileNotFoundError: # nosec B110
|
|
66
|
+
pass
|
|
67
|
+
return loaded_caps
|
|
68
|
+
|
|
69
|
+
def _load_workflows(self, profile_name: str) -> list[dict]:
|
|
70
|
+
profile_lower = profile_name.lower()
|
|
71
|
+
return [
|
|
72
|
+
asdict(workflow)
|
|
73
|
+
for workflow in self.workflows_repo.list_all()
|
|
74
|
+
if workflow.required_profile.lower() in GLOBAL_PROFILE_NAMES | {profile_lower}
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ValidateProfileUseCase:
|
|
79
|
+
"""Valida un perfil y sus dependencias declaradas."""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
profiles_repo: IProfileRepository,
|
|
84
|
+
caps_repo: ICapabilityRepository,
|
|
85
|
+
base_dir: Path,
|
|
86
|
+
) -> None:
|
|
87
|
+
self.profiles_repo = profiles_repo
|
|
88
|
+
self.caps_repo = caps_repo
|
|
89
|
+
self.base_dir = base_dir
|
|
90
|
+
|
|
91
|
+
def execute(self, profile_name: str) -> tuple[bool, list[str], list[str]]:
|
|
92
|
+
service = ProfileService(self.profiles_repo, self.caps_repo, self.base_dir)
|
|
93
|
+
return service.validate_profile(profile_name)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""RAG Application Service - coordina la indexación y búsqueda semántica."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from higpertext.kernel.domain.rag import IEmbeddingProvider, IVectorStore, VectorEntry
|
|
6
|
+
from higpertext.kernel.infrastructure.parser.code_chunker import CodeChunker
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RAGService:
|
|
10
|
+
"""Caso de uso orquestador de indexación y queries semánticas de RAG."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, embedder: IEmbeddingProvider, store: IVectorStore) -> None:
|
|
13
|
+
self.embedder = embedder
|
|
14
|
+
self.store = store
|
|
15
|
+
|
|
16
|
+
def index_project(self, project_root: Path, file_globs: list[str] | None = None) -> int:
|
|
17
|
+
"""Divide e indexa todos los archivos del proyecto que coincidan con los globs."""
|
|
18
|
+
if file_globs is None:
|
|
19
|
+
file_globs = ["src/**/*.py", "src/**/*.md", "tests/**/*.py"]
|
|
20
|
+
|
|
21
|
+
self.store.clear()
|
|
22
|
+
all_chunks = []
|
|
23
|
+
|
|
24
|
+
# Encontrar y parsear archivos
|
|
25
|
+
for glob_pattern in file_globs:
|
|
26
|
+
for file_path in project_root.glob(glob_pattern):
|
|
27
|
+
# Ignorar archivos en __pycache__ y .venv
|
|
28
|
+
if "__pycache__" in file_path.parts or ".venv" in file_path.parts or ".git" in file_path.parts:
|
|
29
|
+
continue
|
|
30
|
+
if file_path.is_file():
|
|
31
|
+
chunks = CodeChunker.chunk_file(file_path)
|
|
32
|
+
all_chunks.extend(chunks)
|
|
33
|
+
|
|
34
|
+
if not all_chunks:
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
# Generar embeddings e indexar
|
|
38
|
+
entries: list[VectorEntry] = []
|
|
39
|
+
for chunk in all_chunks:
|
|
40
|
+
try:
|
|
41
|
+
embedding = self.embedder.get_embedding(chunk.content)
|
|
42
|
+
entries.append(VectorEntry(chunk=chunk, embedding=embedding))
|
|
43
|
+
except Exception as e:
|
|
44
|
+
# Silenciar errores individuales de chunks de API para no romper la indexación global
|
|
45
|
+
print(f"[RAG WARNING] Falló generación de embedding para chunk en {chunk.file}: {e}")
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
if entries:
|
|
49
|
+
self.store.add_entries(entries)
|
|
50
|
+
self.store.save()
|
|
51
|
+
|
|
52
|
+
return len(entries)
|
|
53
|
+
|
|
54
|
+
def search_context(self, query: str, limit: int = 5) -> list[dict]:
|
|
55
|
+
"""Busca fragmentos semánticamente cercanos y los devuelve formateados."""
|
|
56
|
+
self.store.load()
|
|
57
|
+
try:
|
|
58
|
+
query_vector = self.embedder.get_embedding(query)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
raise RuntimeError(f"Error al calcular embedding del query: {e}")
|
|
61
|
+
|
|
62
|
+
hits = self.store.search(query_vector, limit=limit)
|
|
63
|
+
|
|
64
|
+
serialized_hits = []
|
|
65
|
+
for chunk, score in hits:
|
|
66
|
+
serialized_hits.append({
|
|
67
|
+
"file": chunk.file,
|
|
68
|
+
"content": chunk.content,
|
|
69
|
+
"chunk_type": chunk.chunk_type,
|
|
70
|
+
"start_line": chunk.start_line,
|
|
71
|
+
"end_line": chunk.end_line,
|
|
72
|
+
"metadata": chunk.metadata,
|
|
73
|
+
"score": score
|
|
74
|
+
})
|
|
75
|
+
return serialized_hits
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from higpertext.kernel.domain.roadmap_reporter import Phase, PhaseStatus, Roadmap, RoadmapReport
|
|
7
|
+
from higpertext.kernel.infrastructure.presentation.html_renderer import HtmlReportRenderer
|
|
8
|
+
from higpertext.kernel.infrastructure.presentation.markdown_report_renderer import MarkdownReportRenderer
|
|
9
|
+
|
|
10
|
+
_STATUS_ICON = {
|
|
11
|
+
PhaseStatus.DONE: "✅",
|
|
12
|
+
PhaseStatus.ACTIVE: "🔄",
|
|
13
|
+
PhaseStatus.PENDING: "⏳",
|
|
14
|
+
}
|
|
15
|
+
_STATUS_COLOR = {
|
|
16
|
+
PhaseStatus.DONE: "#3fb950",
|
|
17
|
+
PhaseStatus.ACTIVE: "#e3b341",
|
|
18
|
+
PhaseStatus.PENDING: "#8b949e",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class RoadmapAnalysis:
|
|
24
|
+
summary: str
|
|
25
|
+
completion_pct: float
|
|
26
|
+
impact_tags: list[str] = field(default_factory=list)
|
|
27
|
+
phase_insights: list[str] = field(default_factory=list)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ProgressAnalyzer:
|
|
31
|
+
"""Analiza un RoadmapReport y produce un resumen explicativo."""
|
|
32
|
+
|
|
33
|
+
def analyze(self, report: RoadmapReport) -> RoadmapAnalysis:
|
|
34
|
+
pct = report.completion_pct
|
|
35
|
+
active = report.active_phase
|
|
36
|
+
summary = self._build_summary(report, pct, active)
|
|
37
|
+
tags = self._build_tags(report, pct)
|
|
38
|
+
insights = self._build_insights(report)
|
|
39
|
+
return RoadmapAnalysis(
|
|
40
|
+
summary=summary,
|
|
41
|
+
completion_pct=pct,
|
|
42
|
+
impact_tags=tags,
|
|
43
|
+
phase_insights=insights,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def _build_summary(self, report, pct, active) -> str:
|
|
47
|
+
name = report.roadmap.name
|
|
48
|
+
done = report.done_phases
|
|
49
|
+
total = report.total_phases
|
|
50
|
+
if pct == 100.0:
|
|
51
|
+
return f'Roadmap "{name}" completed: all {total} phases done.'
|
|
52
|
+
active_part = f' Currently working on: "{active.name}".' if active else ""
|
|
53
|
+
return f'Roadmap "{name}" is {pct:.0f}% complete ({done}/{total} phases done).{active_part}'
|
|
54
|
+
|
|
55
|
+
def _build_tags(self, report, pct) -> list[str]:
|
|
56
|
+
tags = []
|
|
57
|
+
if pct == 100.0:
|
|
58
|
+
tags.append("completed")
|
|
59
|
+
elif report.active_phase:
|
|
60
|
+
tags.append("in-progress")
|
|
61
|
+
else:
|
|
62
|
+
tags.append("not-started")
|
|
63
|
+
if report.all_skills_used:
|
|
64
|
+
tags.append("skills-tracked")
|
|
65
|
+
if report.all_subagents_used:
|
|
66
|
+
tags.append("subagents-used")
|
|
67
|
+
return tags
|
|
68
|
+
|
|
69
|
+
def _build_insights(self, report) -> list[str]:
|
|
70
|
+
insights = []
|
|
71
|
+
for phase in report.roadmap.phases:
|
|
72
|
+
icon = _STATUS_ICON[phase.status]
|
|
73
|
+
skills_part = f", skills: {', '.join(phase.skills)}" if phase.skills else ""
|
|
74
|
+
insights.append(f"{icon} [{phase.status.value}] {phase.name}{skills_part}")
|
|
75
|
+
return insights
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class RoadmapParser:
|
|
79
|
+
"""Deserializa roadmap JSON en objetos de dominio."""
|
|
80
|
+
|
|
81
|
+
def parse(self, raw: str) -> RoadmapReport:
|
|
82
|
+
try:
|
|
83
|
+
data = json.loads(raw)
|
|
84
|
+
except json.JSONDecodeError as exc:
|
|
85
|
+
raise ValueError(f"Invalid roadmap JSON: {exc}") from exc
|
|
86
|
+
return self._build_report(data)
|
|
87
|
+
|
|
88
|
+
def parse_file(self, path: Path) -> RoadmapReport:
|
|
89
|
+
return self.parse(path.read_text(encoding="utf-8"))
|
|
90
|
+
|
|
91
|
+
def _build_report(self, data: dict) -> RoadmapReport:
|
|
92
|
+
phases = [self._build_phase(p) for p in data.get("phases", [])]
|
|
93
|
+
roadmap = Roadmap(
|
|
94
|
+
id=data["id"],
|
|
95
|
+
name=data["name"],
|
|
96
|
+
description=data.get("description", ""),
|
|
97
|
+
phases=phases,
|
|
98
|
+
)
|
|
99
|
+
return RoadmapReport(roadmap=roadmap)
|
|
100
|
+
|
|
101
|
+
def _build_phase(self, data: dict) -> Phase:
|
|
102
|
+
return Phase(
|
|
103
|
+
id=data["id"],
|
|
104
|
+
name=data["name"],
|
|
105
|
+
description=data.get("description", ""),
|
|
106
|
+
status=PhaseStatus(data.get("status", "pending")),
|
|
107
|
+
skills=list(data.get("skills", [])),
|
|
108
|
+
subagents=list(data.get("subagents", [])),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class RoadmapReportBuilder:
|
|
113
|
+
"""Genera reportes Markdown y HTML para un RoadmapReport."""
|
|
114
|
+
|
|
115
|
+
def to_markdown(self, report: RoadmapReport, analysis: RoadmapAnalysis) -> str:
|
|
116
|
+
r = report.roadmap
|
|
117
|
+
renderer = MarkdownReportRenderer()
|
|
118
|
+
tags = " ".join(f"`{t}`" for t in analysis.impact_tags)
|
|
119
|
+
phases_rows = "\n".join(
|
|
120
|
+
f"| {i} | {phase.name} | {_STATUS_ICON[phase.status]} {phase.status.value}"
|
|
121
|
+
f" | {", ".join(phase.skills) or '—'} | {", ".join(phase.subagents) or '—'} |"
|
|
122
|
+
for i, phase in enumerate(r.phases, 1)
|
|
123
|
+
)
|
|
124
|
+
insights = "\n".join(f"- {ins}" for ins in analysis.phase_insights)
|
|
125
|
+
return renderer.render_roadmap_report(
|
|
126
|
+
name=r.name,
|
|
127
|
+
summary=analysis.summary,
|
|
128
|
+
completion_pct=f"{analysis.completion_pct:.0f}",
|
|
129
|
+
done_phases=report.done_phases,
|
|
130
|
+
total_phases=report.total_phases,
|
|
131
|
+
tags=tags,
|
|
132
|
+
phases_rows=phases_rows,
|
|
133
|
+
insights=insights,
|
|
134
|
+
skills_all=", ".join(report.all_skills_used) or "—",
|
|
135
|
+
subs_all=", ".join(report.all_subagents_used) or "—",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def to_html(self, report: RoadmapReport, analysis: RoadmapAnalysis) -> str:
|
|
139
|
+
renderer = HtmlReportRenderer()
|
|
140
|
+
r = report.roadmap
|
|
141
|
+
pct = f"{analysis.completion_pct:.0f}"
|
|
142
|
+
tags_html = " ".join(
|
|
143
|
+
f'<span class="tag">{t}</span>' for t in analysis.impact_tags
|
|
144
|
+
)
|
|
145
|
+
phases_html = "\n".join(
|
|
146
|
+
renderer.render_roadmap_phase(
|
|
147
|
+
css=f"phase-{p.status.value}",
|
|
148
|
+
icon=_STATUS_ICON[p.status],
|
|
149
|
+
name=p.name,
|
|
150
|
+
status=p.status.value,
|
|
151
|
+
description=p.description or "",
|
|
152
|
+
skills_html=" ".join(
|
|
153
|
+
f'<span class="skill-tag">{s}</span>' for s in p.skills
|
|
154
|
+
),
|
|
155
|
+
subs_html=" ".join(
|
|
156
|
+
f'<span class="sub-tag">{a}</span>' for a in p.subagents
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
for p in r.phases
|
|
160
|
+
)
|
|
161
|
+
skills_html = " ".join(
|
|
162
|
+
f'<span class="skill-tag">{s}</span>' for s in report.all_skills_used
|
|
163
|
+
) or "—"
|
|
164
|
+
subs_html = " ".join(
|
|
165
|
+
f'<span class="sub-tag">{a}</span>' for a in report.all_subagents_used
|
|
166
|
+
) or "—"
|
|
167
|
+
body = renderer.render_roadmap_body(
|
|
168
|
+
name=r.name,
|
|
169
|
+
done_phases=report.done_phases,
|
|
170
|
+
total_phases=report.total_phases,
|
|
171
|
+
summary=analysis.summary,
|
|
172
|
+
tags_html=tags_html,
|
|
173
|
+
pct=pct,
|
|
174
|
+
phases_html=phases_html,
|
|
175
|
+
skills_html=skills_html,
|
|
176
|
+
subs_html=subs_html,
|
|
177
|
+
)
|
|
178
|
+
return renderer.render_roadmap_shell(title=r.name, body=body)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Semantic Engine Application layer — ClusterService, ContextRanker, and GraphBuilder."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
from collections import defaultdict, deque
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from higpertext.kernel.domain.semantic_engine import Cluster, SemanticGraph, Symbol, Relation
|
|
8
|
+
from higpertext.kernel.config_paths import PROJECT_ROOT
|
|
9
|
+
|
|
10
|
+
_SG_CONFIG_PATH = PROJECT_ROOT / "src" / "config" / "semantic_graph.json"
|
|
11
|
+
_SG: dict = json.loads(_SG_CONFIG_PATH.read_text(encoding="utf-8"))
|
|
12
|
+
_SCORES: dict = _SG["ranking"]["scores"]
|
|
13
|
+
_BASE_TOKENS_PER_SYMBOL: int = _SG["ranking"]["base_tokens_per_symbol"]
|
|
14
|
+
_IGNORE_FOLDERS: frozenset[str] = frozenset(_SG["ignore_folders"])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClusterService:
|
|
18
|
+
"""Detecta comunidades usando Louvain phase-1 (modularity gain greedy)."""
|
|
19
|
+
|
|
20
|
+
def detect(self, graph: SemanticGraph, max_passes: int = 20) -> list[Cluster]:
|
|
21
|
+
symbols = graph.all_symbols()
|
|
22
|
+
if not symbols:
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
fqns = [s.fqn for s in symbols]
|
|
26
|
+
sym_by_fqn: dict[str, Symbol] = {s.fqn: s for s in symbols}
|
|
27
|
+
|
|
28
|
+
# Adyacencia ponderada (peso = número de aristas entre par)
|
|
29
|
+
adj: dict[str, dict[str, float]] = defaultdict(lambda: defaultdict(float))
|
|
30
|
+
total_weight = 0.0
|
|
31
|
+
for rel in graph.all_relations():
|
|
32
|
+
adj[rel.source.fqn][rel.target.fqn] += 1.0
|
|
33
|
+
adj[rel.target.fqn][rel.source.fqn] += 1.0
|
|
34
|
+
total_weight += 1.0
|
|
35
|
+
m = total_weight or 1.0
|
|
36
|
+
|
|
37
|
+
# Grado de cada nodo
|
|
38
|
+
degree: dict[str, float] = {fqn: sum(adj[fqn].values()) for fqn in fqns}
|
|
39
|
+
|
|
40
|
+
# Comunidad inicial: cada nodo es su propia comunidad
|
|
41
|
+
community: dict[str, int] = {fqn: i for i, fqn in enumerate(fqns)}
|
|
42
|
+
|
|
43
|
+
def _modularity_gain(fqn: str, target_comm: int) -> float:
|
|
44
|
+
k_i = degree[fqn]
|
|
45
|
+
k_i_in = sum(w for nb, w in adj[fqn].items() if community[nb] == target_comm)
|
|
46
|
+
sigma_tot = sum(degree[f] for f in fqns if community[f] == target_comm)
|
|
47
|
+
return (k_i_in / m) - (sigma_tot * k_i) / (2 * m * m)
|
|
48
|
+
|
|
49
|
+
for _ in range(max_passes):
|
|
50
|
+
moved = False
|
|
51
|
+
for fqn in fqns:
|
|
52
|
+
current = community[fqn]
|
|
53
|
+
best_comm = current
|
|
54
|
+
best_gain = 0.0
|
|
55
|
+
|
|
56
|
+
neighbors_comms = {community[nb] for nb in adj[fqn]}
|
|
57
|
+
neighbors_comms.discard(current)
|
|
58
|
+
|
|
59
|
+
for candidate in neighbors_comms:
|
|
60
|
+
gain = _modularity_gain(fqn, candidate) - _modularity_gain(fqn, current)
|
|
61
|
+
if gain > best_gain:
|
|
62
|
+
best_gain = gain
|
|
63
|
+
best_comm = candidate
|
|
64
|
+
|
|
65
|
+
if best_comm != current:
|
|
66
|
+
community[fqn] = best_comm
|
|
67
|
+
moved = True
|
|
68
|
+
|
|
69
|
+
if not moved:
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
# Renumera comunidades de forma compacta
|
|
73
|
+
label_map: dict[int, int] = {}
|
|
74
|
+
counter = 0
|
|
75
|
+
for old in community.values():
|
|
76
|
+
if old not in label_map:
|
|
77
|
+
label_map[old] = counter
|
|
78
|
+
counter += 1
|
|
79
|
+
|
|
80
|
+
groups: dict[int, list[Symbol]] = defaultdict(list)
|
|
81
|
+
for fqn, comm in community.items():
|
|
82
|
+
groups[label_map[comm]].append(sym_by_fqn[fqn])
|
|
83
|
+
|
|
84
|
+
return [
|
|
85
|
+
Cluster(id=f"cluster-{cid}", members=members) for cid, members in sorted(groups.items())
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ContextRanker:
|
|
90
|
+
"""Rankea símbolos del grafo por relevancia a un conjunto de keywords."""
|
|
91
|
+
|
|
92
|
+
def __init__(self, graph: SemanticGraph) -> None:
|
|
93
|
+
self._graph = graph
|
|
94
|
+
self._in_degree = self._build_in_degree()
|
|
95
|
+
|
|
96
|
+
def _build_in_degree(self) -> dict[str, int]:
|
|
97
|
+
degree: dict[str, int] = {}
|
|
98
|
+
for rel in self._graph.all_relations():
|
|
99
|
+
degree[rel.target.fqn] = degree.get(rel.target.fqn, 0) + 1
|
|
100
|
+
return degree
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _symbol_text(symbol: Symbol) -> str:
|
|
104
|
+
return " ".join((symbol.name, symbol.fqn, symbol.file, symbol.type.value)).lower()
|
|
105
|
+
|
|
106
|
+
def _match_score(self, symbol: Symbol, keywords: list[str]) -> int:
|
|
107
|
+
score = 0
|
|
108
|
+
name = symbol.name.lower()
|
|
109
|
+
file = symbol.file.lower()
|
|
110
|
+
fqn = symbol.fqn.lower()
|
|
111
|
+
symbol_type = symbol.type.value.lower()
|
|
112
|
+
for kw in keywords:
|
|
113
|
+
structural_query = any(mark in kw for mark in ("/", ".", "::"))
|
|
114
|
+
if kw == name:
|
|
115
|
+
score += _SCORES["exact_name"]
|
|
116
|
+
elif name.startswith(kw):
|
|
117
|
+
score += _SCORES["name_starts_with"]
|
|
118
|
+
elif kw in name:
|
|
119
|
+
score += _SCORES["name_contains"]
|
|
120
|
+
if structural_query and kw in fqn:
|
|
121
|
+
score += _SCORES["structural_fqn"]
|
|
122
|
+
if structural_query and kw in file:
|
|
123
|
+
score += _SCORES["structural_file"]
|
|
124
|
+
if kw == symbol_type:
|
|
125
|
+
score += _SCORES["type_match"]
|
|
126
|
+
return score
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def _token_cost(symbol: Symbol) -> int:
|
|
130
|
+
return _BASE_TOKENS_PER_SYMBOL + max(1, (len(symbol.fqn) + len(symbol.type.value)) // 4)
|
|
131
|
+
|
|
132
|
+
def rank(
|
|
133
|
+
self,
|
|
134
|
+
keywords: list[str],
|
|
135
|
+
budget: int,
|
|
136
|
+
depth: int = 2,
|
|
137
|
+
) -> list[Symbol]:
|
|
138
|
+
if not keywords or budget <= 0:
|
|
139
|
+
return []
|
|
140
|
+
|
|
141
|
+
lower_kws = [kw.lower() for kw in keywords]
|
|
142
|
+
all_symbols = self._graph.all_symbols()
|
|
143
|
+
|
|
144
|
+
seeds = [s for s in all_symbols if self._match_score(s, lower_kws) > 0]
|
|
145
|
+
if not seeds:
|
|
146
|
+
return []
|
|
147
|
+
|
|
148
|
+
seeds.sort(
|
|
149
|
+
key=lambda s: (
|
|
150
|
+
-self._match_score(s, lower_kws),
|
|
151
|
+
-self._in_degree.get(s.fqn, 0),
|
|
152
|
+
s.file,
|
|
153
|
+
s.name,
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
visited: dict[str, Symbol] = {}
|
|
158
|
+
queue: deque[tuple[Symbol, int]] = deque()
|
|
159
|
+
|
|
160
|
+
for seed in seeds:
|
|
161
|
+
if seed.fqn not in visited:
|
|
162
|
+
visited[seed.fqn] = seed
|
|
163
|
+
queue.append((seed, 0))
|
|
164
|
+
|
|
165
|
+
while queue:
|
|
166
|
+
sym, current_depth = queue.popleft()
|
|
167
|
+
if current_depth >= depth:
|
|
168
|
+
continue
|
|
169
|
+
for neighbor in self._graph.neighbors(sym):
|
|
170
|
+
if neighbor.fqn not in visited:
|
|
171
|
+
visited[neighbor.fqn] = neighbor
|
|
172
|
+
queue.append((neighbor, current_depth + 1))
|
|
173
|
+
|
|
174
|
+
seed_fqns = {s.fqn for s in seeds}
|
|
175
|
+
ordered = sorted(
|
|
176
|
+
visited.values(),
|
|
177
|
+
key=lambda s: (
|
|
178
|
+
0 if s.fqn in seed_fqns else 1,
|
|
179
|
+
-self._match_score(s, lower_kws),
|
|
180
|
+
-self._in_degree.get(s.fqn, 0),
|
|
181
|
+
s.file,
|
|
182
|
+
s.name,
|
|
183
|
+
),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
result: list[Symbol] = []
|
|
187
|
+
tokens_used = 0
|
|
188
|
+
for sym in ordered:
|
|
189
|
+
cost = self._token_cost(sym)
|
|
190
|
+
if tokens_used + cost > budget:
|
|
191
|
+
break
|
|
192
|
+
result.append(sym)
|
|
193
|
+
tokens_used += cost
|
|
194
|
+
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class GraphBuilder:
|
|
199
|
+
"""Orquesta parsers multi-lenguaje y construye el SemanticGraph con caché incremental."""
|
|
200
|
+
|
|
201
|
+
def build(self, root: Path, incremental: bool = True) -> SemanticGraph:
|
|
202
|
+
return self.build_with_stats(root, incremental)["graph"]
|
|
203
|
+
|
|
204
|
+
def build_with_stats(self, root: Path, incremental: bool = True) -> dict:
|
|
205
|
+
from higpertext.kernel.infrastructure.semantic_engine import (
|
|
206
|
+
FileCache,
|
|
207
|
+
PythonParser,
|
|
208
|
+
TypeScriptParser,
|
|
209
|
+
LanguageParser,
|
|
210
|
+
)
|
|
211
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
212
|
+
|
|
213
|
+
_ignore = _IGNORE_FOLDERS | {WORKSPACE_DIR_NAME}
|
|
214
|
+
|
|
215
|
+
parsers: dict[str, LanguageParser] = {
|
|
216
|
+
ext: _PARSER_CLASS()
|
|
217
|
+
for ext, _PARSER_CLASS in (
|
|
218
|
+
(".py", PythonParser),
|
|
219
|
+
(".ts", TypeScriptParser),
|
|
220
|
+
(".tsx", TypeScriptParser),
|
|
221
|
+
(".js", TypeScriptParser),
|
|
222
|
+
(".jsx", TypeScriptParser),
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
cache = FileCache(root)
|
|
227
|
+
cached_hashes = cache.load() if incremental else {}
|
|
228
|
+
new_hashes: dict[str, str] = dict(cached_hashes)
|
|
229
|
+
graph = SemanticGraph()
|
|
230
|
+
parsed = 0
|
|
231
|
+
skipped = 0
|
|
232
|
+
|
|
233
|
+
# Iterar archivos
|
|
234
|
+
for file_path in root.rglob("*"):
|
|
235
|
+
if file_path.suffix not in parsers:
|
|
236
|
+
continue
|
|
237
|
+
if any(part in _ignore for part in file_path.parts):
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
rel = file_path.relative_to(root).as_posix()
|
|
241
|
+
cached = cached_hashes.get(rel)
|
|
242
|
+
if incremental and not cache.is_changed(rel, file_path, cached):
|
|
243
|
+
skipped += 1
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
parser = parsers[file_path.suffix]
|
|
247
|
+
source = file_path.read_text(encoding="utf-8", errors="ignore")
|
|
248
|
+
symbols, relations = parser.parse(source=source, file=rel)
|
|
249
|
+
for sym in symbols:
|
|
250
|
+
graph.add_symbol(sym)
|
|
251
|
+
for rel_obj in relations:
|
|
252
|
+
graph.add_relation(rel_obj)
|
|
253
|
+
new_hashes[rel] = cache.compute(file_path)
|
|
254
|
+
parsed += 1
|
|
255
|
+
|
|
256
|
+
if incremental:
|
|
257
|
+
cache.save(new_hashes)
|
|
258
|
+
return {"graph": graph, "parsed": parsed, "skipped": skipped}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Lógica pura de aplicación para gestión de ciclo de vida de Sesión."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import datetime
|
|
5
|
+
import secrets
|
|
6
|
+
from higpertext.kernel.domain.entities import Session
|
|
7
|
+
from higpertext.kernel.application.ports import ISessionRepository, IProfileRepository
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SessionApplicationService:
|
|
11
|
+
def __init__(self, session_repo: ISessionRepository, profile_repo: IProfileRepository):
|
|
12
|
+
self.session_repo = session_repo
|
|
13
|
+
self.profile_repo = profile_repo
|
|
14
|
+
|
|
15
|
+
def create_session(
|
|
16
|
+
self, profile_name: str, assistant: str, skills: list[str], subagents: list[str]
|
|
17
|
+
) -> Session:
|
|
18
|
+
session_id = f"sess_{int(datetime.datetime.now().timestamp())}_{secrets.token_hex(2)}"
|
|
19
|
+
new_session = Session(
|
|
20
|
+
session_id=session_id,
|
|
21
|
+
profile=profile_name,
|
|
22
|
+
assistant=assistant,
|
|
23
|
+
status="active",
|
|
24
|
+
created_at=datetime.datetime.now().isoformat(),
|
|
25
|
+
active_skills=skills,
|
|
26
|
+
active_subagents=subagents,
|
|
27
|
+
tasks=[],
|
|
28
|
+
)
|
|
29
|
+
self.session_repo.save(new_session)
|
|
30
|
+
return new_session
|
|
31
|
+
|
|
32
|
+
def clean_session(self) -> None:
|
|
33
|
+
self.session_repo.delete()
|