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,175 @@
|
|
|
1
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
2
|
+
from higpertext.kernel.infrastructure.capability_helper import CapabilityHelper
|
|
3
|
+
from higpertext.kernel.config_paths import (
|
|
4
|
+
CAPABILITIES_DIR,
|
|
5
|
+
WORKFLOWS_DIR,
|
|
6
|
+
PROFILES_DIR,
|
|
7
|
+
PROJECT_ROOT as ROOT,
|
|
8
|
+
)
|
|
9
|
+
import sys
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
14
|
+
_log = get_logger()
|
|
15
|
+
|
|
16
|
+
# Calcular ROOT para bootstrap de sys.path
|
|
17
|
+
_ROOT = Path(__file__).resolve()
|
|
18
|
+
while _ROOT.name != "LLM-agent" and len(_ROOT.parents) > 0:
|
|
19
|
+
_ROOT = _ROOT.parent
|
|
20
|
+
|
|
21
|
+
COMPILED_WORKFLOWS_DIR = ROOT / WORKSPACE_DIR_NAME / "workflows" / "compiled"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
PINNED_CAPABILITIES = [
|
|
25
|
+
"common.grep-search",
|
|
26
|
+
"common.knowledge-asker",
|
|
27
|
+
"ado_admin.git-diff",
|
|
28
|
+
"common.code-skeletonizer",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_profile_capabilities(profile_name: str) -> list[str]:
|
|
33
|
+
profile_file = PROFILES_DIR / f"{profile_name}.json"
|
|
34
|
+
if not profile_file.exists():
|
|
35
|
+
_log.warning(f"[!] Perfil no encontrado: {profile_file}")
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
data = json.loads(profile_file.read_text(encoding="utf-8"))
|
|
38
|
+
return data.get("capabilities", [])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_capability_meta(cap_id: str) -> dict | None:
|
|
42
|
+
if "." not in cap_id:
|
|
43
|
+
return None
|
|
44
|
+
namespace, short_id = cap_id.split(".", 1)
|
|
45
|
+
cap_file = CAPABILITIES_DIR / namespace / f"{short_id}.json"
|
|
46
|
+
if not cap_file.exists():
|
|
47
|
+
return None
|
|
48
|
+
try:
|
|
49
|
+
return json.loads(cap_file.read_text(encoding="utf-8"))
|
|
50
|
+
except json.JSONDecodeError:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_compiled_workflows() -> list[dict]:
|
|
55
|
+
workflows_dir = WORKFLOWS_DIR
|
|
56
|
+
if not workflows_dir.exists():
|
|
57
|
+
return []
|
|
58
|
+
workflows = []
|
|
59
|
+
for wf_file in sorted(workflows_dir.glob("*.json")):
|
|
60
|
+
try:
|
|
61
|
+
data = json.loads(wf_file.read_text(encoding="utf-8"))
|
|
62
|
+
if data.get("id"):
|
|
63
|
+
workflows.append(data)
|
|
64
|
+
except json.JSONDecodeError: # nosec B110
|
|
65
|
+
pass
|
|
66
|
+
return workflows
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def print_capabilities(capability_ids: list[str]) -> None:
|
|
70
|
+
col_ns = 16
|
|
71
|
+
col_id = 24
|
|
72
|
+
col_desc = 48
|
|
73
|
+
col_flag = 7
|
|
74
|
+
|
|
75
|
+
header = f"{'NAMESPACE':<{col_ns}} {'ID':<{col_id}} {'FLAG':<{col_flag}} {'DESCRIPCIÓN'}"
|
|
76
|
+
separator = "-" * (col_ns + col_id + col_flag + col_desc + 3)
|
|
77
|
+
|
|
78
|
+
_log.info(separator)
|
|
79
|
+
_log.info(header)
|
|
80
|
+
_log.info(separator)
|
|
81
|
+
|
|
82
|
+
missing = []
|
|
83
|
+
for cap_id in capability_ids:
|
|
84
|
+
if "." not in cap_id:
|
|
85
|
+
continue
|
|
86
|
+
namespace, short_id = cap_id.split(".", 1)
|
|
87
|
+
meta = load_capability_meta(cap_id)
|
|
88
|
+
flag = "[FIJA]" if cap_id in PINNED_CAPABILITIES else ""
|
|
89
|
+
if meta is None:
|
|
90
|
+
missing.append(cap_id)
|
|
91
|
+
desc = "(JSON no encontrado)"
|
|
92
|
+
else:
|
|
93
|
+
desc = meta.get("description", "")
|
|
94
|
+
if len(desc) > col_desc:
|
|
95
|
+
desc = desc[: col_desc - 3] + "..."
|
|
96
|
+
_log.info(f"{namespace:<{col_ns}} {short_id:<{col_id}} {flag:<{col_flag}} {desc}")
|
|
97
|
+
|
|
98
|
+
_log.info(separator)
|
|
99
|
+
_log.info(
|
|
100
|
+
f"Total: {len(capability_ids)} capacidades "
|
|
101
|
+
f"({sum(1 for c in capability_ids if c in PINNED_CAPABILITIES)} fijas)\n"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if missing:
|
|
105
|
+
_log.warning(f"[!] Sin JSON: {', '.join(missing)}\n")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def print_workflows(workflows: list[dict]) -> None:
|
|
109
|
+
col_id = 36
|
|
110
|
+
col_desc = 52
|
|
111
|
+
|
|
112
|
+
header = f"{'WORKFLOW ID':<{col_id}} {'DESCRIPCIÓN'}"
|
|
113
|
+
separator = "-" * (col_id + col_desc + 1)
|
|
114
|
+
|
|
115
|
+
_log.info(separator)
|
|
116
|
+
_log.info(header)
|
|
117
|
+
_log.info(separator)
|
|
118
|
+
|
|
119
|
+
for wf in workflows:
|
|
120
|
+
wf_id = wf.get("id", "")
|
|
121
|
+
desc = wf.get("description", "")
|
|
122
|
+
if len(desc) > col_desc:
|
|
123
|
+
desc = desc[: col_desc - 3] + "..."
|
|
124
|
+
_log.info(f"{wf_id:<{col_id}} {desc}")
|
|
125
|
+
|
|
126
|
+
_log.info(separator)
|
|
127
|
+
_log.info(f"Total: {len(workflows)} workflows\n")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def parse_args(args: list[str]) -> dict:
|
|
131
|
+
parsed = {"type": "all"}
|
|
132
|
+
i = 0
|
|
133
|
+
while i < len(args):
|
|
134
|
+
if args[i] == "--type" and i + 1 < len(args):
|
|
135
|
+
parsed["type"] = args[i + 1].lower()
|
|
136
|
+
i += 2
|
|
137
|
+
else:
|
|
138
|
+
i += 1
|
|
139
|
+
return parsed
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def main(args: list[str]) -> None:
|
|
143
|
+
parsed = parse_args(args)
|
|
144
|
+
env = CapabilityHelper.load_environment(ROOT)
|
|
145
|
+
|
|
146
|
+
profile_name = env.get("active_profile", "")
|
|
147
|
+
if not profile_name:
|
|
148
|
+
_log.warning("[!] No hay perfil activo en .higpertext/environment.json")
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
list_type = parsed["type"]
|
|
152
|
+
|
|
153
|
+
if list_type in ("all", "capabilities"):
|
|
154
|
+
capability_ids = load_profile_capabilities(profile_name)
|
|
155
|
+
_log.info(f"\nCapacidades disponibles para: {profile_name}")
|
|
156
|
+
_log.info(" [FIJA] = siempre incluida automáticamente por load-rules")
|
|
157
|
+
print_capabilities(capability_ids)
|
|
158
|
+
|
|
159
|
+
if list_type in ("all", "workflows"):
|
|
160
|
+
workflows = load_compiled_workflows()
|
|
161
|
+
_log.info(f"Workflows disponibles (compilados):")
|
|
162
|
+
if workflows:
|
|
163
|
+
print_workflows(workflows)
|
|
164
|
+
else:
|
|
165
|
+
_log.info(" (ninguno compilado — ejecuta 'profile load' para compilarlos)\n")
|
|
166
|
+
|
|
167
|
+
_log.info("Comandos de carga:")
|
|
168
|
+
_log.info(' htx task load-rules --rules "<id1>,<id2>"')
|
|
169
|
+
_log.info(" htx task load-rules --rules all")
|
|
170
|
+
_log.info(" htx task load-rules --workflows all")
|
|
171
|
+
_log.info(' htx task load-rules --rules grep-search --workflows "higpertext-build"\n')
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
main(sys.argv[1:])
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Capacidad common.llm-invoke — invoca un modelo LLM por API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
8
|
+
_log = get_logger()
|
|
9
|
+
|
|
10
|
+
# Permite importar kernel desde cualquier CWD
|
|
11
|
+
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
12
|
+
for _candidate in [
|
|
13
|
+
_SCRIPT_DIR.parents[3],
|
|
14
|
+
_SCRIPT_DIR.parents[3] / "core" / "kernel",
|
|
15
|
+
_SCRIPT_DIR.parents[5] / "src" / "core" / "kernel",
|
|
16
|
+
]:
|
|
17
|
+
if _candidate.exists() and str(_candidate) not in sys.path:
|
|
18
|
+
sys.path.insert(0, str(_candidate))
|
|
19
|
+
break
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
23
|
+
p = argparse.ArgumentParser(description="higpertext LLM Invoke")
|
|
24
|
+
p.add_argument("--prompt", required=True)
|
|
25
|
+
p.add_argument("--provider", default="")
|
|
26
|
+
p.add_argument("--model", default="")
|
|
27
|
+
p.add_argument("--system", default="")
|
|
28
|
+
p.add_argument("--max_tokens", type=int, default=1024)
|
|
29
|
+
p.add_argument("--temperature", type=float, default=0.7)
|
|
30
|
+
p.add_argument("--stream", default="false")
|
|
31
|
+
p.add_argument("--output_file", default="")
|
|
32
|
+
return p
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _resolve_project_root() -> Path:
|
|
36
|
+
"""Sube desde el script hasta encontrar .venv/bin/htx o htx.py."""
|
|
37
|
+
current = Path(__file__).resolve()
|
|
38
|
+
for parent in current.parents:
|
|
39
|
+
if (parent / ".venv" / "bin" / "htx").exists():
|
|
40
|
+
return parent
|
|
41
|
+
if (parent / "htx.py").exists():
|
|
42
|
+
return parent
|
|
43
|
+
return Path.cwd()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def main() -> None:
|
|
47
|
+
args = _build_parser().parse_args()
|
|
48
|
+
|
|
49
|
+
if not args.prompt.strip():
|
|
50
|
+
print("[ERROR] --prompt no puede estar vacío.", file=sys.stderr)
|
|
51
|
+
sys.exit(1)
|
|
52
|
+
|
|
53
|
+
use_stream = args.stream.lower() in ("true", "1", "yes")
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
from higpertext.kernel.infrastructure.llm.registry import LLMRegistry
|
|
57
|
+
except ImportError as exc:
|
|
58
|
+
print(f"[ERROR] No se pudo importar LLMRegistry: {exc}", file=sys.stderr)
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
root = _resolve_project_root()
|
|
62
|
+
registry = LLMRegistry(project_root=root)
|
|
63
|
+
provider_name = args.provider or registry.default_provider()
|
|
64
|
+
model = args.model or registry.default_model(provider_name)
|
|
65
|
+
|
|
66
|
+
if not provider_name:
|
|
67
|
+
_log.info(
|
|
68
|
+
"[ERROR] Provider LLM no definido. Usa --provider, HIGPERTEXT_LLM_PROVIDER "
|
|
69
|
+
"o configura llm.default_provider en .higpertext/config/environment.json.",
|
|
70
|
+
file=sys.stderr,
|
|
71
|
+
)
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
if not model:
|
|
74
|
+
_log.info(
|
|
75
|
+
"[ERROR] Modelo LLM no definido. Usa --model, HIGPERTEXT_LLM_MODEL "
|
|
76
|
+
"o configura llm.providers.<provider>.model en .higpertext/config/environment.json.",
|
|
77
|
+
file=sys.stderr,
|
|
78
|
+
)
|
|
79
|
+
sys.exit(1)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
provider = registry.resolve(provider_name)
|
|
83
|
+
except (ValueError, ImportError) as exc:
|
|
84
|
+
print(f"[ERROR] {exc}", file=sys.stderr)
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
_log.info(f"[*] Provider : {provider_name} | Modelo: {model or 'default'}")
|
|
88
|
+
_log.info(f"[*] Stream : {use_stream}")
|
|
89
|
+
_log.info("─" * 60)
|
|
90
|
+
|
|
91
|
+
output_parts: list[str] = []
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
if use_stream:
|
|
95
|
+
for chunk in provider.stream(
|
|
96
|
+
args.prompt,
|
|
97
|
+
system=args.system,
|
|
98
|
+
model=model,
|
|
99
|
+
max_tokens=args.max_tokens,
|
|
100
|
+
temperature=args.temperature,
|
|
101
|
+
):
|
|
102
|
+
_log.info(chunk, end="", flush=True)
|
|
103
|
+
output_parts.append(chunk)
|
|
104
|
+
_log.info()
|
|
105
|
+
full_output = "".join(output_parts)
|
|
106
|
+
_log.info("─" * 60)
|
|
107
|
+
_log.ok("[SUCCESS] Streaming completado.")
|
|
108
|
+
else:
|
|
109
|
+
resp = provider.complete(
|
|
110
|
+
args.prompt,
|
|
111
|
+
system=args.system,
|
|
112
|
+
model=model,
|
|
113
|
+
max_tokens=args.max_tokens,
|
|
114
|
+
temperature=args.temperature,
|
|
115
|
+
)
|
|
116
|
+
full_output = resp.content
|
|
117
|
+
_log.info(full_output)
|
|
118
|
+
_log.info("─" * 60)
|
|
119
|
+
_log.ok(
|
|
120
|
+
f"[SUCCESS] Tokens — entrada: {resp.input_tokens} | "
|
|
121
|
+
f"salida: {resp.output_tokens}"
|
|
122
|
+
)
|
|
123
|
+
except Exception as exc:
|
|
124
|
+
print(f"[ERROR] Llamada al provider falló: {exc}", file=sys.stderr)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
if args.output_file:
|
|
128
|
+
out_path = Path(args.output_file)
|
|
129
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
out_path.write_text(full_output, encoding="utf-8")
|
|
131
|
+
_log.ok(f"[*] Resultado guardado en: {out_path}")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
main()
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""load-rules: carga reglas de capacidades seleccionadas al contexto del LLM activo."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
7
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
8
|
+
_log = get_logger()
|
|
9
|
+
|
|
10
|
+
from higpertext.kernel.app_config import (
|
|
11
|
+
APPEND_ASSISTANTS,
|
|
12
|
+
PINNED_CAPABILITIES,
|
|
13
|
+
CLI_NAME,
|
|
14
|
+
RULES_DESTINATIONS_REL,
|
|
15
|
+
)
|
|
16
|
+
from higpertext.kernel.config_paths import (
|
|
17
|
+
CAPABILITIES_DIR,
|
|
18
|
+
WORKFLOWS_DIR,
|
|
19
|
+
PROFILES_DIR,
|
|
20
|
+
PROJECT_ROOT as ROOT,
|
|
21
|
+
WORKSPACE_DIR_NAME,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
ENVIRONMENT_FILE = ROOT / WORKSPACE_DIR_NAME / "config" / "environment.json"
|
|
25
|
+
|
|
26
|
+
RULES_DESTINATIONS = {k: ROOT / v for k, v in RULES_DESTINATIONS_REL.items()}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def load_environment() -> dict:
|
|
31
|
+
if not ENVIRONMENT_FILE.exists():
|
|
32
|
+
return {}
|
|
33
|
+
try:
|
|
34
|
+
return json.loads(ENVIRONMENT_FILE.read_text(encoding="utf-8"))
|
|
35
|
+
except json.JSONDecodeError:
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_profile_capabilities(profile_name: str) -> list[str]:
|
|
40
|
+
profile_file = PROFILES_DIR / f"{profile_name}.json"
|
|
41
|
+
if not profile_file.exists():
|
|
42
|
+
_log.warning(f"[!] Perfil no encontrado: {profile_file}")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
data = json.loads(profile_file.read_text(encoding="utf-8"))
|
|
45
|
+
return data.get("capabilities", [])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def load_capability_meta(cap_id: str) -> dict | None:
|
|
49
|
+
if "." not in cap_id:
|
|
50
|
+
return None
|
|
51
|
+
namespace, short_id = cap_id.split(".", 1)
|
|
52
|
+
cap_file = CAPABILITIES_DIR / namespace / f"{short_id}.json"
|
|
53
|
+
if not cap_file.exists():
|
|
54
|
+
return None
|
|
55
|
+
try:
|
|
56
|
+
return json.loads(cap_file.read_text(encoding="utf-8"))
|
|
57
|
+
except json.JSONDecodeError:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def render_capability_md(cap: dict) -> list[str]:
|
|
62
|
+
"""Renderiza una capacidad en formato Markdown."""
|
|
63
|
+
higpertext_cmd = CLI_NAME
|
|
64
|
+
short_id = cap["id"].split(".")[-1]
|
|
65
|
+
lines = [
|
|
66
|
+
f"### `{cap['id']}`",
|
|
67
|
+
f"**Descripción**: {cap['description']}",
|
|
68
|
+
f"**Comando**: `{higpertext_cmd} task {short_id} [--params]`",
|
|
69
|
+
"",
|
|
70
|
+
]
|
|
71
|
+
if cap.get("parameters"):
|
|
72
|
+
lines.append("**Parámetros**:")
|
|
73
|
+
for p in cap["parameters"]:
|
|
74
|
+
req_tag = "`REQUERIDO`" if p.get("required") else "`opcional`"
|
|
75
|
+
default = f" — default: `{p['default']}`" if "default" in p else ""
|
|
76
|
+
lines.append(f"- `--{p['name']}` {req_tag}: {p['description']}{default}")
|
|
77
|
+
lines.append("")
|
|
78
|
+
if cap.get("contract"):
|
|
79
|
+
rules = cap["contract"].get("rules", [])
|
|
80
|
+
if rules:
|
|
81
|
+
lines.append("**Contrato Técnico** — el output DEBE cumplir todas las reglas:")
|
|
82
|
+
for rule in rules:
|
|
83
|
+
lines.append(f"- `[CRÍTICO]` {rule}")
|
|
84
|
+
lines.append("")
|
|
85
|
+
lines += ["---", ""]
|
|
86
|
+
return lines
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def build_section(caps: list[dict], pinned: list[dict]) -> str:
|
|
90
|
+
lines = [
|
|
91
|
+
"<!-- session-capabilities — generado por load-rules, no editar manualmente -->",
|
|
92
|
+
"## Capacidades cargadas en sesión",
|
|
93
|
+
"",
|
|
94
|
+
]
|
|
95
|
+
if pinned:
|
|
96
|
+
lines += ["### Capacidades fijas (siempre disponibles)", ""]
|
|
97
|
+
for cap in pinned:
|
|
98
|
+
lines += render_capability_md(cap)
|
|
99
|
+
if caps:
|
|
100
|
+
lines += ["### Capacidades cargadas bajo demanda", ""]
|
|
101
|
+
for cap in caps:
|
|
102
|
+
lines += render_capability_md(cap)
|
|
103
|
+
lines.append("<!-- end session-capabilities -->")
|
|
104
|
+
return "\n".join(lines)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def write_dedicated(dest: Path, content: str) -> None:
|
|
108
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
dest.write_text(content, encoding="utf-8")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def write_append(dest: Path, content: str) -> None:
|
|
113
|
+
"""Reemplaza bloque session-capabilities si existe, sino lo appenda."""
|
|
114
|
+
if not dest.exists():
|
|
115
|
+
_log.warning(f"[!] Archivo destino no encontrado: {dest}")
|
|
116
|
+
_log.info(" Ejecuta 'htx profile load' primero.")
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
|
|
119
|
+
existing = dest.read_text(encoding="utf-8")
|
|
120
|
+
start_marker = "<!-- session-capabilities"
|
|
121
|
+
end_marker = "<!-- end session-capabilities -->"
|
|
122
|
+
|
|
123
|
+
if start_marker in existing and end_marker in existing:
|
|
124
|
+
start_idx = existing.index(start_marker)
|
|
125
|
+
end_idx = existing.index(end_marker) + len(end_marker)
|
|
126
|
+
updated = existing[:start_idx] + content + existing[end_idx:]
|
|
127
|
+
else:
|
|
128
|
+
updated = existing.rstrip() + "\n\n" + content + "\n"
|
|
129
|
+
|
|
130
|
+
dest.write_text(updated, encoding="utf-8")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def clear_rules(assistant: str) -> None:
|
|
134
|
+
dest = RULES_DESTINATIONS.get(assistant)
|
|
135
|
+
if dest is None:
|
|
136
|
+
_log.warning(f"[!] Asistente no soportado: {assistant}")
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
|
|
139
|
+
if assistant in APPEND_ASSISTANTS:
|
|
140
|
+
if not dest.exists():
|
|
141
|
+
_log.info("[i] No hay reglas de sesión que limpiar.")
|
|
142
|
+
return
|
|
143
|
+
existing = dest.read_text(encoding="utf-8")
|
|
144
|
+
start_marker = "<!-- session-capabilities"
|
|
145
|
+
end_marker = "<!-- end session-capabilities -->"
|
|
146
|
+
if start_marker in existing and end_marker in existing:
|
|
147
|
+
start_idx = existing.index(start_marker)
|
|
148
|
+
end_idx = existing.index(end_marker) + len(end_marker)
|
|
149
|
+
cleaned = existing[:start_idx].rstrip() + "\n" + existing[end_idx:].lstrip()
|
|
150
|
+
dest.write_text(cleaned, encoding="utf-8")
|
|
151
|
+
_log.ok(f"[✓] Bloque session-capabilities eliminado de {dest.relative_to(ROOT)}")
|
|
152
|
+
else:
|
|
153
|
+
_log.info("[i] No se encontró bloque session-capabilities.")
|
|
154
|
+
else:
|
|
155
|
+
if dest.exists():
|
|
156
|
+
dest.unlink()
|
|
157
|
+
_log.ok(f"[✓] Eliminado: {dest.relative_to(ROOT)}")
|
|
158
|
+
else:
|
|
159
|
+
_log.info("[i] No hay reglas de sesión que limpiar.")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def load_workflow_meta(workflow_id: str) -> dict | None:
|
|
163
|
+
"""Carga el JSON de un workflow desde src/config/workflows/."""
|
|
164
|
+
workflows_dir = WORKFLOWS_DIR
|
|
165
|
+
short_id = workflow_id.split(".")[-1]
|
|
166
|
+
wf_file = workflows_dir / f"{short_id}.json"
|
|
167
|
+
if not wf_file.exists():
|
|
168
|
+
wf_file_alt = workflows_dir / f"{short_id.replace('-', '_')}.json"
|
|
169
|
+
if wf_file_alt.exists():
|
|
170
|
+
wf_file = wf_file_alt
|
|
171
|
+
else:
|
|
172
|
+
return None
|
|
173
|
+
try:
|
|
174
|
+
return json.loads(wf_file.read_text(encoding="utf-8"))
|
|
175
|
+
except json.JSONDecodeError:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def load_profile_workflows(profile_name: str) -> list[str]:
|
|
180
|
+
"""Lee los IDs de workflows desde src/config/workflows/."""
|
|
181
|
+
workflows_dir = WORKFLOWS_DIR
|
|
182
|
+
if not workflows_dir.exists():
|
|
183
|
+
return []
|
|
184
|
+
wf_ids = []
|
|
185
|
+
for wf_file in sorted(workflows_dir.glob("*.json")):
|
|
186
|
+
try:
|
|
187
|
+
data = json.loads(wf_file.read_text(encoding="utf-8"))
|
|
188
|
+
if data.get("id"):
|
|
189
|
+
wf_ids.append(data["id"])
|
|
190
|
+
except json.JSONDecodeError: # nosec B110
|
|
191
|
+
pass
|
|
192
|
+
return wf_ids
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def render_workflow_md(wf: dict) -> list[str]:
|
|
196
|
+
"""Renderiza un workflow en formato Markdown."""
|
|
197
|
+
higpertext_cmd = CLI_NAME
|
|
198
|
+
short_id = wf["id"].split(".")[-1]
|
|
199
|
+
lines = [
|
|
200
|
+
f"### `{wf['id']}` ({wf.get('name', short_id)})",
|
|
201
|
+
f"**Descripción**: {wf.get('description', '')}",
|
|
202
|
+
f"**Comando**: `{higpertext_cmd} workflow run {short_id} [--params]`",
|
|
203
|
+
"",
|
|
204
|
+
]
|
|
205
|
+
if wf.get("parameters"):
|
|
206
|
+
lines.append("**Parámetros**:")
|
|
207
|
+
for p in wf["parameters"]:
|
|
208
|
+
req_tag = "`REQUERIDO`" if p.get("required") else "`opcional`"
|
|
209
|
+
lines.append(f"- `--{p['name']}` {req_tag}: {p['description']}")
|
|
210
|
+
lines.append("")
|
|
211
|
+
if wf.get("execution_chain"):
|
|
212
|
+
lines.append("**Cadena de Ejecución**:")
|
|
213
|
+
for step in wf["execution_chain"]:
|
|
214
|
+
lines.append(f"- `Paso {step['step']}`: `{step['task']}`")
|
|
215
|
+
lines.append("")
|
|
216
|
+
lines += ["---", ""]
|
|
217
|
+
return lines
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def build_section(caps: list[dict], pinned: list[dict], workflows: list[dict] | None = None) -> str:
|
|
221
|
+
lines = [
|
|
222
|
+
"<!-- session-capabilities — generado por load-rules, no editar manualmente -->",
|
|
223
|
+
"## Capacidades cargadas en sesión",
|
|
224
|
+
"",
|
|
225
|
+
]
|
|
226
|
+
if pinned:
|
|
227
|
+
lines += ["### Capacidades fijas (siempre disponibles)", ""]
|
|
228
|
+
for cap in pinned:
|
|
229
|
+
lines += render_capability_md(cap)
|
|
230
|
+
if caps:
|
|
231
|
+
lines += ["### Capacidades cargadas bajo demanda", ""]
|
|
232
|
+
for cap in caps:
|
|
233
|
+
lines += render_capability_md(cap)
|
|
234
|
+
if workflows:
|
|
235
|
+
lines += ["## Workflows cargados en sesión", ""]
|
|
236
|
+
for wf in workflows:
|
|
237
|
+
lines += render_workflow_md(wf)
|
|
238
|
+
lines.append("<!-- end session-capabilities -->")
|
|
239
|
+
return "\n".join(lines)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def parse_args(args: list[str]) -> dict:
|
|
243
|
+
parsed = {"rules": None, "workflows": None, "clear": False}
|
|
244
|
+
i = 0
|
|
245
|
+
while i < len(args):
|
|
246
|
+
if args[i] == "--rules" and i + 1 < len(args):
|
|
247
|
+
parsed["rules"] = args[i + 1]
|
|
248
|
+
i += 2
|
|
249
|
+
elif args[i] == "--workflows" and i + 1 < len(args):
|
|
250
|
+
parsed["workflows"] = args[i + 1]
|
|
251
|
+
i += 2
|
|
252
|
+
elif args[i] == "--clear" and i + 1 < len(args):
|
|
253
|
+
parsed["clear"] = args[i + 1].lower() == "true"
|
|
254
|
+
i += 2
|
|
255
|
+
else:
|
|
256
|
+
i += 1
|
|
257
|
+
return parsed
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def main(args: list[str]) -> None:
|
|
261
|
+
parsed = parse_args(args)
|
|
262
|
+
env = load_environment()
|
|
263
|
+
assistant = env.get("assistant", "claude")
|
|
264
|
+
profile_name = env.get("active_profile", "")
|
|
265
|
+
|
|
266
|
+
if not profile_name:
|
|
267
|
+
_log.warning("[!] No hay perfil activo en .higpertext/environment.json")
|
|
268
|
+
sys.exit(1)
|
|
269
|
+
|
|
270
|
+
if parsed["clear"]:
|
|
271
|
+
clear_rules(assistant)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
if not parsed["rules"] and not parsed["workflows"]:
|
|
275
|
+
_log.warning("[!] Se requiere --rules o --workflows (o ambos).")
|
|
276
|
+
_log.info(" Ejemplos:")
|
|
277
|
+
_log.info(" htx task load-rules --rules all")
|
|
278
|
+
_log.info(" htx task load-rules --rules grep-search,code-quality")
|
|
279
|
+
_log.info(" htx task load-rules --workflows all")
|
|
280
|
+
_log.info(" htx task load-rules --rules grep-search --workflows higpertext-build")
|
|
281
|
+
sys.exit(1)
|
|
282
|
+
|
|
283
|
+
all_cap_ids = load_profile_capabilities(profile_name)
|
|
284
|
+
|
|
285
|
+
# --- Capacidades pinned (siempre incluidas) ---
|
|
286
|
+
pinned_caps = []
|
|
287
|
+
pinned_ids_in_profile = [c for c in PINNED_CAPABILITIES if c in all_cap_ids]
|
|
288
|
+
for cap_id in pinned_ids_in_profile:
|
|
289
|
+
meta = load_capability_meta(cap_id)
|
|
290
|
+
if meta:
|
|
291
|
+
pinned_caps.append(meta)
|
|
292
|
+
|
|
293
|
+
# --- Capacidades bajo demanda ---
|
|
294
|
+
demand_caps = []
|
|
295
|
+
if parsed["rules"]:
|
|
296
|
+
if parsed["rules"].strip().lower() == "all":
|
|
297
|
+
# all = todas excepto las pinned (ya se incluyen arriba)
|
|
298
|
+
selected_ids = [c for c in all_cap_ids if c not in PINNED_CAPABILITIES]
|
|
299
|
+
else:
|
|
300
|
+
requested = [r.strip() for r in parsed["rules"].split(",")]
|
|
301
|
+
id_map = {cap_id.split(".")[-1]: cap_id for cap_id in all_cap_ids}
|
|
302
|
+
id_map.update({cap_id: cap_id for cap_id in all_cap_ids})
|
|
303
|
+
selected_ids = []
|
|
304
|
+
unknown = []
|
|
305
|
+
for r in requested:
|
|
306
|
+
if r in id_map:
|
|
307
|
+
full_id = id_map[r]
|
|
308
|
+
if full_id not in PINNED_CAPABILITIES:
|
|
309
|
+
selected_ids.append(full_id)
|
|
310
|
+
else:
|
|
311
|
+
unknown.append(r)
|
|
312
|
+
if unknown:
|
|
313
|
+
_log.warning(f"[!] Capacidades no encontradas en el perfil: {', '.join(unknown)}")
|
|
314
|
+
_log.info(" Usa 'htx task list-rules' para ver las disponibles.")
|
|
315
|
+
sys.exit(1)
|
|
316
|
+
|
|
317
|
+
for cap_id in selected_ids:
|
|
318
|
+
meta = load_capability_meta(cap_id)
|
|
319
|
+
if meta is None:
|
|
320
|
+
_log.warning(f"[!] JSON no encontrado para: {cap_id} — omitido")
|
|
321
|
+
continue
|
|
322
|
+
demand_caps.append(meta)
|
|
323
|
+
|
|
324
|
+
# --- Workflows ---
|
|
325
|
+
wf_metas = []
|
|
326
|
+
if parsed["workflows"]:
|
|
327
|
+
all_wf_ids = load_profile_workflows(profile_name)
|
|
328
|
+
if parsed["workflows"].strip().lower() == "all":
|
|
329
|
+
selected_wf_ids = all_wf_ids
|
|
330
|
+
else:
|
|
331
|
+
requested_wf = [w.strip() for w in parsed["workflows"].split(",")]
|
|
332
|
+
wf_id_map = {wf_id.split(".")[-1]: wf_id for wf_id in all_wf_ids}
|
|
333
|
+
wf_id_map.update({wf_id: wf_id for wf_id in all_wf_ids})
|
|
334
|
+
selected_wf_ids = []
|
|
335
|
+
unknown_wf = []
|
|
336
|
+
for w in requested_wf:
|
|
337
|
+
if w in wf_id_map:
|
|
338
|
+
selected_wf_ids.append(wf_id_map[w])
|
|
339
|
+
else:
|
|
340
|
+
unknown_wf.append(w)
|
|
341
|
+
if unknown_wf:
|
|
342
|
+
_log.warning(f"[!] Workflows no encontrados: {', '.join(unknown_wf)}")
|
|
343
|
+
_log.info(" Usa 'htx task list-rules --type workflows' para ver los disponibles.")
|
|
344
|
+
sys.exit(1)
|
|
345
|
+
for wf_id in selected_wf_ids:
|
|
346
|
+
meta = load_workflow_meta(wf_id)
|
|
347
|
+
if meta is None:
|
|
348
|
+
_log.warning(f"[!] JSON no encontrado para workflow: {wf_id} — omitido")
|
|
349
|
+
continue
|
|
350
|
+
wf_metas.append(meta)
|
|
351
|
+
|
|
352
|
+
if not pinned_caps and not demand_caps and not wf_metas:
|
|
353
|
+
_log.warning("[!] No se cargó ninguna capacidad ni workflow.")
|
|
354
|
+
sys.exit(1)
|
|
355
|
+
|
|
356
|
+
content = build_section(demand_caps, pinned_caps, wf_metas if wf_metas else None)
|
|
357
|
+
dest = RULES_DESTINATIONS.get(assistant)
|
|
358
|
+
|
|
359
|
+
if dest is None:
|
|
360
|
+
_log.warning(f"[!] Asistente no soportado: {assistant}")
|
|
361
|
+
sys.exit(1)
|
|
362
|
+
|
|
363
|
+
if assistant in APPEND_ASSISTANTS:
|
|
364
|
+
write_append(dest, content)
|
|
365
|
+
else:
|
|
366
|
+
write_dedicated(dest, content)
|
|
367
|
+
|
|
368
|
+
total_caps = len(pinned_caps) + len(demand_caps)
|
|
369
|
+
_log.ok(
|
|
370
|
+
f"[OK] {total_caps} capacidad(es) ({len(pinned_caps)} fijas + {len(demand_caps)} bajo demanda)" # noqa: E501
|
|
371
|
+
f" + {len(wf_metas)} workflow(s) -> {dest.relative_to(ROOT)}"
|
|
372
|
+
)
|
|
373
|
+
_log.info(f" Asistente: {assistant} | Perfil: {profile_name}")
|
|
374
|
+
if assistant not in APPEND_ASSISTANTS:
|
|
375
|
+
_log.info(" El archivo se carga automáticamente en el próximo turno.")
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
if __name__ == "__main__":
|
|
379
|
+
main(sys.argv[1:])
|