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,42 @@
|
|
|
1
|
+
"""higpertext Engine — orquestador central que ensambla perfiles, capacidades y workflows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from higpertext.kernel.application.engine import ApplicationHigpertextEngine
|
|
6
|
+
from higpertext.kernel.pkg_resources import resolve_resource
|
|
7
|
+
from higpertext.kernel.application.profile_services import ProfileManager
|
|
8
|
+
from higpertext.kernel.application.capability_manager import CapabilityManager
|
|
9
|
+
from higpertext.kernel.application.workflow_manager import WorkflowManager
|
|
10
|
+
|
|
11
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
12
|
+
_log = get_logger()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HigpertextEngine:
|
|
16
|
+
"""Orquestador central del higpertext Engine v5.0 (Fachada de compatibilidad)."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, base_dir: Path) -> None:
|
|
19
|
+
self.base_dir = base_dir
|
|
20
|
+
self._app_engine = ApplicationHigpertextEngine(base_dir)
|
|
21
|
+
|
|
22
|
+
# Preserva atributos públicos requeridos por otras dependencias del kernel
|
|
23
|
+
# e indirectamente tests
|
|
24
|
+
self.profiles = ProfileManager(resolve_resource(base_dir, "src", "config", "profiles"))
|
|
25
|
+
self.capabilities = CapabilityManager(
|
|
26
|
+
resolve_resource(base_dir, "src", "higpertext", "capabilities")
|
|
27
|
+
)
|
|
28
|
+
self.workflows = WorkflowManager(resolve_resource(base_dir, "src", "config", "workflows"))
|
|
29
|
+
|
|
30
|
+
def get_agent_context(self, profile_name: str, env_data: dict | None = None) -> dict:
|
|
31
|
+
"""Construye el contexto completo resolviendo la jerarquía de perfiles."""
|
|
32
|
+
return self._app_engine.get_agent_context(profile_name, env_data)
|
|
33
|
+
|
|
34
|
+
def validate_profile(self, profile_name: str) -> tuple[bool, list[str], list[str]]:
|
|
35
|
+
"""Valida un perfil e integridad de sus dependencias."""
|
|
36
|
+
return self._app_engine.validate_profile(profile_name)
|
|
37
|
+
|
|
38
|
+
def get_all_capabilities(self) -> list:
|
|
39
|
+
return self.capabilities.get_all_capabilities_data()
|
|
40
|
+
|
|
41
|
+
def get_all_workflows(self) -> list:
|
|
42
|
+
return self.workflows.get_all_workflows_data()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Single source of truth para resolver el ejecutable htx."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
from functools import lru_cache
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@lru_cache(maxsize=1)
|
|
14
|
+
def _load_config() -> dict:
|
|
15
|
+
possible_paths = [
|
|
16
|
+
Path(__file__).resolve().parents[3] / "src" / "config" / "htx_config.json",
|
|
17
|
+
Path(__file__).resolve().parents[4] / "src" / "config" / "htx_config.json",
|
|
18
|
+
Path("/home/aomerge/Documentos/Proyects/agents/LLM-agent/src/config/htx_config.json"),
|
|
19
|
+
]
|
|
20
|
+
for config_path in possible_paths:
|
|
21
|
+
if config_path.exists():
|
|
22
|
+
try:
|
|
23
|
+
return json.loads(config_path.read_text(encoding="utf-8")).get("htx", {})
|
|
24
|
+
except (OSError, json.JSONDecodeError):
|
|
25
|
+
continue
|
|
26
|
+
return {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _config() -> dict:
|
|
30
|
+
return _load_config()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _cli_name() -> str:
|
|
34
|
+
"""Nombre del comando CLI para usar en textos/prompts (leído desde config)."""
|
|
35
|
+
return _config().get("cli_name", "htx")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
HTX_CMD = f"{_cli_name()} task"
|
|
39
|
+
HTX_WORKFLOW_CMD = f"{_cli_name()} workflow run"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_htx_cmd(project_root: Path) -> list[str]:
|
|
43
|
+
"""Resuelve el ejecutable htx en orden de prioridad.
|
|
44
|
+
|
|
45
|
+
1. Variable de entorno HIGPERTEXT_HTX_BIN (override explícito)
|
|
46
|
+
2. .venv/bin/htx del project_root
|
|
47
|
+
3. htx en PATH del sistema
|
|
48
|
+
4. python htx.py (fallback legacy)
|
|
49
|
+
"""
|
|
50
|
+
cfg = _config()
|
|
51
|
+
|
|
52
|
+
env_var = cfg.get("env_override", "HIGPERTEXT_HTX_BIN")
|
|
53
|
+
if override := os.environ.get(env_var):
|
|
54
|
+
return [override]
|
|
55
|
+
|
|
56
|
+
is_windows = platform.system() == "Windows"
|
|
57
|
+
venv_key = "venv_bin_windows" if is_windows else "venv_bin"
|
|
58
|
+
venv_rel = cfg.get(venv_key, ".venv/bin/htx")
|
|
59
|
+
venv_htx = project_root / venv_rel
|
|
60
|
+
if venv_htx.exists():
|
|
61
|
+
return [str(venv_htx)]
|
|
62
|
+
|
|
63
|
+
if system_htx := shutil.which("htx"):
|
|
64
|
+
return [system_htx]
|
|
65
|
+
|
|
66
|
+
fallback = cfg.get("fallback_script", "htx.py")
|
|
67
|
+
venv_python = project_root / ".venv" / ("Scripts/python.exe" if is_windows else "bin/python")
|
|
68
|
+
python = str(venv_python) if venv_python.exists() else "python"
|
|
69
|
+
return [python, str(project_root / fallback)]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Infrastructure layer init
|
|
2
|
+
from higpertext.kernel.infrastructure.governance import ContractLoader, AuditLog
|
|
3
|
+
from higpertext.kernel.infrastructure.agent_registry import JsonAgentRegistry
|
|
4
|
+
from higpertext.kernel.infrastructure.env_runtime import RunStore, ComposeRenderer, TemplateLoader, ComposeBackend, resolve_backend
|
|
5
|
+
from higpertext.kernel.infrastructure.output_store import OutputStore
|
|
6
|
+
from higpertext.kernel.infrastructure.profile_store import ProfileStore
|
|
7
|
+
from higpertext.kernel.infrastructure.telemetry_reporter import render as render_telemetry
|
|
8
|
+
from higpertext.kernel.infrastructure.context_engine import GraphIndex, PackStore, should_skeletonize, extract_skeleton, TelemetryReader
|
|
9
|
+
from higpertext.kernel.infrastructure.semantic_engine import FileCache, GraphRebuildHook, GraphStore, LanguageParser, PythonParser, TypeScriptParser
|
|
10
|
+
from higpertext.kernel.infrastructure.logger import get_logger, HtxLogger, reset_logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
6
|
+
from higpertext.kernel.domain.agent_registry import AgentRecord
|
|
7
|
+
|
|
8
|
+
_REGISTRY_PATH = Path(WORKSPACE_DIR_NAME) / "config" / "agents_registry.json"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class JsonAgentRegistry:
|
|
12
|
+
def __init__(self, engine_root: Path) -> None:
|
|
13
|
+
self._path = engine_root / _REGISTRY_PATH
|
|
14
|
+
|
|
15
|
+
def load(self) -> list[AgentRecord]:
|
|
16
|
+
if not self._path.exists():
|
|
17
|
+
return []
|
|
18
|
+
try:
|
|
19
|
+
data = json.loads(self._path.read_text(encoding="utf-8"))
|
|
20
|
+
return [AgentRecord(**a) for a in data.get("agents", [])]
|
|
21
|
+
except (OSError, json.JSONDecodeError, TypeError):
|
|
22
|
+
return []
|
|
23
|
+
|
|
24
|
+
def save(self, agents: list[AgentRecord]) -> None:
|
|
25
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
payload = {
|
|
27
|
+
"agents": [
|
|
28
|
+
{
|
|
29
|
+
"name": a.name,
|
|
30
|
+
"path": a.path,
|
|
31
|
+
"profile": a.profile,
|
|
32
|
+
"registered_at": a.registered_at,
|
|
33
|
+
}
|
|
34
|
+
for a in agents
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
self._path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
def find_by_name(self, name: str) -> AgentRecord | None:
|
|
40
|
+
return next((a for a in self.load() if a.name == name), None)
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""higpertext Capability Cache - cache de resultados basado en hash de inputs + TTL (Infraestructura).""" # noqa: E501
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
# Tipos CompletedProcess; no ejecuta shell aquí.
|
|
10
|
+
import subprocess # nosec B404
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from higpertext.kernel.app_config import (
|
|
16
|
+
CACHE_DIR as _CACHE_DIR_NAME,
|
|
17
|
+
CACHE_FILE_GLOB as _CACHE_FILE_GLOB,
|
|
18
|
+
NO_CACHE_CAPABILITIES as _NO_CACHE_CAPABILITIES,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_DEFAULT_TTL = 300 # 5 minutos
|
|
22
|
+
_DEFAULT_CACHE_DIR = f"{WORKSPACE_DIR_NAME}/{_CACHE_DIR_NAME}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class CacheEntry:
|
|
27
|
+
stdout: str
|
|
28
|
+
stderr: str
|
|
29
|
+
returncode: int
|
|
30
|
+
created_at: float
|
|
31
|
+
ttl: float
|
|
32
|
+
|
|
33
|
+
def is_expired(self) -> bool:
|
|
34
|
+
return time.monotonic() - self.created_at > self.ttl
|
|
35
|
+
|
|
36
|
+
def to_dict(self) -> dict:
|
|
37
|
+
return {
|
|
38
|
+
"stdout": self.stdout,
|
|
39
|
+
"stderr": self.stderr,
|
|
40
|
+
"returncode": self.returncode,
|
|
41
|
+
"created_at": self.created_at,
|
|
42
|
+
"ttl": self.ttl,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def from_dict(cls, data: dict) -> "CacheEntry":
|
|
47
|
+
return cls(
|
|
48
|
+
stdout=data["stdout"],
|
|
49
|
+
stderr=data["stderr"],
|
|
50
|
+
returncode=data["returncode"],
|
|
51
|
+
created_at=data["created_at"],
|
|
52
|
+
ttl=data["ttl"],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CapabilityCache:
|
|
57
|
+
"""
|
|
58
|
+
Cache de resultados de capabilities con invalidación por TTL y hash de inputs.
|
|
59
|
+
|
|
60
|
+
La clave de cache combina:
|
|
61
|
+
- capability_id
|
|
62
|
+
- parámetros de invocación (ordenados y serializados)
|
|
63
|
+
- hash del archivo entrypoint (detecta cambios en el script)
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
cache_dir: Path | None = None,
|
|
69
|
+
default_ttl: float = _DEFAULT_TTL,
|
|
70
|
+
) -> None:
|
|
71
|
+
self._cache_dir = cache_dir or (Path(os.getcwd()) / _DEFAULT_CACHE_DIR)
|
|
72
|
+
self._default_ttl = default_ttl
|
|
73
|
+
self._memory: dict[str, CacheEntry] = {}
|
|
74
|
+
# Índice inverso: capability_id → set de claves de cache
|
|
75
|
+
self._cap_index: dict[str, set[str]] = {}
|
|
76
|
+
|
|
77
|
+
def _ensure_dir(self) -> None:
|
|
78
|
+
self._cache_dir.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
|
|
80
|
+
def _cache_key(self, capability_id: str, params: dict, entrypoint: str = "") -> str:
|
|
81
|
+
payload = {
|
|
82
|
+
"id": capability_id,
|
|
83
|
+
"params": dict(sorted(params.items())),
|
|
84
|
+
"entrypoint_hash": self._hash_entrypoint(entrypoint),
|
|
85
|
+
"workspace_fingerprint": self._workspace_fingerprint(params),
|
|
86
|
+
}
|
|
87
|
+
raw = json.dumps(payload, sort_keys=True, ensure_ascii=False)
|
|
88
|
+
return hashlib.sha256(raw.encode()).hexdigest()[:16]
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def _hash_entrypoint(entrypoint: str) -> str:
|
|
92
|
+
"""Hash del script para invalidar cache cuando el script cambia."""
|
|
93
|
+
if not entrypoint:
|
|
94
|
+
return ""
|
|
95
|
+
try:
|
|
96
|
+
content = Path(entrypoint).read_bytes()
|
|
97
|
+
return hashlib.md5(content).hexdigest()[:8] # nosec B324
|
|
98
|
+
except OSError:
|
|
99
|
+
return ""
|
|
100
|
+
|
|
101
|
+
def _cache_file(self, key: str) -> Path:
|
|
102
|
+
return self._cache_dir / f"{key}.json"
|
|
103
|
+
|
|
104
|
+
def _workspace_fingerprint(self, params: dict) -> str:
|
|
105
|
+
"""Fingerprint barato del workspace y rutas de entrada relevantes."""
|
|
106
|
+
parts = [self._git_fingerprint(), self._params_paths_fingerprint(params)]
|
|
107
|
+
raw = "|".join(part for part in parts if part)
|
|
108
|
+
return hashlib.sha256(raw.encode()).hexdigest()[:16] if raw else ""
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _git_fingerprint() -> str:
|
|
112
|
+
try:
|
|
113
|
+
head = subprocess.run(
|
|
114
|
+
["git", "rev-parse", "HEAD"],
|
|
115
|
+
capture_output=True,
|
|
116
|
+
text=True,
|
|
117
|
+
timeout=2,
|
|
118
|
+
check=False,
|
|
119
|
+
).stdout.strip()
|
|
120
|
+
status = subprocess.run(
|
|
121
|
+
["git", "status", "--porcelain=v1"],
|
|
122
|
+
capture_output=True,
|
|
123
|
+
text=True,
|
|
124
|
+
timeout=3,
|
|
125
|
+
check=False,
|
|
126
|
+
).stdout.strip()
|
|
127
|
+
return f"{head}\n{status}"
|
|
128
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
129
|
+
return ""
|
|
130
|
+
|
|
131
|
+
def _params_paths_fingerprint(self, params: dict) -> str:
|
|
132
|
+
path_keys = {
|
|
133
|
+
"path",
|
|
134
|
+
"tests_path",
|
|
135
|
+
"source_path",
|
|
136
|
+
"file",
|
|
137
|
+
"files",
|
|
138
|
+
"target",
|
|
139
|
+
"target_dir",
|
|
140
|
+
}
|
|
141
|
+
chunks: list[str] = []
|
|
142
|
+
for key, value in sorted(params.items()):
|
|
143
|
+
if key not in path_keys or not value:
|
|
144
|
+
continue
|
|
145
|
+
for item in str(value).replace(",", " ").split():
|
|
146
|
+
chunks.extend(self._path_fingerprint(Path(item)))
|
|
147
|
+
return "\n".join(chunks)
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _path_fingerprint(path: Path) -> list[str]:
|
|
151
|
+
if not path.exists():
|
|
152
|
+
return []
|
|
153
|
+
if path.is_file():
|
|
154
|
+
stat = path.stat()
|
|
155
|
+
return [f"{path}:{stat.st_size}:{stat.st_mtime_ns}"]
|
|
156
|
+
ignored = {
|
|
157
|
+
".git",
|
|
158
|
+
".venv",
|
|
159
|
+
"venv",
|
|
160
|
+
"node_modules",
|
|
161
|
+
"__pycache__",
|
|
162
|
+
WORKSPACE_DIR_NAME,
|
|
163
|
+
}
|
|
164
|
+
chunks: list[str] = []
|
|
165
|
+
for child in sorted(path.rglob("*")):
|
|
166
|
+
if len(chunks) >= 2000:
|
|
167
|
+
chunks.append("[truncated]")
|
|
168
|
+
break
|
|
169
|
+
if any(part in ignored for part in child.parts):
|
|
170
|
+
continue
|
|
171
|
+
if child.is_file():
|
|
172
|
+
stat = child.stat()
|
|
173
|
+
chunks.append(f"{child}:{stat.st_size}:{stat.st_mtime_ns}")
|
|
174
|
+
return chunks
|
|
175
|
+
|
|
176
|
+
def get(self, capability_id: str, params: dict, entrypoint: str = "") -> CacheEntry | None:
|
|
177
|
+
"""Retorna la entrada cacheada si existe y no ha expirado."""
|
|
178
|
+
short_id = capability_id.split(".")[-1]
|
|
179
|
+
if capability_id in _NO_CACHE_CAPABILITIES or short_id in _NO_CACHE_CAPABILITIES:
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
key = self._cache_key(capability_id, params, entrypoint)
|
|
183
|
+
|
|
184
|
+
# Primero revisa memoria
|
|
185
|
+
if key in self._memory:
|
|
186
|
+
entry = self._memory[key]
|
|
187
|
+
if not entry.is_expired():
|
|
188
|
+
return entry
|
|
189
|
+
del self._memory[key]
|
|
190
|
+
|
|
191
|
+
# Luego revisa disco
|
|
192
|
+
cache_file = self._cache_file(key)
|
|
193
|
+
if not cache_file.exists():
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
data = json.loads(cache_file.read_text(encoding="utf-8"))
|
|
198
|
+
entry = CacheEntry.from_dict(data)
|
|
199
|
+
if entry.is_expired():
|
|
200
|
+
cache_file.unlink(missing_ok=True)
|
|
201
|
+
return None
|
|
202
|
+
self._memory[key] = entry
|
|
203
|
+
return entry
|
|
204
|
+
except (OSError, json.JSONDecodeError, KeyError):
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
def set(
|
|
208
|
+
self,
|
|
209
|
+
capability_id: str,
|
|
210
|
+
params: dict,
|
|
211
|
+
stdout: str,
|
|
212
|
+
stderr: str,
|
|
213
|
+
returncode: int,
|
|
214
|
+
entrypoint: str = "",
|
|
215
|
+
ttl: float | None = None,
|
|
216
|
+
) -> None:
|
|
217
|
+
"""Almacena un resultado en cache (memoria + disco)."""
|
|
218
|
+
short_id = capability_id.split(".")[-1]
|
|
219
|
+
if capability_id in _NO_CACHE_CAPABILITIES or short_id in _NO_CACHE_CAPABILITIES:
|
|
220
|
+
return
|
|
221
|
+
if returncode != 0:
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
effective_ttl = ttl if ttl is not None else self._default_ttl
|
|
225
|
+
key = self._cache_key(capability_id, params, entrypoint)
|
|
226
|
+
entry = CacheEntry(
|
|
227
|
+
stdout=stdout,
|
|
228
|
+
stderr=stderr,
|
|
229
|
+
returncode=returncode,
|
|
230
|
+
created_at=time.monotonic(),
|
|
231
|
+
ttl=effective_ttl,
|
|
232
|
+
)
|
|
233
|
+
self._memory[key] = entry
|
|
234
|
+
self._cap_index.setdefault(capability_id, set()).add(key)
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
self._ensure_dir()
|
|
238
|
+
disk_data = {**entry.to_dict(), "_cap_id": capability_id}
|
|
239
|
+
self._cache_file(key).write_text(
|
|
240
|
+
json.dumps(disk_data, ensure_ascii=False, indent=2),
|
|
241
|
+
encoding="utf-8",
|
|
242
|
+
)
|
|
243
|
+
except OSError: # nosec B110
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
def invalidate(self, capability_id: str) -> int:
|
|
247
|
+
"""Invalida todas las entradas de una capability. Retorna cantidad eliminada."""
|
|
248
|
+
keys = self._cap_index.pop(capability_id, set())
|
|
249
|
+
removed_memory = self._invalidate_memory_and_keys(keys)
|
|
250
|
+
removed_disk = self._invalidate_disk_by_id(capability_id)
|
|
251
|
+
return removed_memory + removed_disk
|
|
252
|
+
|
|
253
|
+
def _invalidate_memory_and_keys(self, keys: set[str]) -> int:
|
|
254
|
+
removed = 0
|
|
255
|
+
for k in keys:
|
|
256
|
+
if k in self._memory:
|
|
257
|
+
del self._memory[k]
|
|
258
|
+
removed += 1
|
|
259
|
+
cache_file = self._cache_file(k)
|
|
260
|
+
if cache_file.exists():
|
|
261
|
+
try:
|
|
262
|
+
cache_file.unlink()
|
|
263
|
+
except OSError: # nosec B110
|
|
264
|
+
pass
|
|
265
|
+
return removed
|
|
266
|
+
|
|
267
|
+
def _invalidate_disk_by_id(self, capability_id: str) -> int:
|
|
268
|
+
removed = 0
|
|
269
|
+
if not self._cache_dir.exists():
|
|
270
|
+
return removed
|
|
271
|
+
for f in self._cache_dir.glob(_CACHE_FILE_GLOB):
|
|
272
|
+
try:
|
|
273
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
274
|
+
if data.get("_cap_id") == capability_id:
|
|
275
|
+
key = f.stem
|
|
276
|
+
self._memory.pop(key, None)
|
|
277
|
+
f.unlink()
|
|
278
|
+
removed += 1
|
|
279
|
+
except (OSError, json.JSONDecodeError): # nosec B110
|
|
280
|
+
pass
|
|
281
|
+
return removed
|
|
282
|
+
|
|
283
|
+
def clear_expired(self) -> int:
|
|
284
|
+
"""Elimina del disco todas las entradas expiradas. Retorna cantidad."""
|
|
285
|
+
removed = 0
|
|
286
|
+
if not self._cache_dir.exists():
|
|
287
|
+
return 0
|
|
288
|
+
for f in self._cache_dir.glob(_CACHE_FILE_GLOB):
|
|
289
|
+
try:
|
|
290
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
291
|
+
entry = CacheEntry.from_dict(data)
|
|
292
|
+
if entry.is_expired():
|
|
293
|
+
f.unlink()
|
|
294
|
+
removed += 1
|
|
295
|
+
except (OSError, json.JSONDecodeError, KeyError):
|
|
296
|
+
f.unlink(missing_ok=True)
|
|
297
|
+
removed += 1
|
|
298
|
+
return removed
|
|
299
|
+
|
|
300
|
+
def stats(self) -> dict:
|
|
301
|
+
"""Retorna estadísticas del cache."""
|
|
302
|
+
total_disk = len(list(self._cache_dir.glob(_CACHE_FILE_GLOB))) if self._cache_dir.exists() else 0
|
|
303
|
+
return {
|
|
304
|
+
"in_memory": len(self._memory),
|
|
305
|
+
"on_disk": total_disk,
|
|
306
|
+
"cache_dir": str(self._cache_dir),
|
|
307
|
+
"default_ttl_seconds": self._default_ttl,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# Singleton global del cache
|
|
312
|
+
_capability_cache: CapabilityCache | None = None
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def get_capability_cache() -> CapabilityCache:
|
|
316
|
+
global _capability_cache
|
|
317
|
+
if _capability_cache is None:
|
|
318
|
+
_capability_cache = CapabilityCache()
|
|
319
|
+
return _capability_cache
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Módulo centralizado de utilidades y helpers comunes para scripts de capacidades
|
|
2
|
+
(Infraestructura)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CapabilityHelper:
|
|
11
|
+
@staticmethod
|
|
12
|
+
def get_project_root(start_path: Path | None = None) -> Path:
|
|
13
|
+
"""Busca recursivamente hacia arriba hasta encontrar el directorio raíz del proyecto
|
|
14
|
+
(.higpertext o .git)."""
|
|
15
|
+
current = Path(start_path or Path(__file__)).resolve()
|
|
16
|
+
for parent in [current] + list(current.parents):
|
|
17
|
+
if (parent / WORKSPACE_DIR_NAME).exists() or (parent / ".git").exists():
|
|
18
|
+
return parent
|
|
19
|
+
# Fallback al directorio de trabajo actual
|
|
20
|
+
return Path.cwd().resolve()
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def load_json_file(file_path: Path) -> dict:
|
|
24
|
+
"""Carga y parsea de forma segura un archivo JSON."""
|
|
25
|
+
if not file_path.exists():
|
|
26
|
+
return {}
|
|
27
|
+
try:
|
|
28
|
+
return json.loads(file_path.read_text(encoding="utf-8"))
|
|
29
|
+
except (OSError, json.JSONDecodeError):
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def load_environment(cls, root: Path) -> dict:
|
|
34
|
+
"""Carga el archivo de configuración de entorno del proyecto."""
|
|
35
|
+
return cls.load_json_file(root / WORKSPACE_DIR_NAME / "config" / "environment.json")
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def load_session(cls, root: Path) -> dict:
|
|
39
|
+
"""Carga la sesión de desarrollo temporal activa del proyecto."""
|
|
40
|
+
return cls.load_json_file(root / WORKSPACE_DIR_NAME / "state" / "session.json")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Infrastructure CLI init
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Handlers CLI para gestión de agentes registrados."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def dispatch_agent(nexus, args: argparse.Namespace, logger, root_dir: Path) -> None:
|
|
10
|
+
action = getattr(args, "agent_action", None)
|
|
11
|
+
target = getattr(args, "target", None)
|
|
12
|
+
handlers = {
|
|
13
|
+
"init": lambda: nexus.agent_init(args.profile, target),
|
|
14
|
+
"status": lambda: nexus.agent_status(target),
|
|
15
|
+
"clean": lambda: nexus.agent_clean(target),
|
|
16
|
+
"register": lambda: dispatch_agent_register(args, logger, root_dir),
|
|
17
|
+
"sync": lambda: dispatch_agent_sync(args, logger, root_dir),
|
|
18
|
+
"list": lambda: dispatch_agent_list(logger, root_dir),
|
|
19
|
+
}
|
|
20
|
+
handler = handlers.get(action)
|
|
21
|
+
if handler is None:
|
|
22
|
+
logger.info("Uso: htx agent [init|status|clean|register|sync|list]")
|
|
23
|
+
return
|
|
24
|
+
handler()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def dispatch_agent_register(args: argparse.Namespace, logger, root_dir: Path) -> None:
|
|
28
|
+
from higpertext.kernel.infrastructure import JsonAgentRegistry
|
|
29
|
+
from higpertext.kernel.application import RegisterAgentUseCase
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
repo = JsonAgentRegistry(root_dir)
|
|
33
|
+
rec = RegisterAgentUseCase(repo).execute(args.name, args.path, args.profile)
|
|
34
|
+
logger.ok(f"[SUCCESS] Agente '{rec.name}' registrado (perfil: {rec.profile})")
|
|
35
|
+
except ValueError as e:
|
|
36
|
+
logger.error(f"[ERROR] {e}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def dispatch_agent_sync(args: argparse.Namespace, logger, root_dir: Path) -> None:
|
|
40
|
+
from higpertext.kernel.infrastructure import JsonAgentRegistry
|
|
41
|
+
from higpertext.kernel.application import SyncAgentUseCase
|
|
42
|
+
|
|
43
|
+
repo = JsonAgentRegistry(root_dir)
|
|
44
|
+
results = SyncAgentUseCase(repo, root_dir).execute(
|
|
45
|
+
getattr(args, "name", None), getattr(args, "assistant", "claude")
|
|
46
|
+
)
|
|
47
|
+
for result in results:
|
|
48
|
+
logger.ok(f" {'✓' if result.success else '✗'} {result.name}: {result.message}")
|
|
49
|
+
if not results:
|
|
50
|
+
logger.info("[INFO] No hay agentes registrados para sincronizar.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def dispatch_agent_list(logger, root_dir: Path) -> None:
|
|
54
|
+
from higpertext.kernel.infrastructure import JsonAgentRegistry
|
|
55
|
+
from higpertext.kernel.application import ListAgentsUseCase
|
|
56
|
+
|
|
57
|
+
agents = ListAgentsUseCase(JsonAgentRegistry(root_dir)).execute()
|
|
58
|
+
if not agents:
|
|
59
|
+
logger.info("[INFO] No hay agentes registrados.")
|
|
60
|
+
return
|
|
61
|
+
for agent in agents:
|
|
62
|
+
logger.info(f" • {agent.name:20s} perfil: {agent.profile:20s} {agent.path}")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Helpers de parsing para la capa CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_kv_args(raw_args: list[str]) -> dict:
|
|
7
|
+
"""Parsea una lista ['--key', 'val', ...] en un dict."""
|
|
8
|
+
params: dict = {}
|
|
9
|
+
it = iter(raw_args)
|
|
10
|
+
for item in it:
|
|
11
|
+
if item.startswith("--"):
|
|
12
|
+
key = item.lstrip("--")
|
|
13
|
+
try:
|
|
14
|
+
params[key] = next(it)
|
|
15
|
+
except StopIteration:
|
|
16
|
+
params[key] = True
|
|
17
|
+
return params
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def pop_bool_flag(task_args: list[str], flag: str, default: bool) -> tuple[bool, list[str]]:
|
|
21
|
+
"""Extrae un flag booleano opcional de argumentos remanentes."""
|
|
22
|
+
if flag not in task_args:
|
|
23
|
+
return default, task_args
|
|
24
|
+
|
|
25
|
+
idx = task_args.index(flag)
|
|
26
|
+
task_args.pop(idx)
|
|
27
|
+
value = True
|
|
28
|
+
if idx < len(task_args) and task_args[idx].lower() in (
|
|
29
|
+
"true",
|
|
30
|
+
"1",
|
|
31
|
+
"yes",
|
|
32
|
+
"false",
|
|
33
|
+
"0",
|
|
34
|
+
"no",
|
|
35
|
+
):
|
|
36
|
+
value = task_args[idx].lower() in ("true", "1", "yes")
|
|
37
|
+
task_args.pop(idx)
|
|
38
|
+
|
|
39
|
+
return value, task_args
|