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,85 @@
|
|
|
1
|
+
"""SkillHookCompiler — lee el perfil activo y registra sus hook tasks en hooks_config.json."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from higpertext.kernel.infrastructure.hook_config_loader import (
|
|
8
|
+
load_hooks_config,
|
|
9
|
+
load_hook_definitions,
|
|
10
|
+
save_hooks_config,
|
|
11
|
+
)
|
|
12
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
13
|
+
|
|
14
|
+
_log = get_logger()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SkillHookCompiler:
|
|
18
|
+
"""Sincroniza los hook IDs activos en hooks_config.json según el perfil."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, project_root: Path) -> None:
|
|
21
|
+
self.project_root = project_root
|
|
22
|
+
from higpertext.kernel.config_paths import CAPABILITIES_DIR
|
|
23
|
+
|
|
24
|
+
self._caps_root = project_root / "src" / "higpertext" / "capabilities"
|
|
25
|
+
if not self._caps_root.exists():
|
|
26
|
+
# Fallback to قدیمی/legacy or other folder, or global dir if project
|
|
27
|
+
# specific doesn't exist
|
|
28
|
+
legacy_caps = project_root / "src" / "capabilities"
|
|
29
|
+
if legacy_caps.exists():
|
|
30
|
+
self._caps_root = legacy_caps
|
|
31
|
+
else:
|
|
32
|
+
self._caps_root = CAPABILITIES_DIR
|
|
33
|
+
|
|
34
|
+
# Perfiles que activan evaluación de calidad de código
|
|
35
|
+
_QUALITY_PROFILES: frozenset[str] = frozenset({"software_developer", "devsecops", "ado_admin"})
|
|
36
|
+
|
|
37
|
+
def compile(self, profile_name: str, capabilities: list[str]) -> None:
|
|
38
|
+
# Hooks consolidados base — activos en todos los perfiles
|
|
39
|
+
hook_ids: list[str] = [
|
|
40
|
+
"hook_bash_guard", # todas las reglas Bash en cadena
|
|
41
|
+
"hook_post_observer", # telemetría + aviso de output masivo
|
|
42
|
+
"hook_session_prompt", # auto-start + estado de sesión + skills
|
|
43
|
+
"hook_session_stop", # cierre de turno y verificación de commits
|
|
44
|
+
"hook_context_manager", # preserva estado higpertext antes de compresión
|
|
45
|
+
"hook_read_guard", # evita lecturas completas de archivos grandes
|
|
46
|
+
"hook_security_guard_pre", # bloquea comandos/rutas sensibles
|
|
47
|
+
"hook_security_guard_post", # enmascara secretos en outputs
|
|
48
|
+
]
|
|
49
|
+
if profile_name in self._QUALITY_PROFILES:
|
|
50
|
+
hook_ids.append("hook_code_quality")
|
|
51
|
+
for cap_id in capabilities:
|
|
52
|
+
hook_id = self._resolve_hook_id(cap_id)
|
|
53
|
+
if hook_id and hook_id not in hook_ids:
|
|
54
|
+
hook_ids.append(hook_id)
|
|
55
|
+
|
|
56
|
+
config = load_hooks_config(self.project_root)
|
|
57
|
+
|
|
58
|
+
# Siempre sincroniza las definiciones desde el catálogo canónico del engine
|
|
59
|
+
# (.higpertext/hooks_config.json) para que los proyectos destino reflejen
|
|
60
|
+
# cualquier actualización (nuevos hooks, cambios de matcher, etc.).
|
|
61
|
+
from higpertext.kernel.config_paths import PROJECT_ROOT as ENGINE_ROOT
|
|
62
|
+
|
|
63
|
+
definitions = load_hook_definitions(ENGINE_ROOT)
|
|
64
|
+
if definitions:
|
|
65
|
+
config["hooks"] = definitions
|
|
66
|
+
|
|
67
|
+
config.setdefault("profile_hooks", {})[profile_name] = hook_ids
|
|
68
|
+
save_hooks_config(self.project_root, config)
|
|
69
|
+
_log.info(
|
|
70
|
+
f"SkillHookCompiler: {len(hook_ids)} hooks compilados para perfil '{profile_name}'"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def _resolve_hook_id(self, cap_id: str) -> str | None:
|
|
74
|
+
"""Lee hook_task_id del JSON de la capability; devuelve None si no existe."""
|
|
75
|
+
namespace, _, name = cap_id.partition(".")
|
|
76
|
+
if not name:
|
|
77
|
+
return None
|
|
78
|
+
cap_file = self._caps_root / namespace / f"{name}.json"
|
|
79
|
+
if not cap_file.exists():
|
|
80
|
+
return None
|
|
81
|
+
try:
|
|
82
|
+
data = json.loads(cap_file.read_text(encoding="utf-8"))
|
|
83
|
+
return data.get("hook_task_id") or None
|
|
84
|
+
except (OSError, json.JSONDecodeError):
|
|
85
|
+
return None
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Telemetry Application layer — TelemetryCollector and TelemetryAggregator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
5
|
+
from higpertext.kernel.app_config import TELEMETRY_FILE as _STORE_FILENAME
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from datetime import datetime, timedelta, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
from collections import Counter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TelemetryCollector:
|
|
15
|
+
"""Escribe eventos de telemetría de forma atómica al store JSONL."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, project_root: Path, project_id: str | None = None) -> None:
|
|
18
|
+
self._store = project_root / WORKSPACE_DIR_NAME / "state" / _STORE_FILENAME
|
|
19
|
+
self._project_id = project_id or _resolve_project_id(project_root)
|
|
20
|
+
|
|
21
|
+
def record(self, event: str, **fields: Any) -> None:
|
|
22
|
+
"""Registra un evento con timestamp UTC y project_id. Silencia errores de I/O."""
|
|
23
|
+
entry = {"ts": _now(), "event": event, "project_id": self._project_id, **fields}
|
|
24
|
+
try:
|
|
25
|
+
with self._store.open("a", encoding="utf-8") as fh:
|
|
26
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
27
|
+
except OSError: # nosec B110
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
def session_start(self, session_id: str, profile: str) -> None:
|
|
31
|
+
self.record("session_start", session_id=session_id, profile=profile)
|
|
32
|
+
|
|
33
|
+
def session_stop(self, session_id: str, profile: str, duration_min: float) -> None:
|
|
34
|
+
self.record(
|
|
35
|
+
"session_stop",
|
|
36
|
+
session_id=session_id,
|
|
37
|
+
profile=profile,
|
|
38
|
+
duration_min=round(duration_min, 2),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def commit(
|
|
42
|
+
self,
|
|
43
|
+
session_id: str,
|
|
44
|
+
commit_hash: str,
|
|
45
|
+
commit_type: str,
|
|
46
|
+
scope: str,
|
|
47
|
+
profile: str,
|
|
48
|
+
) -> None:
|
|
49
|
+
self.record(
|
|
50
|
+
"commit",
|
|
51
|
+
session_id=session_id,
|
|
52
|
+
hash=commit_hash,
|
|
53
|
+
type=commit_type,
|
|
54
|
+
scope=scope,
|
|
55
|
+
profile=profile,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def capability_used(self, session_id: str, capability_id: str) -> None:
|
|
59
|
+
self.record("capability_used", session_id=session_id, capability=capability_id)
|
|
60
|
+
|
|
61
|
+
def hook_intercept(self, session_id: str, capability_id: str, command_fragment: str) -> None:
|
|
62
|
+
"""Registra cuando el enforcer redirige un comando bash a una capacidad."""
|
|
63
|
+
self.record(
|
|
64
|
+
"hook_intercept",
|
|
65
|
+
session_id=session_id,
|
|
66
|
+
capability=capability_id,
|
|
67
|
+
fragment=command_fragment[:80],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def hook_passthrough(self, session_id: str, command_fragment: str) -> None:
|
|
71
|
+
"""Registra cuando el enforcer deja pasar un comando sin redirigir."""
|
|
72
|
+
self.record(
|
|
73
|
+
"hook_passthrough",
|
|
74
|
+
session_id=session_id,
|
|
75
|
+
fragment=command_fragment[:80],
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def tool_call(
|
|
79
|
+
self,
|
|
80
|
+
session_id: str,
|
|
81
|
+
tool_name: str,
|
|
82
|
+
input_chars: int,
|
|
83
|
+
output_chars: int,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Registra una tool call con estimación de tokens por conteo de chars."""
|
|
86
|
+
input_tokens = input_chars // 4
|
|
87
|
+
output_tokens = output_chars // 4
|
|
88
|
+
cache_read_tokens = int(input_tokens * 0.80)
|
|
89
|
+
cache_write_tokens = int(input_tokens * 0.20)
|
|
90
|
+
estimated_tokens = input_tokens + output_tokens
|
|
91
|
+
estimated_cost_usd = (
|
|
92
|
+
cache_read_tokens * 0.30 / 1_000_000
|
|
93
|
+
+ cache_write_tokens * 3.75 / 1_000_000
|
|
94
|
+
+ output_tokens * 15.0 / 1_000_000
|
|
95
|
+
)
|
|
96
|
+
self.record(
|
|
97
|
+
"tool_call",
|
|
98
|
+
session_id=session_id,
|
|
99
|
+
tool=tool_name,
|
|
100
|
+
input_chars=input_chars,
|
|
101
|
+
output_chars=output_chars,
|
|
102
|
+
estimated_tokens=estimated_tokens,
|
|
103
|
+
estimated_cost_usd=round(estimated_cost_usd, 6),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class TelemetryAggregator:
|
|
108
|
+
"""Calcula métricas agregadas y correlaciones desde el store JSONL."""
|
|
109
|
+
|
|
110
|
+
def __init__(self, project_root: Path, project_id: str | None = None) -> None:
|
|
111
|
+
self._store = project_root / WORKSPACE_DIR_NAME / "state" / _STORE_FILENAME
|
|
112
|
+
self._project_id = project_id
|
|
113
|
+
|
|
114
|
+
def load(self, days: int = 7) -> list[dict]:
|
|
115
|
+
"""Carga eventos de los últimos N días, filtrando por project_id si se especificó."""
|
|
116
|
+
if not self._store.exists():
|
|
117
|
+
return []
|
|
118
|
+
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
|
|
119
|
+
events: list[dict] = []
|
|
120
|
+
for line in self._store.read_text(encoding="utf-8").splitlines():
|
|
121
|
+
if not line.strip():
|
|
122
|
+
continue
|
|
123
|
+
try:
|
|
124
|
+
e = json.loads(line)
|
|
125
|
+
ts = datetime.fromisoformat(e.get("ts", ""))
|
|
126
|
+
if ts < cutoff:
|
|
127
|
+
continue
|
|
128
|
+
if self._project_id and e.get("project_id") and e["project_id"] != self._project_id:
|
|
129
|
+
continue
|
|
130
|
+
events.append(e)
|
|
131
|
+
except (json.JSONDecodeError, ValueError):
|
|
132
|
+
continue
|
|
133
|
+
return events
|
|
134
|
+
|
|
135
|
+
def projects(self) -> list[str]:
|
|
136
|
+
"""Retorna todos los project_id únicos presentes en el store."""
|
|
137
|
+
if not self._store.exists():
|
|
138
|
+
return []
|
|
139
|
+
ids: set[str] = set()
|
|
140
|
+
for line in self._store.read_text(encoding="utf-8").splitlines():
|
|
141
|
+
if not line.strip():
|
|
142
|
+
continue
|
|
143
|
+
try:
|
|
144
|
+
e = json.loads(line)
|
|
145
|
+
pid = e.get("project_id")
|
|
146
|
+
if pid:
|
|
147
|
+
ids.add(pid)
|
|
148
|
+
except json.JSONDecodeError:
|
|
149
|
+
continue
|
|
150
|
+
return sorted(ids)
|
|
151
|
+
|
|
152
|
+
def compute_activity(self, days: int = 1, limit: int = 20) -> list[dict]:
|
|
153
|
+
"""Retorna las últimas `limit` actividades ordenadas por timestamp desc."""
|
|
154
|
+
events = self.load(days)
|
|
155
|
+
activities = []
|
|
156
|
+
|
|
157
|
+
for e in events:
|
|
158
|
+
ev = e.get("event", "")
|
|
159
|
+
if ev == "activity":
|
|
160
|
+
activities.append(
|
|
161
|
+
{
|
|
162
|
+
"ts": e.get("ts", ""),
|
|
163
|
+
"tool": e.get("tool", ""),
|
|
164
|
+
"op_type": e.get("op_type", ""),
|
|
165
|
+
"target": e.get("target", ""),
|
|
166
|
+
"scope": e.get("scope", ""),
|
|
167
|
+
"higpertext_related": e.get("higpertext_related", False),
|
|
168
|
+
"session_id": e.get("session_id", ""),
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
elif ev == "commit":
|
|
172
|
+
activities.append(
|
|
173
|
+
{
|
|
174
|
+
"ts": e.get("ts", ""),
|
|
175
|
+
"tool": "Commit",
|
|
176
|
+
"op_type": "commit",
|
|
177
|
+
"target": f"{e.get('type', '?')}({e.get('scope', '')}):"
|
|
178
|
+
f" {e.get('hash', '')}",
|
|
179
|
+
"scope": "git",
|
|
180
|
+
"higpertext_related": True,
|
|
181
|
+
"session_id": e.get("session_id", ""),
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
elif ev == "hook_intercept" and e.get("capability") != "BLOCKED":
|
|
185
|
+
activities.append(
|
|
186
|
+
{
|
|
187
|
+
"ts": e.get("ts", ""),
|
|
188
|
+
"tool": "higpertext",
|
|
189
|
+
"op_type": "higpertext-task",
|
|
190
|
+
"target": e.get("capability", ""),
|
|
191
|
+
"scope": "higpertext",
|
|
192
|
+
"higpertext_related": True,
|
|
193
|
+
"session_id": e.get("session_id", ""),
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
activities.sort(key=lambda x: x["ts"], reverse=True)
|
|
198
|
+
return activities[:limit]
|
|
199
|
+
|
|
200
|
+
def compute(self, days: int = 7) -> dict[str, Any]:
|
|
201
|
+
events = self.load(days)
|
|
202
|
+
if not events:
|
|
203
|
+
return {}
|
|
204
|
+
|
|
205
|
+
sessions: dict[str, dict] = {}
|
|
206
|
+
commits: list[dict] = []
|
|
207
|
+
capabilities: Counter = Counter()
|
|
208
|
+
intercepts: Counter = Counter()
|
|
209
|
+
tool_calls_total = 0
|
|
210
|
+
higpertext_direct_calls = 0
|
|
211
|
+
tool_calls_by_name: Counter = Counter()
|
|
212
|
+
estimated_tokens = 0
|
|
213
|
+
estimated_cost = 0.0
|
|
214
|
+
|
|
215
|
+
for e in events:
|
|
216
|
+
ev = e.get("event", "")
|
|
217
|
+
sid = e.get("session_id", "unknown")
|
|
218
|
+
|
|
219
|
+
if ev == "session_start":
|
|
220
|
+
sessions.setdefault(sid, {})["start_ts"] = e.get("ts")
|
|
221
|
+
sessions[sid]["profile"] = e.get("profile", "")
|
|
222
|
+
|
|
223
|
+
elif ev == "session_stop":
|
|
224
|
+
sessions.setdefault(sid, {})["stop_ts"] = e.get("ts")
|
|
225
|
+
sessions[sid]["duration_min"] = e.get("duration_min", 0)
|
|
226
|
+
|
|
227
|
+
elif ev == "commit":
|
|
228
|
+
commits.append(e)
|
|
229
|
+
|
|
230
|
+
elif ev == "capability_used":
|
|
231
|
+
capabilities[e.get("capability", "unknown")] += 1
|
|
232
|
+
|
|
233
|
+
elif ev == "hook_intercept":
|
|
234
|
+
cap = e.get("capability", "unknown")
|
|
235
|
+
if cap != "BLOCKED":
|
|
236
|
+
intercepts[cap] += 1
|
|
237
|
+
|
|
238
|
+
elif ev == "tool_call":
|
|
239
|
+
tool_calls_total += 1
|
|
240
|
+
tool_calls_by_name[e.get("tool", "unknown")] += 1
|
|
241
|
+
estimated_tokens += e.get("estimated_tokens", 0)
|
|
242
|
+
estimated_cost += e.get("estimated_cost_usd", 0.0)
|
|
243
|
+
if e.get("is_higpertext_call"):
|
|
244
|
+
higpertext_direct_calls += 1
|
|
245
|
+
|
|
246
|
+
active_sessions = sum(1 for s in sessions.values() if "stop_ts" not in s)
|
|
247
|
+
total_duration_min = sum(s.get("duration_min", 0) for s in sessions.values())
|
|
248
|
+
|
|
249
|
+
tokens_per_session = estimated_tokens // len(sessions) if sessions else 0
|
|
250
|
+
cost_per_commit = round(estimated_cost / len(commits), 4) if commits else 0.0
|
|
251
|
+
commits_per_session = round(len(commits) / len(sessions), 2) if sessions else 0.0
|
|
252
|
+
|
|
253
|
+
total_intercepts = sum(intercepts.values())
|
|
254
|
+
total_capability_used = sum(capabilities.values())
|
|
255
|
+
higpertext_calls = total_intercepts + total_capability_used + higpertext_direct_calls
|
|
256
|
+
bash_direct = tool_calls_total - higpertext_direct_calls
|
|
257
|
+
higpertext_adoption_pct = (
|
|
258
|
+
round(higpertext_calls / (higpertext_calls + bash_direct) * 100, 1)
|
|
259
|
+
if (higpertext_calls + bash_direct) > 0
|
|
260
|
+
else 0.0
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
all_caps: Counter = Counter()
|
|
264
|
+
all_caps.update(capabilities)
|
|
265
|
+
all_caps.update(intercepts)
|
|
266
|
+
top_capabilities = all_caps.most_common(5)
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
"period_days": days,
|
|
270
|
+
"sessions_total": len(sessions),
|
|
271
|
+
"sessions_active": active_sessions,
|
|
272
|
+
"total_duration_min": round(total_duration_min, 1),
|
|
273
|
+
"commits_total": len(commits),
|
|
274
|
+
"tool_calls_total": tool_calls_total,
|
|
275
|
+
"estimated_tokens": estimated_tokens,
|
|
276
|
+
"estimated_cost_usd": round(estimated_cost, 4),
|
|
277
|
+
"correlations": {
|
|
278
|
+
"tokens_per_session": tokens_per_session,
|
|
279
|
+
"cost_per_commit_usd": cost_per_commit,
|
|
280
|
+
"commits_per_session": commits_per_session,
|
|
281
|
+
"higpertext_adoption_pct": higpertext_adoption_pct,
|
|
282
|
+
"higpertext_calls": higpertext_calls,
|
|
283
|
+
"higpertext_direct_calls": higpertext_direct_calls,
|
|
284
|
+
"bash_direct_calls": bash_direct,
|
|
285
|
+
},
|
|
286
|
+
"top_capabilities": top_capabilities,
|
|
287
|
+
"top_direct_tools": tool_calls_by_name.most_common(5),
|
|
288
|
+
"commits": commits,
|
|
289
|
+
"recent_activity": self.compute_activity(days, limit=12),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _now() -> str:
|
|
294
|
+
return datetime.now(timezone.utc).isoformat(timespec="seconds")
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def get_collector(project_root: Path | None = None) -> TelemetryCollector:
|
|
298
|
+
"""Factory que resuelve la raíz del proyecto si no se provee."""
|
|
299
|
+
if project_root is None:
|
|
300
|
+
project_root = _resolve_root()
|
|
301
|
+
return TelemetryCollector(project_root)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _resolve_root() -> Path:
|
|
305
|
+
candidate = Path(__file__).resolve().parent
|
|
306
|
+
for _ in range(8):
|
|
307
|
+
if (candidate / "src/config/htx_config.json").exists():
|
|
308
|
+
return candidate
|
|
309
|
+
candidate = candidate.parent
|
|
310
|
+
_fallback = Path("/home/aomerge/Documentos/Proyects/agents/LLM-agent")
|
|
311
|
+
if (_fallback / "src/config/htx_config.json").exists():
|
|
312
|
+
return _fallback
|
|
313
|
+
return Path.cwd()
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _resolve_project_id(project_root: Path) -> str:
|
|
317
|
+
env_file = project_root / WORKSPACE_DIR_NAME / "config" / "environment.json"
|
|
318
|
+
try:
|
|
319
|
+
data = json.loads(env_file.read_text(encoding="utf-8"))
|
|
320
|
+
return (
|
|
321
|
+
data.get("variables", {}).get("repository_id")
|
|
322
|
+
or data.get("project_name")
|
|
323
|
+
or project_root.name
|
|
324
|
+
)
|
|
325
|
+
except (OSError, json.JSONDecodeError):
|
|
326
|
+
return project_root.name
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""higpertext Workflow Manager — compila workflows YAML al esquema interno JSON
|
|
2
|
+
y los gestiona (Aplicación)."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
8
|
+
|
|
9
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
10
|
+
|
|
11
|
+
_log = get_logger()
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import yaml
|
|
15
|
+
HAS_YAML = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
HAS_YAML = False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class WorkflowManager:
|
|
21
|
+
def __init__(self, workflows_dir: Path):
|
|
22
|
+
self.workflows_dir = workflows_dir
|
|
23
|
+
self.workflows_dir.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
|
|
25
|
+
def _get_custom_workflows_dir(self) -> Path:
|
|
26
|
+
"""Obtiene la ruta del directorio de flujos personalizados del proyecto actual
|
|
27
|
+
(.higpertext/workflows)."""
|
|
28
|
+
project_root = Path(os.getcwd()).resolve()
|
|
29
|
+
return project_root / WORKSPACE_DIR_NAME / "workflows"
|
|
30
|
+
|
|
31
|
+
def _write_inline_capabilities(self, capabilities_data: list) -> None:
|
|
32
|
+
"""Registra capacidades inline definidas en el YAML
|
|
33
|
+
en .higpertext/capabilities/ y scripts/."""
|
|
34
|
+
project_root = Path(os.getcwd()).resolve()
|
|
35
|
+
caps_dir = project_root / WORKSPACE_DIR_NAME / "capabilities"
|
|
36
|
+
scripts_dir = project_root / WORKSPACE_DIR_NAME / "scripts"
|
|
37
|
+
caps_dir.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
scripts_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
for c_def in capabilities_data:
|
|
41
|
+
if not isinstance(c_def, dict) or "id" not in c_def:
|
|
42
|
+
continue
|
|
43
|
+
c_id = c_def["id"]
|
|
44
|
+
c_lang = c_def.get("language", "bash")
|
|
45
|
+
ext = ".py" if c_lang == "python" else ".ps1" if c_lang == "powershell" else ".sh"
|
|
46
|
+
script_path = scripts_dir / f"{c_id}{ext}"
|
|
47
|
+
script_path.write_text(c_def.get("script", ""), encoding="utf-8")
|
|
48
|
+
try:
|
|
49
|
+
script_path.chmod(0o755)
|
|
50
|
+
except OSError: # nosec B110
|
|
51
|
+
pass
|
|
52
|
+
cap_json = {
|
|
53
|
+
"id": c_id,
|
|
54
|
+
"name": c_def.get("name", c_id),
|
|
55
|
+
"description": c_def.get("description", "Capacidad inline personalizada"),
|
|
56
|
+
"language": c_lang,
|
|
57
|
+
"entrypoint": str(script_path.resolve()),
|
|
58
|
+
"is_custom": True,
|
|
59
|
+
"parameters": c_def.get("parameters", []),
|
|
60
|
+
}
|
|
61
|
+
(caps_dir / f"{c_id}.json").write_text(
|
|
62
|
+
json.dumps(cap_json, indent=4, ensure_ascii=False), encoding="utf-8"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def _extract_steps(self, data: dict) -> list:
|
|
66
|
+
"""Extrae la lista de pasos desde 'steps' o desde 'jobs[].steps'."""
|
|
67
|
+
if "steps" in data and isinstance(data["steps"], list):
|
|
68
|
+
return data["steps"]
|
|
69
|
+
raw: list = []
|
|
70
|
+
for job in data.get("jobs", []):
|
|
71
|
+
if isinstance(job, dict):
|
|
72
|
+
raw.extend(job.get("steps", []))
|
|
73
|
+
return raw
|
|
74
|
+
|
|
75
|
+
def _build_chain(self, raw_steps: list) -> list:
|
|
76
|
+
"""Convierte la lista de pasos raw al formato de execution_chain interno."""
|
|
77
|
+
chain = []
|
|
78
|
+
for idx, st in enumerate(raw_steps, 1):
|
|
79
|
+
if not isinstance(st, dict) or not st.get("task"):
|
|
80
|
+
continue
|
|
81
|
+
chain.append(
|
|
82
|
+
{
|
|
83
|
+
"step": idx,
|
|
84
|
+
"task": st["task"],
|
|
85
|
+
"params": st.get("inputs", st.get("params", {})),
|
|
86
|
+
"on_failure": st.get(
|
|
87
|
+
"on_failure",
|
|
88
|
+
"continue" if st.get("continueOnError") else "abort",
|
|
89
|
+
),
|
|
90
|
+
"retries": int(st.get("retries", 0)),
|
|
91
|
+
"retry_delay": int(st.get("retry_delay", st.get("retryDelay", 5))),
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
return chain
|
|
95
|
+
|
|
96
|
+
def compile_yaml_workflow(self, yaml_file: Path) -> dict | None:
|
|
97
|
+
"""Lee y compila un archivo YAML estilo CI/CD al esquema interno JSON de higpertext."""
|
|
98
|
+
if not HAS_YAML:
|
|
99
|
+
_log.warning(f"[Warning] No se pudo compilar '{yaml_file.name}': PyYAML no instalado.")
|
|
100
|
+
return None
|
|
101
|
+
try:
|
|
102
|
+
data = yaml.safe_load(yaml_file.read_text(encoding="utf-8"))
|
|
103
|
+
if not isinstance(data, dict):
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
wf_id = data.get("id", data.get("name", yaml_file.stem))
|
|
107
|
+
wf_json = {
|
|
108
|
+
"id": wf_id,
|
|
109
|
+
"name": data.get("displayName", data.get("name", wf_id)),
|
|
110
|
+
"description": data.get("description", "Flujo personalizado del proyecto"),
|
|
111
|
+
"version": data.get("version", "1.0.0"),
|
|
112
|
+
"required_profile": data.get("required_profile", "any"),
|
|
113
|
+
"parameters": data.get("parameters", []),
|
|
114
|
+
"execution_chain": self._build_chain(self._extract_steps(data)),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if isinstance(data.get("capabilities"), list):
|
|
118
|
+
self._write_inline_capabilities(data["capabilities"])
|
|
119
|
+
|
|
120
|
+
compiled_dir = yaml_file.parent / "compiled"
|
|
121
|
+
compiled_dir.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
(compiled_dir / f"{wf_id}.json").write_text(
|
|
123
|
+
json.dumps(wf_json, indent=4, ensure_ascii=False), encoding="utf-8"
|
|
124
|
+
)
|
|
125
|
+
return wf_json
|
|
126
|
+
except (OSError, json.JSONDecodeError, yaml.YAMLError) as e:
|
|
127
|
+
_log.warning(f"[!] Error compilando YAML {yaml_file}: {e}")
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
def _get_all_workflows(self) -> list:
|
|
131
|
+
"""Carga y retorna todos los flujos del sistema
|
|
132
|
+
y los flujos YAML compilados del proyecto actual."""
|
|
133
|
+
wfs = []
|
|
134
|
+
# 1. Flujos del sistema en JSON
|
|
135
|
+
for file in self.workflows_dir.rglob("*.json"):
|
|
136
|
+
try:
|
|
137
|
+
wfs.append(json.loads(file.read_text(encoding="utf-8")))
|
|
138
|
+
except (OSError, json.JSONDecodeError):
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
# 2. Flujos personalizados del proyecto actual en YAML
|
|
142
|
+
custom_dir = self._get_custom_workflows_dir()
|
|
143
|
+
if custom_dir.exists():
|
|
144
|
+
for file in custom_dir.glob("*.yaml"):
|
|
145
|
+
compiled_wf = self.compile_yaml_workflow(file)
|
|
146
|
+
if compiled_wf:
|
|
147
|
+
wfs.append(compiled_wf)
|
|
148
|
+
for file in custom_dir.glob("*.yml"):
|
|
149
|
+
compiled_wf = self.compile_yaml_workflow(file)
|
|
150
|
+
if compiled_wf:
|
|
151
|
+
wfs.append(compiled_wf)
|
|
152
|
+
|
|
153
|
+
return wfs
|
|
154
|
+
|
|
155
|
+
def load_workflow(self, workflow_id: str) -> dict:
|
|
156
|
+
for wf in self._get_all_workflows():
|
|
157
|
+
if wf.get("id") == workflow_id or wf.get("id", "").split(".")[-1] == workflow_id:
|
|
158
|
+
return wf
|
|
159
|
+
raise FileNotFoundError(f"Workflow no encontrado: {workflow_id}")
|
|
160
|
+
|
|
161
|
+
def list_workflows(self) -> list:
|
|
162
|
+
wfs = []
|
|
163
|
+
for wf in self._get_all_workflows():
|
|
164
|
+
wfs.append(
|
|
165
|
+
{
|
|
166
|
+
"id": wf.get("id"),
|
|
167
|
+
"name": wf.get("name"),
|
|
168
|
+
"description": wf.get("description"),
|
|
169
|
+
"required_profile": wf.get("required_profile"),
|
|
170
|
+
"parameters": wf.get("parameters", []),
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
return wfs
|
|
174
|
+
|
|
175
|
+
def get_all_workflows_data(self) -> list:
|
|
176
|
+
return self._get_all_workflows()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Single source of truth for project directory paths, driven by src/config/htx_config.json."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
_ROOT = Path(__file__).resolve()
|
|
8
|
+
while _ROOT.name != "LLM-agent" and _ROOT.parent != _ROOT:
|
|
9
|
+
_ROOT = _ROOT.parent
|
|
10
|
+
|
|
11
|
+
if not (_ROOT / "src" / "config" / "htx_config.json").exists():
|
|
12
|
+
_fallback_root = Path("/home/aomerge/Documentos/Proyects/agents/LLM-agent")
|
|
13
|
+
if _fallback_root.exists():
|
|
14
|
+
_ROOT = _fallback_root
|
|
15
|
+
|
|
16
|
+
_CONFIG_FILE = _ROOT / "src" / "config" / "htx_config.json"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load() -> dict[str, Path]:
|
|
20
|
+
raw = json.loads(_CONFIG_FILE.read_text(encoding="utf-8")).get("paths", {})
|
|
21
|
+
return {key: _ROOT / value for key, value in raw.items()}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_PATHS: dict[str, Path] = _load()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get(key: str) -> Path:
|
|
28
|
+
"""Return the absolute Path for a named project directory."""
|
|
29
|
+
return _PATHS[key]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Convenience constants
|
|
33
|
+
CAPABILITIES_DIR: Path = _PATHS["capabilities"]
|
|
34
|
+
PROFILES_DIR: Path = _PATHS["profiles"]
|
|
35
|
+
WORKFLOWS_DIR: Path = _PATHS["workflows"]
|
|
36
|
+
HOOKS_GLOBAL_DIR: Path = _PATHS["hooks_global"]
|
|
37
|
+
HOOKS_PROFILES_DIR: Path = _PATHS["hooks_profiles"]
|
|
38
|
+
HOOKS_CUSTOM_DIR: Path = _PATHS["hooks_custom"]
|
|
39
|
+
GOVERNANCE_DIR: Path = _PATHS["governance"]
|
|
40
|
+
TEMPLATES_DIR: Path = _PATHS["templates"]
|
|
41
|
+
ENVIRONMENTS_DIR: Path = _PATHS["environments"]
|
|
42
|
+
SKILLS_TEMPLATES_DIR: Path = _PATHS["skills_templates"]
|
|
43
|
+
SUBAGENTS_TEMPLATES_DIR: Path = _PATHS["subagents_templates"]
|
|
44
|
+
PROJECT_ROOT: Path = _ROOT
|
|
45
|
+
|
|
46
|
+
# Load workspace_dir name from config
|
|
47
|
+
def _load_workspace_dir_name() -> str:
|
|
48
|
+
try:
|
|
49
|
+
data = json.loads(_CONFIG_FILE.read_text(encoding="utf-8"))
|
|
50
|
+
return data.get("htx", {}).get("workspace_dir", ".higpertext")
|
|
51
|
+
except Exception:
|
|
52
|
+
return ".higpertext"
|
|
53
|
+
|
|
54
|
+
WORKSPACE_DIR_NAME: str = _load_workspace_dir_name()
|
|
55
|
+
WORKSPACE_DIR_PATH: Path = _ROOT / WORKSPACE_DIR_NAME
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _load_file_extension() -> str:
|
|
59
|
+
try:
|
|
60
|
+
data = json.loads(_CONFIG_FILE.read_text(encoding="utf-8"))
|
|
61
|
+
return data.get("htx", {}).get("file_extension", ".json")
|
|
62
|
+
except Exception:
|
|
63
|
+
return ".json"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
FILE_EXTENSION: str = _load_file_extension()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Domain layer init
|
|
2
|
+
from higpertext.kernel.domain.governance import Rule, Severity, Scope, Verdict, Finding
|
|
3
|
+
from higpertext.kernel.domain.agent_registry import AgentRecord, SyncResult
|
|
4
|
+
from higpertext.kernel.domain.env_runtime import EnvironmentTemplate, RunSpec, RunState
|
|
5
|
+
from higpertext.kernel.domain.commit_reporter import Commit, FileChange, CommitReport, CommitType, Layer
|
|
6
|
+
from higpertext.kernel.domain.roadmap_reporter import Phase, PhaseStatus, Roadmap, RoadmapReport
|
|
7
|
+
from higpertext.kernel.domain.compilers import ICompiler
|
|
8
|
+
from higpertext.kernel.domain.profile_learner import SessionPattern, SkillProfile, score_session, classify_patterns, load_weights, save_weights
|
|
9
|
+
from higpertext.kernel.domain.context_engine import ContextPack, FileSkeleton, SymbolRef, TaskIntent, EfficiencyReport, TaskNode, TaskGraph, WindowState, load_window_state, save_window_state, reset_window_state
|
|
10
|
+
from higpertext.kernel.domain.semantic_engine import Symbol, SymbolType, Relation, RelationType, Cluster, SemanticGraph
|
|
11
|
+
|
|
12
|
+
|