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,49 @@
|
|
|
1
|
+
"""Puertos de aplicación para persistencia y carga de recursos del kernel."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from higpertext.kernel.domain.entities import Capability, Profile, Session, Workflow
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IProfileRepository(ABC):
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def load(self, name: str) -> Profile:
|
|
15
|
+
"""Carga un perfil por su nombre."""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def list_all(self) -> list[str]:
|
|
19
|
+
"""Lista los nombres de todos los perfiles disponibles."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ICapabilityRepository(ABC):
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def load(self, cap_id: str) -> Capability:
|
|
25
|
+
"""Carga una capacidad por su ID."""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def list_all(self) -> list[Capability]:
|
|
29
|
+
"""Lista todas las capacidades disponibles."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class IWorkflowRepository(ABC):
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def list_all(self) -> list[Workflow]:
|
|
35
|
+
"""Lista todos los workflows disponibles."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ISessionRepository(ABC):
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def load_active(self) -> Session | None:
|
|
41
|
+
"""Carga la sesión activa si existe."""
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def save(self, session: Session) -> None:
|
|
45
|
+
"""Guarda el estado de la sesión."""
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def delete(self) -> None:
|
|
49
|
+
"""Elimina el estado de la sesión activa."""
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Profile Learner Application layer — ProfileExtractor, ProfileUpdater, and WeightsCalibrator."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import math
|
|
5
|
+
import re
|
|
6
|
+
from collections import Counter
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from higpertext.kernel.domain.profile_learner import (
|
|
9
|
+
SessionPattern,
|
|
10
|
+
SkillProfile,
|
|
11
|
+
classify_patterns,
|
|
12
|
+
load_weights,
|
|
13
|
+
save_weights,
|
|
14
|
+
score_session,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ProfileExtractor:
|
|
19
|
+
"""Lee eventos de telemetría y extrae patrones de comportamiento del usuario."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, project_root: Path) -> None:
|
|
22
|
+
self._root = project_root
|
|
23
|
+
|
|
24
|
+
def extract(self, days: int = 30) -> tuple[SkillProfile, list[SessionPattern]]:
|
|
25
|
+
from higpertext.kernel.application.telemetry import TelemetryAggregator
|
|
26
|
+
|
|
27
|
+
agg = TelemetryAggregator(self._root)
|
|
28
|
+
metrics = agg.compute(days)
|
|
29
|
+
if not metrics:
|
|
30
|
+
return SkillProfile(), []
|
|
31
|
+
|
|
32
|
+
events = agg.load(days)
|
|
33
|
+
patterns = self._build_patterns(events, metrics)
|
|
34
|
+
return self._build_profile(patterns, metrics), patterns
|
|
35
|
+
|
|
36
|
+
def _build_patterns(self, events: list[dict], metrics: dict) -> list[SessionPattern]:
|
|
37
|
+
from collections import defaultdict
|
|
38
|
+
|
|
39
|
+
sessions: dict[str, dict] = defaultdict(
|
|
40
|
+
lambda: {
|
|
41
|
+
"duration_min": 0.0,
|
|
42
|
+
"commits": [],
|
|
43
|
+
"caps": [],
|
|
44
|
+
"intercepts": [],
|
|
45
|
+
"higpertext_direct": 0,
|
|
46
|
+
"bash_direct": 0,
|
|
47
|
+
"adoption_pct": 0.0,
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
for e in events:
|
|
52
|
+
sid = e.get("session_id", "unknown")
|
|
53
|
+
ev = e.get("event", "")
|
|
54
|
+
if ev == "session_stop":
|
|
55
|
+
sessions[sid]["duration_min"] = e.get("duration_min", 0.0)
|
|
56
|
+
elif ev == "commit":
|
|
57
|
+
sessions[sid]["commits"].append(e)
|
|
58
|
+
elif ev == "capability_used":
|
|
59
|
+
sessions[sid]["caps"].append(e.get("capability", ""))
|
|
60
|
+
elif ev == "hook_intercept":
|
|
61
|
+
sessions[sid]["intercepts"].append(e.get("capability", ""))
|
|
62
|
+
elif ev == "tool_call":
|
|
63
|
+
if e.get("is_higpertext_call"):
|
|
64
|
+
sessions[sid]["higpertext_direct"] += 1
|
|
65
|
+
else:
|
|
66
|
+
sessions[sid]["bash_direct"] += 1
|
|
67
|
+
|
|
68
|
+
patterns = []
|
|
69
|
+
for sid, s in sessions.items():
|
|
70
|
+
higpertext_count = len(s["caps"]) + len(s["intercepts"]) + s["higpertext_direct"]
|
|
71
|
+
bash = s["bash_direct"]
|
|
72
|
+
total = higpertext_count + bash
|
|
73
|
+
adoption = round(higpertext_count / total * 100, 1) if total else 0.0
|
|
74
|
+
|
|
75
|
+
commit_types = tuple(_parse_commit_type(c.get("type", "")) for c in s["commits"])
|
|
76
|
+
commit_messages = tuple(c.get("scope", "") for c in s["commits"] if c.get("scope"))
|
|
77
|
+
|
|
78
|
+
patterns.append(
|
|
79
|
+
SessionPattern(
|
|
80
|
+
session_id=sid,
|
|
81
|
+
duration_min=s["duration_min"],
|
|
82
|
+
commits=len(s["commits"]),
|
|
83
|
+
higpertext_adoption_pct=adoption,
|
|
84
|
+
bash_direct_calls=bash,
|
|
85
|
+
capability_used_calls=len(s["caps"]),
|
|
86
|
+
hook_intercept_calls=len(s["intercepts"]),
|
|
87
|
+
higpertext_direct_calls=s["higpertext_direct"],
|
|
88
|
+
commit_types=commit_types,
|
|
89
|
+
commit_messages=commit_messages,
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
return patterns
|
|
93
|
+
|
|
94
|
+
def _build_profile(self, patterns: list[SessionPattern], metrics: dict) -> SkillProfile:
|
|
95
|
+
if not patterns:
|
|
96
|
+
return SkillProfile()
|
|
97
|
+
|
|
98
|
+
all_strong: Counter = Counter()
|
|
99
|
+
all_weak: Counter = Counter()
|
|
100
|
+
cap_counter: Counter = Counter()
|
|
101
|
+
|
|
102
|
+
for p in patterns:
|
|
103
|
+
strong, weak = classify_patterns(p)
|
|
104
|
+
for s in strong:
|
|
105
|
+
all_strong[s] += 1
|
|
106
|
+
for w in weak:
|
|
107
|
+
all_weak[w] += 1
|
|
108
|
+
cap_counter.update(p.commit_messages)
|
|
109
|
+
|
|
110
|
+
all_messages = [m for p in patterns for m in p.commit_messages]
|
|
111
|
+
topics = extract_commit_topics(all_messages, top_n=5)
|
|
112
|
+
|
|
113
|
+
corr = metrics.get("correlations", {})
|
|
114
|
+
avg_adoption = sum(p.higpertext_adoption_pct for p in patterns) / len(patterns)
|
|
115
|
+
|
|
116
|
+
effective = [p for p in patterns if p.is_effective]
|
|
117
|
+
best = (
|
|
118
|
+
f"sesiones <45min con ≥2 commits ({len(effective)} de {len(patterns)})"
|
|
119
|
+
if effective
|
|
120
|
+
else "sin patrón efectivo identificado aún"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return SkillProfile(
|
|
124
|
+
strong_patterns=[k for k, _ in all_strong.most_common(5)],
|
|
125
|
+
weak_patterns=[k for k, _ in all_weak.most_common(5)],
|
|
126
|
+
preferred_caps=[cap for cap, _ in cap_counter.most_common(5)],
|
|
127
|
+
commit_topics=topics,
|
|
128
|
+
cost_per_commit_avg=corr.get("cost_per_commit_usd", 0.0),
|
|
129
|
+
higpertext_adoption_trend=round(avg_adoption, 1),
|
|
130
|
+
best_session_pattern=best,
|
|
131
|
+
total_sessions_analyzed=len(patterns),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
_DECAY = 0.8 # peso del perfil histórico vs. nuevo batch
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ProfileUpdater:
|
|
139
|
+
"""Combina perfil existente con nuevo batch. Sesiones recientes pesan más."""
|
|
140
|
+
|
|
141
|
+
def merge(self, existing: SkillProfile | None, fresh: SkillProfile) -> SkillProfile:
|
|
142
|
+
if existing is None:
|
|
143
|
+
return fresh
|
|
144
|
+
|
|
145
|
+
return SkillProfile(
|
|
146
|
+
strong_patterns=_merge_lists(existing.strong_patterns, fresh.strong_patterns),
|
|
147
|
+
weak_patterns=_merge_lists(existing.weak_patterns, fresh.weak_patterns),
|
|
148
|
+
preferred_caps=_merge_lists(existing.preferred_caps, fresh.preferred_caps),
|
|
149
|
+
commit_topics=_merge_lists(existing.commit_topics, fresh.commit_topics),
|
|
150
|
+
cost_per_commit_avg=_decay_float(
|
|
151
|
+
existing.cost_per_commit_avg, fresh.cost_per_commit_avg
|
|
152
|
+
),
|
|
153
|
+
higpertext_adoption_trend=_decay_float(
|
|
154
|
+
existing.higpertext_adoption_trend, fresh.higpertext_adoption_trend
|
|
155
|
+
),
|
|
156
|
+
best_session_pattern=fresh.best_session_pattern or existing.best_session_pattern,
|
|
157
|
+
total_sessions_analyzed=(
|
|
158
|
+
existing.total_sessions_analyzed + fresh.total_sessions_analyzed
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _decay_float(old: float, new: float) -> float:
|
|
164
|
+
return round(old * _DECAY + new * (1 - _DECAY), 4)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _merge_lists(old: list[str], new: list[str]) -> list[str]:
|
|
168
|
+
"""Une listas priorizando los del batch nuevo, sin duplicados."""
|
|
169
|
+
seen: set[str] = set()
|
|
170
|
+
result: list[str] = []
|
|
171
|
+
for item in new + old:
|
|
172
|
+
if item not in seen:
|
|
173
|
+
seen.add(item)
|
|
174
|
+
result.append(item)
|
|
175
|
+
return result[:5]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
_MIN_SESSIONS_ZSCORE = 10
|
|
179
|
+
_MIN_SESSIONS_REGRESSION = 20
|
|
180
|
+
_LEARNING_RATE = 0.01
|
|
181
|
+
_EPOCHS = 500
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class WeightsCalibrator:
|
|
185
|
+
"""Ajusta pesos de scoring usando el historial de SessionPatterns.
|
|
186
|
+
|
|
187
|
+
- < 10 sesiones : solo carga pesos existentes (sin cambios)
|
|
188
|
+
- >= 10 sesiones: calibración z-score (normaliza distribución de scores)
|
|
189
|
+
- >= 20 sesiones: regresión lineal (optimiza predicción de productividad)
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def __init__(self, project_root: Path) -> None:
|
|
193
|
+
self._root = project_root
|
|
194
|
+
|
|
195
|
+
def calibrate(self, patterns: list[SessionPattern]) -> dict:
|
|
196
|
+
n = len(patterns)
|
|
197
|
+
weights = load_weights(self._root)
|
|
198
|
+
|
|
199
|
+
if n < _MIN_SESSIONS_ZSCORE:
|
|
200
|
+
return weights
|
|
201
|
+
|
|
202
|
+
if n >= _MIN_SESSIONS_REGRESSION:
|
|
203
|
+
weights = self._regression_calibrate(patterns, weights)
|
|
204
|
+
else:
|
|
205
|
+
weights = self._zscore_calibrate(patterns, weights)
|
|
206
|
+
|
|
207
|
+
save_weights(weights, self._root)
|
|
208
|
+
return weights
|
|
209
|
+
|
|
210
|
+
def _zscore_calibrate(self, patterns: list[SessionPattern], weights: dict) -> dict:
|
|
211
|
+
"""Ajusta el multiplicador global para que el score promedio sea 0."""
|
|
212
|
+
scores = [score_session(p, weights) for p in patterns]
|
|
213
|
+
mean = sum(scores) / len(scores)
|
|
214
|
+
variance = sum((s - mean) ** 2 for s in scores) / len(scores)
|
|
215
|
+
std = math.sqrt(variance) if variance > 0 else 1.0
|
|
216
|
+
|
|
217
|
+
# Escala todos los pesos numéricos para que std ≈ 10 (rango legible)
|
|
218
|
+
target_std = 10.0
|
|
219
|
+
if std < 0.01:
|
|
220
|
+
return weights
|
|
221
|
+
scale = target_std / std
|
|
222
|
+
|
|
223
|
+
calibrated = dict(weights)
|
|
224
|
+
_SCALE_KEYS = {
|
|
225
|
+
"hook_intercept",
|
|
226
|
+
"capability_used",
|
|
227
|
+
"higpertext_direct",
|
|
228
|
+
"bash_direct_penalty",
|
|
229
|
+
"zero_commits_penalty",
|
|
230
|
+
"conventional_commit",
|
|
231
|
+
"high_adoption_bonus",
|
|
232
|
+
"effective_session_bonus",
|
|
233
|
+
}
|
|
234
|
+
for key in _SCALE_KEYS:
|
|
235
|
+
if key in calibrated and isinstance(calibrated[key], (int, float)):
|
|
236
|
+
calibrated[key] = round(calibrated[key] * scale, 4)
|
|
237
|
+
|
|
238
|
+
return calibrated
|
|
239
|
+
|
|
240
|
+
def _regression_calibrate(self, patterns: list[SessionPattern], weights: dict) -> dict:
|
|
241
|
+
"""Aprende pesos que mejor predicen commits_per_session (proxy de productividad)."""
|
|
242
|
+
features, targets = _build_feature_matrix(patterns)
|
|
243
|
+
if not features:
|
|
244
|
+
return weights
|
|
245
|
+
|
|
246
|
+
n_features = len(features[0])
|
|
247
|
+
w = [1.0] * n_features # pesos iniciales uniformes
|
|
248
|
+
n = len(features)
|
|
249
|
+
|
|
250
|
+
for _ in range(_EPOCHS):
|
|
251
|
+
grad = [0.0] * n_features
|
|
252
|
+
for x, y in zip(features, targets):
|
|
253
|
+
pred = sum(wi * xi for wi, xi in zip(w, x))
|
|
254
|
+
err = pred - y
|
|
255
|
+
for j in range(n_features):
|
|
256
|
+
grad[j] += (2 / n) * err * x[j]
|
|
257
|
+
w = [wi - _LEARNING_RATE * g for wi, g in zip(w, grad)]
|
|
258
|
+
|
|
259
|
+
calibrated = dict(weights)
|
|
260
|
+
_REGRESSION_KEYS = [
|
|
261
|
+
"hook_intercept",
|
|
262
|
+
"capability_used",
|
|
263
|
+
"higpertext_direct",
|
|
264
|
+
"bash_direct_penalty",
|
|
265
|
+
"conventional_commit",
|
|
266
|
+
]
|
|
267
|
+
for i, key in enumerate(_REGRESSION_KEYS):
|
|
268
|
+
if i < len(w) and key in calibrated:
|
|
269
|
+
calibrated[key] = round(w[i], 4)
|
|
270
|
+
|
|
271
|
+
return calibrated
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _build_feature_matrix(
|
|
275
|
+
patterns: list[SessionPattern],
|
|
276
|
+
) -> tuple[list[list[float]], list[float]]:
|
|
277
|
+
"""Construye matriz de features y vector de targets para regresión lineal simple.
|
|
278
|
+
|
|
279
|
+
Features por sesión (normalización básica):
|
|
280
|
+
0: hook_intercept_count / 10
|
|
281
|
+
1: capability_count / 10
|
|
282
|
+
2: higpertext_pct (0-1)
|
|
283
|
+
3: duration_min / 60
|
|
284
|
+
Target: commits_per_session (proxy de productividad)
|
|
285
|
+
"""
|
|
286
|
+
features: list[list[float]] = []
|
|
287
|
+
targets: list[float] = []
|
|
288
|
+
|
|
289
|
+
for p in patterns:
|
|
290
|
+
total_tools = (p.higpertext_direct_calls or 0) + (p.bash_direct_calls or 0) + 1
|
|
291
|
+
higpertext_pct = (p.higpertext_direct_calls or 0) / total_tools
|
|
292
|
+
|
|
293
|
+
feat = [
|
|
294
|
+
(p.hook_intercept_calls or 0) / 10.0,
|
|
295
|
+
(p.capability_used_calls or 0) / 10.0,
|
|
296
|
+
higpertext_pct,
|
|
297
|
+
(p.duration_min or 0.0) / 60.0,
|
|
298
|
+
]
|
|
299
|
+
features.append(feat)
|
|
300
|
+
targets.append(float(p.commits or 0))
|
|
301
|
+
|
|
302
|
+
return features, targets
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def extract_commit_topics(messages: list[str], top_n: int = 5) -> list[str]:
|
|
306
|
+
"""Extrae temas recurrentes usando TF-IDF simplificado (sin dependencias externas)."""
|
|
307
|
+
if not messages:
|
|
308
|
+
return []
|
|
309
|
+
|
|
310
|
+
docs = [_tokenize(m) for m in messages]
|
|
311
|
+
n_docs = len(docs)
|
|
312
|
+
|
|
313
|
+
tf: list[Counter] = [Counter(d) for d in docs]
|
|
314
|
+
df: Counter = Counter()
|
|
315
|
+
for doc in docs:
|
|
316
|
+
df.update(set(doc))
|
|
317
|
+
|
|
318
|
+
import math
|
|
319
|
+
|
|
320
|
+
scores: Counter = Counter()
|
|
321
|
+
for doc_tf in tf:
|
|
322
|
+
total = sum(doc_tf.values()) or 1
|
|
323
|
+
for term, count in doc_tf.items():
|
|
324
|
+
tfidf = (count / total) * math.log((n_docs + 1) / (df[term] + 1))
|
|
325
|
+
scores[term] += tfidf
|
|
326
|
+
|
|
327
|
+
return [term for term, _ in scores.most_common(top_n)]
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _tokenize(text: str) -> list[str]:
|
|
331
|
+
tokens = re.findall(r"[a-zA-Z_][a-zA-Z0-9_]{2,}", text.lower())
|
|
332
|
+
stopwords = {
|
|
333
|
+
"the",
|
|
334
|
+
"and",
|
|
335
|
+
"for",
|
|
336
|
+
"fix",
|
|
337
|
+
"add",
|
|
338
|
+
"update",
|
|
339
|
+
"feat",
|
|
340
|
+
"chore",
|
|
341
|
+
"refactor",
|
|
342
|
+
"test",
|
|
343
|
+
"docs",
|
|
344
|
+
"style",
|
|
345
|
+
"perf",
|
|
346
|
+
"build",
|
|
347
|
+
"this",
|
|
348
|
+
"with",
|
|
349
|
+
"from",
|
|
350
|
+
"that",
|
|
351
|
+
"into",
|
|
352
|
+
"when",
|
|
353
|
+
}
|
|
354
|
+
return [t for t in tokens if t not in stopwords]
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _parse_commit_type(raw: str) -> str:
|
|
358
|
+
return raw.split("(")[0].strip().lower() if raw else "unknown"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Servicio de Aplicación para resolución, validación y gestión de perfiles (DDD)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import asdict
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from higpertext.kernel.application.ports import (
|
|
10
|
+
IProfileRepository,
|
|
11
|
+
ICapabilityRepository,
|
|
12
|
+
)
|
|
13
|
+
from higpertext.kernel.domain.entities import Profile
|
|
14
|
+
from higpertext.kernel.pkg_resources import resolve_resource
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProfileApplicationService:
|
|
18
|
+
def __init__(self, profile_repo: IProfileRepository):
|
|
19
|
+
self.profile_repo = profile_repo
|
|
20
|
+
|
|
21
|
+
def get_profile_data(self, name: str) -> dict:
|
|
22
|
+
profile = self.profile_repo.load(name)
|
|
23
|
+
return asdict(profile)
|
|
24
|
+
|
|
25
|
+
def get_all_profile_names(self) -> list[str]:
|
|
26
|
+
return self.profile_repo.list_all()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ProfileManager:
|
|
30
|
+
"""Facade que conecta FileProfileRepository + ProfileApplicationService."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, profiles_dir: Path):
|
|
33
|
+
self.profiles_dir = profiles_dir
|
|
34
|
+
from higpertext.kernel.infrastructure.file_repositories import FileProfileRepository
|
|
35
|
+
|
|
36
|
+
self._repo = FileProfileRepository(profiles_dir)
|
|
37
|
+
self._service = ProfileApplicationService(self._repo)
|
|
38
|
+
|
|
39
|
+
def _get_custom_profiles_dir(self) -> Path:
|
|
40
|
+
return Path(os.getcwd()).resolve() / WORKSPACE_DIR_NAME / "profiles"
|
|
41
|
+
|
|
42
|
+
def _get_all_profile_files(self) -> list[Path]:
|
|
43
|
+
files = list(self.profiles_dir.glob("*.json"))
|
|
44
|
+
custom_dir = self._get_custom_profiles_dir()
|
|
45
|
+
if custom_dir.exists():
|
|
46
|
+
files.extend(custom_dir.glob("*.json"))
|
|
47
|
+
return files
|
|
48
|
+
|
|
49
|
+
def load_profile(self, profile_name: str) -> dict:
|
|
50
|
+
return self._service.get_profile_data(profile_name)
|
|
51
|
+
|
|
52
|
+
def list_profiles(self) -> list[str]:
|
|
53
|
+
return self._service.get_all_profile_names()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ProfileService:
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
profiles_repo: IProfileRepository,
|
|
60
|
+
caps_repo: ICapabilityRepository,
|
|
61
|
+
base_dir: Path,
|
|
62
|
+
):
|
|
63
|
+
self.profiles_repo = profiles_repo
|
|
64
|
+
self.caps_repo = caps_repo
|
|
65
|
+
self.base_dir = base_dir
|
|
66
|
+
|
|
67
|
+
def resolve_hierarchy(self, profile_name: str, visited: set[str] | None = None) -> Profile:
|
|
68
|
+
"""Resuelve recursivamente la herencia de un perfil mezclando sus campos."""
|
|
69
|
+
if visited is None:
|
|
70
|
+
visited = set()
|
|
71
|
+
|
|
72
|
+
name_lower = profile_name.lower()
|
|
73
|
+
if name_lower in visited:
|
|
74
|
+
cycle = " -> ".join(list(visited) + [profile_name])
|
|
75
|
+
raise ValueError(f"Herencia cíclica detectada en los perfiles: {cycle}")
|
|
76
|
+
|
|
77
|
+
visited.add(name_lower)
|
|
78
|
+
profile = self.profiles_repo.load(profile_name)
|
|
79
|
+
|
|
80
|
+
if not profile.extends:
|
|
81
|
+
return profile
|
|
82
|
+
|
|
83
|
+
parent = self.resolve_hierarchy(profile.extends, visited)
|
|
84
|
+
|
|
85
|
+
merged_caps = self._merge_lists(parent.capabilities, profile.capabilities)
|
|
86
|
+
merged_skills = self._merge_lists(parent.session_skills, profile.session_skills)
|
|
87
|
+
merged_subagents = self._merge_lists(parent.session_subagents, profile.session_subagents)
|
|
88
|
+
|
|
89
|
+
system_prompt = (
|
|
90
|
+
profile.system_prompt if profile.system_prompt.strip() else parent.system_prompt
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return Profile(
|
|
94
|
+
name=profile.name,
|
|
95
|
+
description=profile.description or parent.description,
|
|
96
|
+
system_prompt=system_prompt,
|
|
97
|
+
capabilities=merged_caps,
|
|
98
|
+
session_skills=merged_skills,
|
|
99
|
+
session_subagents=merged_subagents,
|
|
100
|
+
governance_access=profile.governance_access or parent.governance_access,
|
|
101
|
+
extends=profile.extends,
|
|
102
|
+
model=profile.model or parent.model,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _merge_lists(self, parent_list: list[str], child_list: list[str]) -> list[str]:
|
|
106
|
+
merged = list(parent_list)
|
|
107
|
+
for item in child_list:
|
|
108
|
+
if item not in merged:
|
|
109
|
+
merged.append(item)
|
|
110
|
+
return merged
|
|
111
|
+
|
|
112
|
+
def validate_profile(self, profile_name: str) -> tuple[bool, list[str], list[str]]:
|
|
113
|
+
"""Valida la estructura, integridad, dependencias y saturación de contexto de un perfil."""
|
|
114
|
+
errors = []
|
|
115
|
+
warnings = []
|
|
116
|
+
try:
|
|
117
|
+
resolved = self.resolve_hierarchy(profile_name)
|
|
118
|
+
except (FileNotFoundError, ValueError) as e:
|
|
119
|
+
return False, [str(e)], []
|
|
120
|
+
|
|
121
|
+
self._validate_capabilities(resolved.capabilities, errors)
|
|
122
|
+
|
|
123
|
+
total_chars = len(resolved.system_prompt)
|
|
124
|
+
total_chars += self._validate_skills(resolved.session_skills, errors)
|
|
125
|
+
total_chars += self._validate_subagents(resolved.session_subagents, errors)
|
|
126
|
+
|
|
127
|
+
self._validate_context_saturation(total_chars, warnings)
|
|
128
|
+
|
|
129
|
+
return len(errors) == 0, errors, warnings
|
|
130
|
+
|
|
131
|
+
def _validate_capabilities(self, capabilities: list[str], errors: list[str]) -> None:
|
|
132
|
+
for cap_id in capabilities:
|
|
133
|
+
try:
|
|
134
|
+
self.caps_repo.load(cap_id)
|
|
135
|
+
except FileNotFoundError:
|
|
136
|
+
errors.append(f"Capacidad '{cap_id}' referenciada no encontrada en el repositorio.")
|
|
137
|
+
|
|
138
|
+
def _validate_skills(self, skills: list[str], errors: list[str]) -> int:
|
|
139
|
+
total_chars = 0
|
|
140
|
+
skills_dirs = self._resolve_template_dirs("skills")
|
|
141
|
+
for skill in skills:
|
|
142
|
+
matches = self._find_in_dirs(skills_dirs, f"{skill}.json")
|
|
143
|
+
if not matches:
|
|
144
|
+
errors.append(
|
|
145
|
+
f"Skill '{skill}' referenciada no tiene plantilla en templates/skills."
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
try:
|
|
149
|
+
s_data = json.loads(matches[0].read_text(encoding="utf-8"))
|
|
150
|
+
total_chars += len(s_data.get("content", ""))
|
|
151
|
+
except Exception: # nosec B110
|
|
152
|
+
pass
|
|
153
|
+
return total_chars
|
|
154
|
+
|
|
155
|
+
def _validate_subagents(self, subagents: list[str], errors: list[str]) -> int:
|
|
156
|
+
total_chars = 0
|
|
157
|
+
subagents_dirs = self._resolve_template_dirs("subagents")
|
|
158
|
+
for subagent in subagents:
|
|
159
|
+
matches = self._find_in_dirs(subagents_dirs, f"{subagent}.json")
|
|
160
|
+
if not matches:
|
|
161
|
+
errors.append(
|
|
162
|
+
f"Subagent '{subagent}' referenciado no tiene"
|
|
163
|
+
" plantilla en templates/subagents."
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
try:
|
|
167
|
+
total_chars += len(matches[0].read_text(encoding="utf-8"))
|
|
168
|
+
except Exception: # nosec B110
|
|
169
|
+
pass
|
|
170
|
+
return total_chars
|
|
171
|
+
|
|
172
|
+
def _validate_context_saturation(self, total_chars: int, warnings: list[str]) -> None:
|
|
173
|
+
if total_chars > 50000:
|
|
174
|
+
warnings.append(
|
|
175
|
+
f"Saturación crítica de contexto: El tamaño estimado"
|
|
176
|
+
f" del perfil ({total_chars} chars) "
|
|
177
|
+
"es demasiado alto y degradará el rendimiento del LLM."
|
|
178
|
+
)
|
|
179
|
+
elif total_chars > 25000:
|
|
180
|
+
warnings.append(
|
|
181
|
+
f"Riesgo de saturación de contexto: El tamaño estimado"
|
|
182
|
+
f" del perfil ({total_chars} chars) "
|
|
183
|
+
"es alto y podría diluir la atención del LLM."
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def _resolve_template_dirs(self, subdir: str) -> list[Path]:
|
|
187
|
+
"""Retorna lista de directorios de templates: core primero, luego agente externo."""
|
|
188
|
+
dirs = [resolve_resource(self.base_dir, "src", "higpertext", "templates", subdir)]
|
|
189
|
+
agent_templates = self.base_dir / WORKSPACE_DIR_NAME / "agent_templates" / subdir
|
|
190
|
+
if agent_templates.exists():
|
|
191
|
+
dirs.append(agent_templates)
|
|
192
|
+
return dirs
|
|
193
|
+
|
|
194
|
+
def _find_in_dirs(self, dirs: list[Path], filename: str) -> list[Path]:
|
|
195
|
+
"""Busca filename (o skill.json en subdirectorio homónimo) en múltiples directorios."""
|
|
196
|
+
matches: list[Path] = []
|
|
197
|
+
stem = Path(filename).stem # e.g. "clean-code"
|
|
198
|
+
for d in dirs:
|
|
199
|
+
if not d.exists():
|
|
200
|
+
continue
|
|
201
|
+
matches.extend(d.rglob(filename))
|
|
202
|
+
subdir_skill = d / stem / "skill.json"
|
|
203
|
+
if subdir_skill.exists():
|
|
204
|
+
matches.append(subdir_skill)
|
|
205
|
+
return matches
|