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,198 @@
|
|
|
1
|
+
"""Entidades y Value Objects de Gobernanza (Dominio)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from datetime import date
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Severity(str, Enum):
|
|
11
|
+
CRITICAL = "critical" # inmutable — no override posible
|
|
12
|
+
HIGH = "high" # solo puede endurecerse
|
|
13
|
+
MEDIUM = "medium" # solo puede endurecerse
|
|
14
|
+
LOW = "low" # override bidireccional con justificación
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Scope(str, Enum):
|
|
18
|
+
COMMIT = "commit"
|
|
19
|
+
PR = "pr"
|
|
20
|
+
DEPLOY = "deploy"
|
|
21
|
+
ANY = "any" # aplica en todos los scopes
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class Rule:
|
|
26
|
+
"""Unidad atómica de gobernanza.
|
|
27
|
+
|
|
28
|
+
Una regla es inmutable una vez creada. Los overrides de perfil
|
|
29
|
+
producen nuevas instancias de Rule, no modifican la original.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
description: str
|
|
34
|
+
severity: Severity
|
|
35
|
+
scopes: tuple[Scope, ...]
|
|
36
|
+
automated: bool = False
|
|
37
|
+
capability: Optional[str] = None # capability que valida esta regla
|
|
38
|
+
source: str = "global" # "global" | nombre del perfil
|
|
39
|
+
numeric_threshold: Optional[float] = None # para reglas cuantitativas
|
|
40
|
+
|
|
41
|
+
def applies_to(self, scope: Scope) -> bool:
|
|
42
|
+
return Scope.ANY in self.scopes or scope in self.scopes
|
|
43
|
+
|
|
44
|
+
def is_overridable_by(self, other_severity: Severity) -> bool:
|
|
45
|
+
"""Una regla CRITICAL nunca es overridable. HIGH/MEDIUM solo pueden endurecerse."""
|
|
46
|
+
if self.severity == Severity.CRITICAL:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
severity_order = {
|
|
50
|
+
Severity.LOW: 1,
|
|
51
|
+
Severity.MEDIUM: 2,
|
|
52
|
+
Severity.HIGH: 3,
|
|
53
|
+
Severity.CRITICAL: 4,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return severity_order[other_severity] >= severity_order[self.severity]
|
|
57
|
+
|
|
58
|
+
def merge(self, override: "RuleOverride") -> "Rule":
|
|
59
|
+
"""Produce una nueva Rule aplicando el override si es válido."""
|
|
60
|
+
if self.severity == Severity.CRITICAL:
|
|
61
|
+
return self # CRITICAL no admite cambios
|
|
62
|
+
|
|
63
|
+
new_threshold = override.numeric_threshold
|
|
64
|
+
if self.severity in (Severity.HIGH, Severity.MEDIUM):
|
|
65
|
+
# Solo permite endurecer (threshold más alto = más estricto)
|
|
66
|
+
if (
|
|
67
|
+
self.numeric_threshold is not None
|
|
68
|
+
and new_threshold is not None
|
|
69
|
+
and new_threshold < self.numeric_threshold
|
|
70
|
+
):
|
|
71
|
+
return self # rechaza aflojar
|
|
72
|
+
|
|
73
|
+
return Rule(
|
|
74
|
+
id=self.id,
|
|
75
|
+
description=override.description or self.description,
|
|
76
|
+
severity=self.severity,
|
|
77
|
+
scopes=self.scopes,
|
|
78
|
+
automated=self.automated,
|
|
79
|
+
capability=self.capability,
|
|
80
|
+
source=override.source,
|
|
81
|
+
numeric_threshold=(
|
|
82
|
+
new_threshold if new_threshold is not None else self.numeric_threshold
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True)
|
|
88
|
+
class RuleOverride:
|
|
89
|
+
"""Instrucción de override desde governance.json de un perfil."""
|
|
90
|
+
|
|
91
|
+
rule_id: str
|
|
92
|
+
source: str
|
|
93
|
+
numeric_threshold: Optional[float] = None
|
|
94
|
+
description: Optional[str] = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True)
|
|
98
|
+
class GovernanceException:
|
|
99
|
+
"""Excepción aprobada que suspende temporalmente una regla para un perfil."""
|
|
100
|
+
|
|
101
|
+
rule_id: str
|
|
102
|
+
reason: str
|
|
103
|
+
approver: str
|
|
104
|
+
profile: str
|
|
105
|
+
expires: Optional[date] = None
|
|
106
|
+
created_at: Optional[str] = None
|
|
107
|
+
|
|
108
|
+
def is_active(self) -> bool:
|
|
109
|
+
if self.expires is None:
|
|
110
|
+
return True
|
|
111
|
+
return date.today() <= self.expires
|
|
112
|
+
|
|
113
|
+
def to_dict(self) -> dict:
|
|
114
|
+
return {
|
|
115
|
+
"rule_id": self.rule_id,
|
|
116
|
+
"reason": self.reason,
|
|
117
|
+
"approver": self.approver,
|
|
118
|
+
"profile": self.profile,
|
|
119
|
+
"expires": self.expires.isoformat() if self.expires else None,
|
|
120
|
+
"created_at": self.created_at,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def from_dict(cls, data: dict) -> "GovernanceException":
|
|
125
|
+
expires_raw = data.get("expires")
|
|
126
|
+
expires = date.fromisoformat(expires_raw) if expires_raw else None
|
|
127
|
+
return cls(
|
|
128
|
+
rule_id=data["rule_id"],
|
|
129
|
+
reason=data.get("reason", ""),
|
|
130
|
+
approver=data.get("approver", "unknown"),
|
|
131
|
+
profile=data.get("profile", "global"),
|
|
132
|
+
expires=expires,
|
|
133
|
+
created_at=data.get("created_at"),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class VerdictStatus(str, Enum):
|
|
138
|
+
PASSED = "passed"
|
|
139
|
+
WARN = "warn"
|
|
140
|
+
BLOCK = "block"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(frozen=True)
|
|
144
|
+
class Finding:
|
|
145
|
+
"""Un hallazgo individual de una regla evaluada."""
|
|
146
|
+
|
|
147
|
+
rule_id: str
|
|
148
|
+
severity: Severity
|
|
149
|
+
message: str
|
|
150
|
+
source: str = "global"
|
|
151
|
+
detail: Optional[str] = None
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def blocks(self) -> bool:
|
|
155
|
+
return self.severity == Severity.CRITICAL
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class Verdict:
|
|
160
|
+
"""Resultado agregado de evaluar un conjunto de reglas."""
|
|
161
|
+
|
|
162
|
+
scope: str
|
|
163
|
+
findings: list[Finding] = field(default_factory=list)
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def status(self) -> VerdictStatus:
|
|
167
|
+
if any(f.blocks for f in self.findings):
|
|
168
|
+
return VerdictStatus.BLOCK
|
|
169
|
+
if self.findings:
|
|
170
|
+
return VerdictStatus.WARN
|
|
171
|
+
return VerdictStatus.PASSED
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def blocking_findings(self) -> list[Finding]:
|
|
175
|
+
return [f for f in self.findings if f.blocks]
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def warning_findings(self) -> list[Finding]:
|
|
179
|
+
return [f for f in self.findings if not f.blocks]
|
|
180
|
+
|
|
181
|
+
def add(self, finding: Finding) -> None:
|
|
182
|
+
self.findings.append(finding)
|
|
183
|
+
|
|
184
|
+
def to_dict(self) -> dict:
|
|
185
|
+
return {
|
|
186
|
+
"scope": self.scope,
|
|
187
|
+
"status": self.status.value,
|
|
188
|
+
"findings": [
|
|
189
|
+
{
|
|
190
|
+
"rule_id": f.rule_id,
|
|
191
|
+
"severity": f.severity.value,
|
|
192
|
+
"message": f.message,
|
|
193
|
+
"source": f.source,
|
|
194
|
+
"detail": f.detail,
|
|
195
|
+
}
|
|
196
|
+
for f in self.findings
|
|
197
|
+
],
|
|
198
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Modelos de dominio para el sistema de hooks de higpertext."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class HookDefinition:
|
|
9
|
+
id: str
|
|
10
|
+
event: str
|
|
11
|
+
script: str # ruta relativa al script Python
|
|
12
|
+
description: str = ""
|
|
13
|
+
matcher: str = "" # filtro de herramienta, vacío = todas
|
|
14
|
+
timeout: int = 10
|
|
15
|
+
enabled: bool = True
|
|
16
|
+
assistants: list[str] = field(default_factory=list)
|
|
17
|
+
profiles: list[str] = field(default_factory=list)
|
|
18
|
+
capability_id: str = "" # capability higpertext que origina este hook
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class WebhookEvent:
|
|
23
|
+
id: str
|
|
24
|
+
event: str
|
|
25
|
+
url: str # soporta ${ENV_VAR}
|
|
26
|
+
payload_template: dict = field(default_factory=dict)
|
|
27
|
+
assistants: list[str] = field(default_factory=list)
|
|
28
|
+
enabled: bool = False # desactivado por defecto — requiere opt-in explícito
|
|
29
|
+
timeout: int = 5
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Profile Learner Domain layer — SessionPattern, SkillProfile, and scoring logic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class SessionPattern:
|
|
13
|
+
session_id: str
|
|
14
|
+
duration_min: float
|
|
15
|
+
commits: int
|
|
16
|
+
higpertext_adoption_pct: float
|
|
17
|
+
bash_direct_calls: int
|
|
18
|
+
capability_used_calls: int
|
|
19
|
+
hook_intercept_calls: int
|
|
20
|
+
higpertext_direct_calls: int
|
|
21
|
+
commit_types: tuple[str, ...] = field(default_factory=tuple)
|
|
22
|
+
commit_messages: tuple[str, ...] = field(default_factory=tuple)
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def is_effective(self) -> bool:
|
|
26
|
+
return self.duration_min <= 45 and self.commits >= 2
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def tool_calls_total(self) -> int:
|
|
30
|
+
return (
|
|
31
|
+
self.bash_direct_calls
|
|
32
|
+
+ self.higpertext_direct_calls
|
|
33
|
+
+ self.hook_intercept_calls
|
|
34
|
+
+ self.capability_used_calls
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class SkillProfile:
|
|
40
|
+
strong_patterns: list[str] = field(default_factory=list)
|
|
41
|
+
weak_patterns: list[str] = field(default_factory=list)
|
|
42
|
+
preferred_caps: list[str] = field(default_factory=list)
|
|
43
|
+
commit_topics: list[str] = field(default_factory=list)
|
|
44
|
+
cost_per_commit_avg: float = 0.0
|
|
45
|
+
higpertext_adoption_trend: float = 0.0
|
|
46
|
+
best_session_pattern: str = ""
|
|
47
|
+
total_sessions_analyzed: int = 0
|
|
48
|
+
last_updated: str = field(
|
|
49
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat(timespec="seconds")
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> dict:
|
|
53
|
+
return {
|
|
54
|
+
"strong_patterns": self.strong_patterns,
|
|
55
|
+
"weak_patterns": self.weak_patterns,
|
|
56
|
+
"preferred_caps": self.preferred_caps,
|
|
57
|
+
"commit_topics": self.commit_topics,
|
|
58
|
+
"cost_per_commit_avg": self.cost_per_commit_avg,
|
|
59
|
+
"higpertext_adoption_trend": self.higpertext_adoption_trend,
|
|
60
|
+
"best_session_pattern": self.best_session_pattern,
|
|
61
|
+
"total_sessions_analyzed": self.total_sessions_analyzed,
|
|
62
|
+
"last_updated": self.last_updated,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_dict(cls, data: dict) -> SkillProfile:
|
|
67
|
+
return cls(
|
|
68
|
+
strong_patterns=data.get("strong_patterns", []),
|
|
69
|
+
weak_patterns=data.get("weak_patterns", []),
|
|
70
|
+
preferred_caps=data.get("preferred_caps", []),
|
|
71
|
+
commit_topics=data.get("commit_topics", []),
|
|
72
|
+
cost_per_commit_avg=data.get("cost_per_commit_avg", 0.0),
|
|
73
|
+
higpertext_adoption_trend=data.get("higpertext_adoption_trend", 0.0),
|
|
74
|
+
best_session_pattern=data.get("best_session_pattern", ""),
|
|
75
|
+
total_sessions_analyzed=data.get("total_sessions_analyzed", 0),
|
|
76
|
+
last_updated=data.get("last_updated", ""),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
_PL_CONFIG_PATH = Path(__file__).resolve().parents[4] / "src" / "config" / "profile_learner.json"
|
|
81
|
+
_PL_CFG: dict = json.loads(_PL_CONFIG_PATH.read_text(encoding="utf-8"))
|
|
82
|
+
|
|
83
|
+
_CONVENTIONAL_TYPES = frozenset(_PL_CFG["conventional_commit_types"])
|
|
84
|
+
_DEFAULTS: dict = dict(_PL_CFG["scoring_defaults"])
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def load_weights(project_root: Path | None = None) -> dict:
|
|
88
|
+
"""Carga pesos desde scoring_weights.json; usa defaults si no existe."""
|
|
89
|
+
if project_root is None:
|
|
90
|
+
project_root = _resolve_root()
|
|
91
|
+
path = project_root / WORKSPACE_DIR_NAME / "config" / "scoring_weights.json"
|
|
92
|
+
try:
|
|
93
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
94
|
+
return {**_DEFAULTS, **{k: v for k, v in data.items() if not k.startswith("_")}}
|
|
95
|
+
except (OSError, json.JSONDecodeError):
|
|
96
|
+
return dict(_DEFAULTS)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def save_weights(weights: dict, project_root: Path | None = None) -> None:
|
|
100
|
+
"""Persiste pesos actualizados en scoring_weights.json."""
|
|
101
|
+
if project_root is None:
|
|
102
|
+
project_root = _resolve_root()
|
|
103
|
+
path = project_root / WORKSPACE_DIR_NAME / "config" / "scoring_weights.json"
|
|
104
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
current: dict = {}
|
|
106
|
+
try:
|
|
107
|
+
current = json.loads(path.read_text(encoding="utf-8"))
|
|
108
|
+
except (OSError, json.JSONDecodeError): # nosec B110
|
|
109
|
+
pass
|
|
110
|
+
comment = current.get("_comment", "")
|
|
111
|
+
out = {"_comment": comment, **weights} if comment else dict(weights)
|
|
112
|
+
path.write_text(json.dumps(out, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def score_session(pattern: SessionPattern, weights: dict | None = None) -> float:
|
|
116
|
+
"""Calcula score numérico para una sesión. Positivo = buena sesión."""
|
|
117
|
+
w = weights or load_weights()
|
|
118
|
+
score = 0.0
|
|
119
|
+
|
|
120
|
+
score += pattern.hook_intercept_calls * w["hook_intercept"]
|
|
121
|
+
score += pattern.capability_used_calls * w["capability_used"]
|
|
122
|
+
score += pattern.higpertext_direct_calls * w["higpertext_direct"]
|
|
123
|
+
score += pattern.bash_direct_calls * w["bash_direct_penalty"]
|
|
124
|
+
|
|
125
|
+
if pattern.commits == 0:
|
|
126
|
+
score += w["zero_commits_penalty"]
|
|
127
|
+
|
|
128
|
+
conventional_commits = sum(1 for t in pattern.commit_types if t in _CONVENTIONAL_TYPES)
|
|
129
|
+
score += conventional_commits * w["conventional_commit"]
|
|
130
|
+
|
|
131
|
+
if pattern.higpertext_adoption_pct >= w["high_adoption_threshold"]:
|
|
132
|
+
score += w["high_adoption_bonus"]
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
pattern.duration_min <= w["effective_session_max_min"]
|
|
136
|
+
and pattern.commits >= w["effective_session_min_commits"]
|
|
137
|
+
):
|
|
138
|
+
score += w["effective_session_bonus"]
|
|
139
|
+
|
|
140
|
+
return score
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def classify_patterns(
|
|
144
|
+
pattern: SessionPattern, weights: dict | None = None
|
|
145
|
+
) -> tuple[list[str], list[str]]:
|
|
146
|
+
"""Retorna (strong_patterns, weak_patterns) observados en la sesión."""
|
|
147
|
+
w = weights or load_weights()
|
|
148
|
+
strong: list[str] = []
|
|
149
|
+
weak: list[str] = []
|
|
150
|
+
|
|
151
|
+
if pattern.higpertext_adoption_pct >= w["high_adoption_threshold"]:
|
|
152
|
+
strong.append("alta adopción higpertext")
|
|
153
|
+
elif pattern.higpertext_adoption_pct < 40:
|
|
154
|
+
weak.append("baja adopción higpertext")
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
pattern.duration_min <= w["effective_session_max_min"]
|
|
158
|
+
and pattern.commits >= w["effective_session_min_commits"]
|
|
159
|
+
):
|
|
160
|
+
strong.append("sesiones cortas y productivas")
|
|
161
|
+
|
|
162
|
+
if pattern.commits >= 3:
|
|
163
|
+
strong.append("commits frecuentes")
|
|
164
|
+
elif pattern.commits == 0:
|
|
165
|
+
weak.append("sesiones sin commits")
|
|
166
|
+
|
|
167
|
+
if pattern.bash_direct_calls > 10:
|
|
168
|
+
weak.append(f"bash directo excesivo ({pattern.bash_direct_calls} llamadas)")
|
|
169
|
+
|
|
170
|
+
conventional = sum(1 for t in pattern.commit_types if t in _CONVENTIONAL_TYPES)
|
|
171
|
+
if conventional == len(pattern.commit_types) and pattern.commits > 0:
|
|
172
|
+
strong.append("commits convencionales correctos")
|
|
173
|
+
|
|
174
|
+
return strong, weak
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _resolve_root() -> Path:
|
|
178
|
+
candidate = Path(__file__).resolve()
|
|
179
|
+
for _ in range(8):
|
|
180
|
+
if (candidate / "src/config/htx_config.json").exists():
|
|
181
|
+
return candidate
|
|
182
|
+
candidate = candidate.parent
|
|
183
|
+
_fallback = Path("/home/aomerge/Documentos/Proyects/agents/LLM-agent")
|
|
184
|
+
if (_fallback / "src/config/htx_config.json").exists():
|
|
185
|
+
return _fallback
|
|
186
|
+
return Path.cwd()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Semantic RAG Domain Entities and Interfaces (Domain Layer)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class DocumentChunk:
|
|
10
|
+
"""Fragmento de documento o código fuente con metadatos asociados."""
|
|
11
|
+
|
|
12
|
+
file: str
|
|
13
|
+
content: str
|
|
14
|
+
chunk_type: str # "class" | "function" | "markdown" | "general"
|
|
15
|
+
start_line: int
|
|
16
|
+
end_line: int
|
|
17
|
+
metadata: dict = field(default_factory=dict)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class VectorEntry:
|
|
22
|
+
"""Entrada que vincula un fragmento de texto con su vector de embedding."""
|
|
23
|
+
|
|
24
|
+
chunk: DocumentChunk
|
|
25
|
+
embedding: list[float]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class IEmbeddingProvider(ABC):
|
|
29
|
+
"""Interfaz para la generación de embeddings vectoriales."""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_embedding(self, text: str) -> list[float]:
|
|
33
|
+
"""Calcula el vector de embedding para un texto dado."""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def get_embeddings_batch(self, texts: list[str]) -> list[list[float]]:
|
|
38
|
+
"""Calcula embeddings para una lista de textos en lote."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class IVectorStore(ABC):
|
|
43
|
+
"""Interfaz para persistencia y búsqueda por similitud de vectores."""
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def add_entries(self, entries: list[VectorEntry]) -> None:
|
|
47
|
+
"""Agrega un lote de entradas vectoriales al almacén."""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def search(
|
|
52
|
+
self, query_vector: list[float], limit: int = 5
|
|
53
|
+
) -> list[tuple[DocumentChunk, float]]:
|
|
54
|
+
"""Busca los fragmentos más similares ordenados por score (similitud coseno)."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def clear(self) -> None:
|
|
59
|
+
"""Limpia todo el contenido del almacén."""
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def save(self) -> None:
|
|
64
|
+
"""Persiste el estado actual de los vectores."""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def load(self) -> None:
|
|
69
|
+
"""Carga el estado de los vectores guardados."""
|
|
70
|
+
pass
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PhaseStatus(str, Enum):
|
|
7
|
+
PENDING = "pending"
|
|
8
|
+
ACTIVE = "active"
|
|
9
|
+
DONE = "done"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class Phase:
|
|
14
|
+
id: str
|
|
15
|
+
name: str
|
|
16
|
+
description: str
|
|
17
|
+
status: PhaseStatus
|
|
18
|
+
skills: list[str] = field(default_factory=list)
|
|
19
|
+
subagents: list[str] = field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def is_done(self) -> bool:
|
|
23
|
+
return self.status == PhaseStatus.DONE
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def is_active(self) -> bool:
|
|
27
|
+
return self.status == PhaseStatus.ACTIVE
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Roadmap:
|
|
32
|
+
id: str
|
|
33
|
+
name: str
|
|
34
|
+
description: str
|
|
35
|
+
phases: list[Phase] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class RoadmapReport:
|
|
40
|
+
roadmap: Roadmap
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def total_phases(self) -> int:
|
|
44
|
+
return len(self.roadmap.phases)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def done_phases(self) -> int:
|
|
48
|
+
return sum(1 for p in self.roadmap.phases if p.is_done)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def completion_pct(self) -> float:
|
|
52
|
+
if not self.roadmap.phases:
|
|
53
|
+
return 0.0
|
|
54
|
+
return round(self.done_phases / self.total_phases * 100, 2)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def active_phase(self) -> Phase | None:
|
|
58
|
+
return next((p for p in self.roadmap.phases if p.is_active), None)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def all_skills_used(self) -> list[str]:
|
|
62
|
+
seen: set[str] = set()
|
|
63
|
+
result = []
|
|
64
|
+
for p in self.roadmap.phases:
|
|
65
|
+
for s in p.skills:
|
|
66
|
+
if s not in seen:
|
|
67
|
+
seen.add(s)
|
|
68
|
+
result.append(s)
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def all_subagents_used(self) -> list[str]:
|
|
73
|
+
seen: set[str] = set()
|
|
74
|
+
result = []
|
|
75
|
+
for p in self.roadmap.phases:
|
|
76
|
+
for a in p.subagents:
|
|
77
|
+
if a not in seen:
|
|
78
|
+
seen.add(a)
|
|
79
|
+
result.append(a)
|
|
80
|
+
return result
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Semantic Engine Domain layer — Symbol, Relation, Cluster, and SemanticGraph."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SymbolType(str, Enum):
|
|
10
|
+
CLASS = "class"
|
|
11
|
+
FUNCTION = "function"
|
|
12
|
+
METHOD = "method"
|
|
13
|
+
VARIABLE = "variable"
|
|
14
|
+
MODULE = "module"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class Symbol:
|
|
19
|
+
name: str
|
|
20
|
+
type: SymbolType
|
|
21
|
+
file: str
|
|
22
|
+
line: int
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def fqn(self) -> str:
|
|
26
|
+
return f"{self.file}::{self.name}"
|
|
27
|
+
|
|
28
|
+
def __hash__(self) -> int:
|
|
29
|
+
return hash(self.fqn)
|
|
30
|
+
|
|
31
|
+
def __eq__(self, other: object) -> bool:
|
|
32
|
+
if not isinstance(other, Symbol):
|
|
33
|
+
return NotImplemented
|
|
34
|
+
return self.fqn == other.fqn
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RelationType(str, Enum):
|
|
38
|
+
CALLS = "calls"
|
|
39
|
+
IMPORTS = "imports"
|
|
40
|
+
INHERITS = "inherits"
|
|
41
|
+
USES = "uses"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class Relation:
|
|
46
|
+
source: Symbol
|
|
47
|
+
target: Symbol
|
|
48
|
+
type: RelationType
|
|
49
|
+
confidence: float = field(default=1.0)
|
|
50
|
+
|
|
51
|
+
def __post_init__(self) -> None:
|
|
52
|
+
if not (0.0 <= self.confidence <= 1.0):
|
|
53
|
+
raise ValueError(f"confidence must be in [0, 1], got {self.confidence}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class Cluster:
|
|
58
|
+
id: str
|
|
59
|
+
members: tuple[Symbol, ...]
|
|
60
|
+
|
|
61
|
+
def __init__(self, id: str, members: list[Symbol]) -> None:
|
|
62
|
+
object.__setattr__(self, "id", id)
|
|
63
|
+
object.__setattr__(self, "members", tuple(members))
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def size(self) -> int:
|
|
67
|
+
return len(self.members)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SemanticGraph:
|
|
71
|
+
def __init__(self) -> None:
|
|
72
|
+
self._symbols: dict[str, Symbol] = {}
|
|
73
|
+
self._relations: list[Relation] = []
|
|
74
|
+
self._adjacency: dict[str, list[Symbol]] = defaultdict(list)
|
|
75
|
+
self._in_degree: dict[str, int] = defaultdict(int)
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def symbol_count(self) -> int:
|
|
79
|
+
return len(self._symbols)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def relation_count(self) -> int:
|
|
83
|
+
return len(self._relations)
|
|
84
|
+
|
|
85
|
+
def add_symbol(self, symbol: Symbol) -> None:
|
|
86
|
+
self._symbols.setdefault(symbol.fqn, symbol)
|
|
87
|
+
|
|
88
|
+
def add_relation(self, relation: Relation) -> None:
|
|
89
|
+
self.add_symbol(relation.source)
|
|
90
|
+
self.add_symbol(relation.target)
|
|
91
|
+
self._relations.append(relation)
|
|
92
|
+
self._adjacency[relation.source.fqn].append(relation.target)
|
|
93
|
+
self._in_degree[relation.target.fqn] += 1
|
|
94
|
+
|
|
95
|
+
def neighbors(self, symbol: Symbol) -> list[Symbol]:
|
|
96
|
+
return list(self._adjacency.get(symbol.fqn, []))
|
|
97
|
+
|
|
98
|
+
def god_nodes(self, threshold: int = 5) -> list[Symbol]:
|
|
99
|
+
return [
|
|
100
|
+
self._symbols[fqn] for fqn, degree in self._in_degree.items() if degree >= threshold
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
def all_symbols(self) -> list[Symbol]:
|
|
104
|
+
return list(self._symbols.values())
|
|
105
|
+
|
|
106
|
+
def all_relations(self) -> list[Relation]:
|
|
107
|
+
return list(self._relations)
|