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.
Files changed (335) hide show
  1. config/adapters_config.json +450 -0
  2. config/antigravity_agent_template.json +31 -0
  3. config/app_config.json +174 -0
  4. config/context_engine.json +33 -0
  5. config/environments/model_defaults.json +5 -0
  6. config/governance/branching_strategy.json +36 -0
  7. config/governance/deployment_gates.json +30 -0
  8. config/governance/guidelines_contract.json +54 -0
  9. config/governance/quality_gates.json +39 -0
  10. config/governance/section_rules.json +22 -0
  11. config/governance/security_guardrails.json +52 -0
  12. config/hooks/README.md +35 -0
  13. config/hooks/custom/test_output_limiter.json +9 -0
  14. config/hooks/global/session_prompt.json +9 -0
  15. config/htx_config.json +24 -0
  16. config/profile_learner.json +18 -0
  17. config/profiles/base_agent.json +40 -0
  18. config/profiles/base_auditor.json +19 -0
  19. config/profiles/base_developer.json +19 -0
  20. config/profiles/base_operator.json +16 -0
  21. config/profiles/global.json +33 -0
  22. config/profiles/software_developer.json +23 -0
  23. config/router_content.json +137 -0
  24. config/semantic_graph.json +66 -0
  25. config/workflows/ado_release_flow.json +38 -0
  26. config/workflows/docs-update.json +33 -0
  27. config/workflows/governance-check.yaml +26 -0
  28. config/workflows/guidelines-sync.json +40 -0
  29. config/workflows/higpertext-build.json +73 -0
  30. config/workflows/higpertext-plan.json +38 -0
  31. config/workflows/higpertext-review.json +41 -0
  32. config/workflows/pr-quality-check.json +56 -0
  33. config/workflows/quality-remediation.json +57 -0
  34. higpertext/__init__.py +18 -0
  35. higpertext/adapters/__init__.py +27 -0
  36. higpertext/adapters/adapter_utils.py +604 -0
  37. higpertext/adapters/claude_adapter/__init__.py +0 -0
  38. higpertext/adapters/claude_adapter/claude_adapter.py +154 -0
  39. higpertext/adapters/copilot_adapter/__init__.py +0 -0
  40. higpertext/adapters/copilot_adapter/copilot_adapter.py +231 -0
  41. higpertext/adapters/gemini_adapter/__init__.py +0 -0
  42. higpertext/adapters/gemini_adapter/gemini_adapter.py +211 -0
  43. higpertext/adapters/llm_formatter.py +46 -0
  44. higpertext/adapters/open_code_adapter/__init__.py +0 -0
  45. higpertext/adapters/open_code_adapter/open_code_adapter.py +480 -0
  46. higpertext/capabilities/capabilities_runner.py +216 -0
  47. higpertext/capabilities/common/agent-builder.json +54 -0
  48. higpertext/capabilities/common/agent-sync.json +34 -0
  49. higpertext/capabilities/common/code-skeletonizer.json +35 -0
  50. higpertext/capabilities/common/commit-report.json +42 -0
  51. higpertext/capabilities/common/context-assembler.json +37 -0
  52. higpertext/capabilities/common/context-budget-report.json +15 -0
  53. higpertext/capabilities/common/dep-manager.json +43 -0
  54. higpertext/capabilities/common/docs-sync.json +14 -0
  55. higpertext/capabilities/common/doctor.json +18 -0
  56. higpertext/capabilities/common/efficiency-meter.json +31 -0
  57. higpertext/capabilities/common/env-catalog.json +13 -0
  58. higpertext/capabilities/common/env-clean.json +14 -0
  59. higpertext/capabilities/common/env-logs.json +16 -0
  60. higpertext/capabilities/common/env-runner.json +23 -0
  61. higpertext/capabilities/common/env-status.json +13 -0
  62. higpertext/capabilities/common/env-stop.json +14 -0
  63. higpertext/capabilities/common/env-template.json +14 -0
  64. higpertext/capabilities/common/error-context-locator.json +23 -0
  65. higpertext/capabilities/common/eval-agent.json +33 -0
  66. higpertext/capabilities/common/file-map.json +17 -0
  67. higpertext/capabilities/common/governance-exception.json +54 -0
  68. higpertext/capabilities/common/graph-query.json +59 -0
  69. higpertext/capabilities/common/graph-rebuild.json +31 -0
  70. higpertext/capabilities/common/graph-visualize.json +37 -0
  71. higpertext/capabilities/common/grep-search.json +176 -0
  72. higpertext/capabilities/common/higpertext-tester.json +25 -0
  73. higpertext/capabilities/common/hook-health.json +19 -0
  74. higpertext/capabilities/common/hook-sync-check.json +19 -0
  75. higpertext/capabilities/common/hooks-manager.json +55 -0
  76. higpertext/capabilities/common/knowledge-asker.json +27 -0
  77. higpertext/capabilities/common/list-rules.json +27 -0
  78. higpertext/capabilities/common/llm-invoke.json +59 -0
  79. higpertext/capabilities/common/load-rules.json +37 -0
  80. higpertext/capabilities/common/memory-manager.json +65 -0
  81. higpertext/capabilities/common/quality-scan.json +21 -0
  82. higpertext/capabilities/common/quality-updater.json +35 -0
  83. higpertext/capabilities/common/rag-index.json +17 -0
  84. higpertext/capabilities/common/report-viewer.json +24 -0
  85. higpertext/capabilities/common/roadmap-report.json +37 -0
  86. higpertext/capabilities/common/scripts/_env_cli.py +65 -0
  87. higpertext/capabilities/common/scripts/agent_builder.py +60 -0
  88. higpertext/capabilities/common/scripts/agent_sync.py +56 -0
  89. higpertext/capabilities/common/scripts/ask_higpertext.py +38 -0
  90. higpertext/capabilities/common/scripts/code_skeletonizer.py +225 -0
  91. higpertext/capabilities/common/scripts/commit_report.py +134 -0
  92. higpertext/capabilities/common/scripts/context_assembler.py +70 -0
  93. higpertext/capabilities/common/scripts/context_budget_report.py +53 -0
  94. higpertext/capabilities/common/scripts/dep_manager.py +81 -0
  95. higpertext/capabilities/common/scripts/docs_sync.py +981 -0
  96. higpertext/capabilities/common/scripts/doctor.py +144 -0
  97. higpertext/capabilities/common/scripts/efficiency_meter.py +83 -0
  98. higpertext/capabilities/common/scripts/env_catalog.py +47 -0
  99. higpertext/capabilities/common/scripts/env_clean.py +30 -0
  100. higpertext/capabilities/common/scripts/env_logs.py +32 -0
  101. higpertext/capabilities/common/scripts/env_runner.py +53 -0
  102. higpertext/capabilities/common/scripts/env_status.py +38 -0
  103. higpertext/capabilities/common/scripts/env_stop.py +30 -0
  104. higpertext/capabilities/common/scripts/env_template.py +73 -0
  105. higpertext/capabilities/common/scripts/error_context_locator.py +138 -0
  106. higpertext/capabilities/common/scripts/eval_agent.py +80 -0
  107. higpertext/capabilities/common/scripts/file_map.py +95 -0
  108. higpertext/capabilities/common/scripts/governance_exception.py +116 -0
  109. higpertext/capabilities/common/scripts/graph_query.py +104 -0
  110. higpertext/capabilities/common/scripts/graph_rebuild.py +107 -0
  111. higpertext/capabilities/common/scripts/graph_visualize.py +76 -0
  112. higpertext/capabilities/common/scripts/grep_search.py +648 -0
  113. higpertext/capabilities/common/scripts/higpertext_tester.py +102 -0
  114. higpertext/capabilities/common/scripts/hook_health.py +149 -0
  115. higpertext/capabilities/common/scripts/hook_sync_check.py +134 -0
  116. higpertext/capabilities/common/scripts/hooks_manager.py +171 -0
  117. higpertext/capabilities/common/scripts/list_rules.py +175 -0
  118. higpertext/capabilities/common/scripts/llm_invoke.py +135 -0
  119. higpertext/capabilities/common/scripts/load_rules.py +379 -0
  120. higpertext/capabilities/common/scripts/memory_manager.py +210 -0
  121. higpertext/capabilities/common/scripts/presentation_engine.py +63 -0
  122. higpertext/capabilities/common/scripts/quality_scan.py +132 -0
  123. higpertext/capabilities/common/scripts/rag_index.py +39 -0
  124. higpertext/capabilities/common/scripts/report_viewer.py +106 -0
  125. higpertext/capabilities/common/scripts/roadmap_report.py +73 -0
  126. higpertext/capabilities/common/scripts/search_router.py +111 -0
  127. higpertext/capabilities/common/scripts/semantic_diff.py +166 -0
  128. higpertext/capabilities/common/scripts/semantic_search.py +43 -0
  129. higpertext/capabilities/common/scripts/session_control.py +136 -0
  130. higpertext/capabilities/common/scripts/smart_read.py +232 -0
  131. higpertext/capabilities/common/scripts/subagent_executor.py +143 -0
  132. higpertext/capabilities/common/scripts/sync_agents.py +353 -0
  133. higpertext/capabilities/common/scripts/task_decomposer.py +78 -0
  134. higpertext/capabilities/common/scripts/telemetry_report.py +36 -0
  135. higpertext/capabilities/common/search-router.json +24 -0
  136. higpertext/capabilities/common/semantic-diff.json +40 -0
  137. higpertext/capabilities/common/semantic-search.json +19 -0
  138. higpertext/capabilities/common/session-clean.json +20 -0
  139. higpertext/capabilities/common/session-start.json +44 -0
  140. higpertext/capabilities/common/smart-read.json +28 -0
  141. higpertext/capabilities/common/subagent-executor.json +25 -0
  142. higpertext/capabilities/common/sync-agents.json +32 -0
  143. higpertext/capabilities/common/task-decomposer.json +37 -0
  144. higpertext/capabilities/common/telemetry-report.json +23 -0
  145. higpertext/capabilities/git/__init__.py +0 -0
  146. higpertext/capabilities/git/committer.json +61 -0
  147. higpertext/capabilities/git/diff.json +33 -0
  148. higpertext/capabilities/git/ls-files.json +44 -0
  149. higpertext/capabilities/git/rm.json +27 -0
  150. higpertext/capabilities/git/scripts/__init__.py +0 -0
  151. higpertext/capabilities/git/scripts/commit_changes.py +1077 -0
  152. higpertext/capabilities/git/scripts/git_diff.py +171 -0
  153. higpertext/capabilities/git/scripts/git_ls_files.py +376 -0
  154. higpertext/capabilities/git/scripts/git_rm.py +62 -0
  155. higpertext/capabilities/security/k8s-auditor.json +33 -0
  156. higpertext/capabilities/security/scripts/k8s_auditor.py +307 -0
  157. higpertext/capabilities/security/scripts/secret_scanner.py +235 -0
  158. higpertext/capabilities/security/secret-scanner.json +32 -0
  159. higpertext/hooks/__init__.py +28 -0
  160. higpertext/hooks/_compat.py +27 -0
  161. higpertext/hooks/hook_tasks/__init__.py +1 -0
  162. higpertext/hooks/hook_tasks/_rules/__init__.py +0 -0
  163. higpertext/hooks/hook_tasks/_rules/bash_rules.py +635 -0
  164. higpertext/hooks/hook_tasks/_rules/context_engine_rule.py +79 -0
  165. higpertext/hooks/hook_tasks/_rules/context_rules.py +199 -0
  166. higpertext/hooks/hook_tasks/_rules/governance_adapter.py +72 -0
  167. higpertext/hooks/hook_tasks/_rules/profile_rules.json +25 -0
  168. higpertext/hooks/hook_tasks/_rules/quality_rules.py +86 -0
  169. higpertext/hooks/hook_tasks/_rules/security_rules.py +214 -0
  170. higpertext/hooks/hook_tasks/_rules/session_rules.py +316 -0
  171. higpertext/hooks/hook_tasks/_rules/telemetry_rules.py +121 -0
  172. higpertext/hooks/hook_tasks/audit_logger_hook.py +28 -0
  173. higpertext/hooks/hook_tasks/hook_bash_guard.py +101 -0
  174. higpertext/hooks/hook_tasks/hook_code_quality.py +48 -0
  175. higpertext/hooks/hook_tasks/hook_context_hint.py +46 -0
  176. higpertext/hooks/hook_tasks/hook_context_manager.py +44 -0
  177. higpertext/hooks/hook_tasks/hook_io.py +122 -0
  178. higpertext/hooks/hook_tasks/hook_loop_guard.py +182 -0
  179. higpertext/hooks/hook_tasks/hook_post_observer.py +54 -0
  180. higpertext/hooks/hook_tasks/hook_read_guard.py +85 -0
  181. higpertext/hooks/hook_tasks/hook_security_guard.py +81 -0
  182. higpertext/hooks/hook_tasks/hook_session_prompt.py +83 -0
  183. higpertext/hooks/hook_tasks/hook_session_stop.py +115 -0
  184. higpertext/hooks/hook_tasks/hook_utils.py +144 -0
  185. higpertext/hooks/hook_tasks/session_guard_hook.py +23 -0
  186. higpertext/hooks/hook_tasks/telemetry_utils.py +176 -0
  187. higpertext/hooks/hook_tasks/test_echo_hook.py +33 -0
  188. higpertext/hooks/hook_tasks/webhook_hook.py +54 -0
  189. higpertext/hooks/hook_tasks/workflow_runner_hook.py +49 -0
  190. higpertext/hooks/hooks_catalog.json +116 -0
  191. higpertext/kernel/__init__.py +63 -0
  192. higpertext/kernel/_compat.py +138 -0
  193. higpertext/kernel/app_config.py +117 -0
  194. higpertext/kernel/application/__init__.py +13 -0
  195. higpertext/kernel/application/agent_registry.py +102 -0
  196. higpertext/kernel/application/capability_manager.py +61 -0
  197. higpertext/kernel/application/commit_reporter.py +247 -0
  198. higpertext/kernel/application/context_builder.py +166 -0
  199. higpertext/kernel/application/context_engine.py +409 -0
  200. higpertext/kernel/application/engine.py +41 -0
  201. higpertext/kernel/application/env_runtime.py +174 -0
  202. higpertext/kernel/application/environment_manager.py +154 -0
  203. higpertext/kernel/application/governance.py +192 -0
  204. higpertext/kernel/application/hook_registry.py +102 -0
  205. higpertext/kernel/application/hook_renderer.py +720 -0
  206. higpertext/kernel/application/ports.py +49 -0
  207. higpertext/kernel/application/profile_learner.py +358 -0
  208. higpertext/kernel/application/profile_service.py +205 -0
  209. higpertext/kernel/application/profile_services.py +6 -0
  210. higpertext/kernel/application/profile_use_cases.py +93 -0
  211. higpertext/kernel/application/rag_service.py +75 -0
  212. higpertext/kernel/application/roadmap_reporter.py +178 -0
  213. higpertext/kernel/application/semantic_engine.py +258 -0
  214. higpertext/kernel/application/session_services.py +33 -0
  215. higpertext/kernel/application/skill_hook_compiler.py +85 -0
  216. higpertext/kernel/application/telemetry.py +326 -0
  217. higpertext/kernel/application/workflow_manager.py +176 -0
  218. higpertext/kernel/config_paths.py +66 -0
  219. higpertext/kernel/domain/__init__.py +12 -0
  220. higpertext/kernel/domain/agent_registry.py +23 -0
  221. higpertext/kernel/domain/commit_reporter.py +155 -0
  222. higpertext/kernel/domain/compilers.py +7 -0
  223. higpertext/kernel/domain/context_engine.py +319 -0
  224. higpertext/kernel/domain/entities.py +51 -0
  225. higpertext/kernel/domain/env_runtime.py +62 -0
  226. higpertext/kernel/domain/governance.py +198 -0
  227. higpertext/kernel/domain/hook_models.py +29 -0
  228. higpertext/kernel/domain/profile_learner.py +186 -0
  229. higpertext/kernel/domain/rag.py +70 -0
  230. higpertext/kernel/domain/repositories.py +8 -0
  231. higpertext/kernel/domain/roadmap_reporter.py +80 -0
  232. higpertext/kernel/domain/semantic_engine.py +107 -0
  233. higpertext/kernel/engine.py +42 -0
  234. higpertext/kernel/htx_resolver.py +69 -0
  235. higpertext/kernel/infrastructure/__init__.py +13 -0
  236. higpertext/kernel/infrastructure/agent_registry.py +40 -0
  237. higpertext/kernel/infrastructure/cache/capability_cache.py +319 -0
  238. higpertext/kernel/infrastructure/capability_helper.py +40 -0
  239. higpertext/kernel/infrastructure/cli/__init__.py +1 -0
  240. higpertext/kernel/infrastructure/cli/agent_commands.py +62 -0
  241. higpertext/kernel/infrastructure/cli/arguments.py +39 -0
  242. higpertext/kernel/infrastructure/cli/capability_command_builder.py +86 -0
  243. higpertext/kernel/infrastructure/cli/capability_task_service.py +234 -0
  244. higpertext/kernel/infrastructure/cli/cli_search.py +234 -0
  245. higpertext/kernel/infrastructure/cli/parameter_contracts.py +83 -0
  246. higpertext/kernel/infrastructure/cli/parser_builder.py +122 -0
  247. higpertext/kernel/infrastructure/cli/profile_commands.py +89 -0
  248. higpertext/kernel/infrastructure/cli/roadmap_commands.py +117 -0
  249. higpertext/kernel/infrastructure/cli/router.py +1110 -0
  250. higpertext/kernel/infrastructure/cli/session_commands.py +36 -0
  251. higpertext/kernel/infrastructure/cli/task_commands.py +23 -0
  252. higpertext/kernel/infrastructure/cli/task_result_reporter.py +56 -0
  253. higpertext/kernel/infrastructure/cli/workflow_commands.py +25 -0
  254. higpertext/kernel/infrastructure/compilers/__init__.py +3 -0
  255. higpertext/kernel/infrastructure/compilers/factory.py +27 -0
  256. higpertext/kernel/infrastructure/compilers/graph_compiler.py +20 -0
  257. higpertext/kernel/infrastructure/compilers/guide_compiler.py +50 -0
  258. higpertext/kernel/infrastructure/compilers/hook_compiler.py +69 -0
  259. higpertext/kernel/infrastructure/compilers/playbook_compiler.py +154 -0
  260. higpertext/kernel/infrastructure/context_engine.py +303 -0
  261. higpertext/kernel/infrastructure/database/local_vector_store.py +99 -0
  262. higpertext/kernel/infrastructure/deployment/__init__.py +1 -0
  263. higpertext/kernel/infrastructure/deployment/resource_deployer.py +283 -0
  264. higpertext/kernel/infrastructure/diagnostics/__init__.py +1 -0
  265. higpertext/kernel/infrastructure/diagnostics/health.py +191 -0
  266. higpertext/kernel/infrastructure/env_runtime.py +227 -0
  267. higpertext/kernel/infrastructure/execution/__init__.py +1 -0
  268. higpertext/kernel/infrastructure/execution/parallel.py +188 -0
  269. higpertext/kernel/infrastructure/execution/resilience.py +155 -0
  270. higpertext/kernel/infrastructure/file_repositories.py +213 -0
  271. higpertext/kernel/infrastructure/governance.py +198 -0
  272. higpertext/kernel/infrastructure/hook_config_loader.py +53 -0
  273. higpertext/kernel/infrastructure/hook_webhook_dispatcher.py +61 -0
  274. higpertext/kernel/infrastructure/hook_workflow_bridge.py +60 -0
  275. higpertext/kernel/infrastructure/llm/__init__.py +6 -0
  276. higpertext/kernel/infrastructure/llm/provider.py +46 -0
  277. higpertext/kernel/infrastructure/llm/providers/__init__.py +0 -0
  278. higpertext/kernel/infrastructure/llm/providers/anthropic_provider.py +94 -0
  279. higpertext/kernel/infrastructure/llm/providers/gemini_embeddings.py +74 -0
  280. higpertext/kernel/infrastructure/llm/providers/gemini_provider.py +101 -0
  281. higpertext/kernel/infrastructure/llm/providers/ollama_provider.py +110 -0
  282. higpertext/kernel/infrastructure/llm/providers/openai_provider.py +98 -0
  283. higpertext/kernel/infrastructure/llm/registry.py +81 -0
  284. higpertext/kernel/infrastructure/logger.py +303 -0
  285. higpertext/kernel/infrastructure/output_store.py +70 -0
  286. higpertext/kernel/infrastructure/parser/__init__.py +1 -0
  287. higpertext/kernel/infrastructure/parser/code_chunker.py +144 -0
  288. higpertext/kernel/infrastructure/parser/language/__init__.py +14 -0
  289. higpertext/kernel/infrastructure/parser/language/base.py +41 -0
  290. higpertext/kernel/infrastructure/parser/language/powershell_parser.py +35 -0
  291. higpertext/kernel/infrastructure/parser/language/python_parser.py +98 -0
  292. higpertext/kernel/infrastructure/parser/language/typescript_parser.py +91 -0
  293. higpertext/kernel/infrastructure/parser/semantic_graph.py +409 -0
  294. higpertext/kernel/infrastructure/presentation/__init__.py +1 -0
  295. higpertext/kernel/infrastructure/presentation/html_renderer.py +137 -0
  296. higpertext/kernel/infrastructure/presentation/markdown_renderer.py +84 -0
  297. higpertext/kernel/infrastructure/presentation/markdown_report_renderer.py +97 -0
  298. higpertext/kernel/infrastructure/profile_store.py +28 -0
  299. higpertext/kernel/infrastructure/semantic_engine.py +289 -0
  300. higpertext/kernel/infrastructure/telemetry_reporter.py +132 -0
  301. higpertext/kernel/infrastructure/validation/__init__.py +1 -0
  302. higpertext/kernel/infrastructure/validation/contract_validator.py +163 -0
  303. higpertext/kernel/pkg_resources.py +38 -0
  304. higpertext/kernel/session_manager.py +319 -0
  305. higpertext/templates/env/generic-shell.yaml +21 -0
  306. higpertext/templates/env/node-vitest.yaml +27 -0
  307. higpertext/templates/env/python-pytest.yaml +29 -0
  308. higpertext/templates/html/commit_body.html +20 -0
  309. higpertext/templates/html/commit_diff.html +4 -0
  310. higpertext/templates/html/commit_index.html +29 -0
  311. higpertext/templates/html/commit_layer.html +11 -0
  312. higpertext/templates/html/commit_shell.html +28 -0
  313. higpertext/templates/html/graph_visualize.html +86 -0
  314. higpertext/templates/html/roadmap_body.html +12 -0
  315. higpertext/templates/html/roadmap_phase.html +5 -0
  316. higpertext/templates/html/roadmap_shell.html +29 -0
  317. higpertext/templates/markdown/commit_report.md +18 -0
  318. higpertext/templates/markdown/efficiency_report.md +12 -0
  319. higpertext/templates/markdown/roadmap_report.md +25 -0
  320. higpertext/templates/skills/best-practices.md +7 -0
  321. higpertext/templates/skills/clean-code.md +8 -0
  322. higpertext/templates/skills/ddd-standards.md +7 -0
  323. higpertext/templates/skills/tdd-practices.md +7 -0
  324. higpertext/templates/subagents/architect.md +7 -0
  325. higpertext/templates/subagents/test-engineer.md +7 -0
  326. higpertext/templates/workflows/build.json +23 -0
  327. higpertext/templates/workflows/compact.json +21 -0
  328. higpertext/templates/workflows/plan.json +59 -0
  329. higpertext/templates/workflows/review.json +26 -0
  330. higpertext/templates/workflows/spec.json +27 -0
  331. higpertext_cli-0.8.0.dist-info/METADATA +35 -0
  332. higpertext_cli-0.8.0.dist-info/RECORD +335 -0
  333. higpertext_cli-0.8.0.dist-info/WHEEL +5 -0
  334. higpertext_cli-0.8.0.dist-info/entry_points.txt +2 -0
  335. higpertext_cli-0.8.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,1110 @@
1
+ """higpertext Engine CLI — orquestador de capacidades, perfiles y workflows."""
2
+
3
+ import sys
4
+ import os
5
+ import json
6
+ import argparse
7
+ import subprocess # nosec B404
8
+ import shutil
9
+ from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
10
+ from higpertext.kernel.infrastructure.cli.arguments import parse_kv_args, pop_bool_flag
11
+ from higpertext.kernel.infrastructure.cli.capability_command_builder import (
12
+ build_command,
13
+ build_powershell_command,
14
+ extra_args,
15
+ resolve_entrypoint,
16
+ )
17
+ from higpertext.kernel.infrastructure.cli.capability_task_service import CapabilityTaskService
18
+ from higpertext.kernel.infrastructure.cli.parser_builder import (
19
+ add_agent_subparser,
20
+ add_health_subparser,
21
+ add_profile_init_subparsers,
22
+ add_roadmap_subparser,
23
+ add_session_workflow_subparsers,
24
+ add_task_ask_subparsers,
25
+ build_parser,
26
+ )
27
+ from higpertext.kernel.infrastructure.cli.agent_commands import (
28
+ dispatch_agent,
29
+ dispatch_agent_list,
30
+ dispatch_agent_register,
31
+ dispatch_agent_sync,
32
+ )
33
+ from higpertext.kernel.infrastructure.cli.profile_commands import (
34
+ dispatch_profile,
35
+ dispatch_profile_learn,
36
+ dispatch_profile_validate,
37
+ )
38
+ from higpertext.kernel.infrastructure.cli.roadmap_commands import dispatch_roadmap
39
+ from higpertext.kernel.infrastructure.cli.session_commands import (
40
+ parse_session_resources,
41
+ start_session,
42
+ )
43
+ from higpertext.kernel.infrastructure.cli.task_commands import dispatch_task
44
+ from higpertext.kernel.infrastructure.cli.task_result_reporter import (
45
+ build_memory_notes,
46
+ print_task_failure,
47
+ report_task_result,
48
+ )
49
+ from higpertext.kernel.infrastructure.cli.workflow_commands import (
50
+ dispatch_workflow,
51
+ resolve_workflow_params,
52
+ )
53
+ from higpertext.kernel.infrastructure.logger import get_logger
54
+ from higpertext.kernel.app_config import ASSISTANTS as _ASSISTANTS, RESERVED_PROFILES as _RESERVED_PROFILES_CFG
55
+ from pathlib import Path
56
+
57
+ _log = get_logger()
58
+
59
+ # ROOT_DIR: project root for the user's workspace (CWD when installed as CLI).
60
+ # PKG_DATA_DIR: where shipped static data lives (site-packages when installed, src/ in dev).
61
+
62
+
63
+ def _resolve_root_dir() -> Path:
64
+ env_override = os.environ.get("HIGPERTEXT_ENGINE_ROOT")
65
+ if env_override:
66
+ return Path(env_override).resolve()
67
+ # router.py is nested 6 levels deep from the repo root in development.
68
+ this_file = Path(__file__).resolve()
69
+ if "site-packages" in this_file.parts:
70
+ return Path.cwd()
71
+ return this_file.parent.parent.parent.parent.parent.parent
72
+
73
+
74
+ ROOT_DIR = _resolve_root_dir()
75
+
76
+
77
+ def _resolve_pkg_data_dir() -> Path:
78
+ try:
79
+ import higpertext_data
80
+
81
+ return Path(higpertext_data.__path__[0]).resolve()
82
+ except (ImportError, AttributeError, IndexError):
83
+ return ROOT_DIR / "src"
84
+
85
+
86
+ PKG_DATA_DIR = _resolve_pkg_data_dir()
87
+
88
+ _router_file = Path(__file__).resolve()
89
+ _kernel_core = str(_router_file.parent.parent.parent.parent) # src/core when installed
90
+ if _kernel_core not in sys.path:
91
+ sys.path.insert(0, _kernel_core)
92
+ # In development, also add the repo's src/core
93
+ _dev_candidate = _router_file.parent.parent.parent.parent.parent.parent
94
+ if "site-packages" not in str(_dev_candidate):
95
+ _dev_core = str(_dev_candidate / "src" / "core")
96
+ if _dev_core not in sys.path:
97
+ sys.path.insert(0, _dev_core)
98
+
99
+ # Helper for resolving mockable classes/functions — always via
100
+ # kernel._compat so tests patch one place
101
+
102
+
103
+ def _dep(name):
104
+ import higpertext.kernel._compat as _compat
105
+
106
+ if hasattr(_compat, name):
107
+ return getattr(_compat, name)
108
+ if name == "_start_session":
109
+ return _start_session
110
+ if name == "_parse_session_resources":
111
+ return _parse_session_resources
112
+ raise AttributeError(name)
113
+
114
+
115
+ try:
116
+ if "htx" in sys.modules and hasattr(sys.modules["htx"], "HigpertextEngine"):
117
+ HigpertextEngine = sys.modules["htx"].HigpertextEngine # noqa: F841
118
+ else:
119
+ from higpertext.kernel.engine import HigpertextEngine # noqa: F401
120
+ from higpertext.kernel.infrastructure.cli.cli_search import HigpertextSearchMixin
121
+ from higpertext.adapters.gemini_adapter.gemini_adapter import GeminiAdapter
122
+ from higpertext.adapters.claude_adapter.claude_adapter import ClaudeAdapter
123
+ from higpertext.adapters.copilot_adapter.copilot_adapter import CopilotAdapter
124
+ from higpertext.adapters.open_code_adapter.open_code_adapter import OpenCodeAdapter
125
+ except ImportError as e:
126
+ _log.warning(f"[!] Error cargando higpertext Engine en CLI: {e}")
127
+ sys.exit(1)
128
+
129
+ POSTMORTEM_DIR = ROOT_DIR.parent.parent / "Test" / "postmortem"
130
+ GOVERNANCE_DIR = ROOT_DIR.parent.parent / "Gobernanza"
131
+ DOCS_DIR = ROOT_DIR / "docs"
132
+
133
+ from higpertext.kernel.app_config import (
134
+ IGNORE_PATTERNS as _IGNORE_PATTERNS,
135
+ ASSISTANT_IGNORE_FILES as _ASSISTANT_IGNORE_FILES,
136
+ GITIGNORE_SECTION as _GITIGNORE_SECTION,
137
+ GUIDE_SPECS as _GUIDE_SPECS_RAW,
138
+ PROFILES_SECTION as _PROFILES_SECTION,
139
+ RULES_SECTION as _RULES_SECTION,
140
+ )
141
+
142
+ _IGNORE_CONTENT = "\n".join(_IGNORE_PATTERNS)
143
+ _ASSISTANT_IGNORE: dict[str, tuple[str, ...]] = {
144
+ k: tuple(v) for k, v in _ASSISTANT_IGNORE_FILES.items()
145
+ }
146
+ _GUIDE_SPECS: dict[str, tuple[str, str, list[str]]] = {
147
+ k: (v["filename"], v["title"], v["intro"]) for k, v in _GUIDE_SPECS_RAW.items()
148
+ }
149
+
150
+ # --- Maps assistant id to its rule-generation adapter function ---
151
+ _ADAPTER_MAP = {
152
+ "gemini": lambda ctx, root: GeminiAdapter.generate_rules(ctx, root),
153
+ "antigravity": lambda ctx, root: GeminiAdapter.generate_rules(ctx, root),
154
+ "claude": lambda ctx, root: ClaudeAdapter.generate_rules(ctx, root),
155
+ "copilot": lambda ctx, root: CopilotAdapter.generate_rules(ctx, root),
156
+ "github-copilot": lambda ctx, root: CopilotAdapter.generate_rules(ctx, root),
157
+ "opencode": lambda ctx, root: OpenCodeAdapter.generate_rules(ctx, root),
158
+ }
159
+
160
+
161
+
162
+
163
+ # --- CLI argument parsing helpers ---
164
+ def _parse_kv_args(raw_args: list[str]) -> dict:
165
+ """Parsea una lista ['--key', 'val', ...] en un dict."""
166
+ return parse_kv_args(raw_args)
167
+
168
+
169
+ class HigpertextHub(HigpertextSearchMixin):
170
+ """Orquestador principal del CLI de higpertext Engine."""
171
+
172
+ def __init__(self) -> None:
173
+ self.version = "5.0.0"
174
+ engine_cls = self._load_engine_cls()
175
+ self.engine = engine_cls(ROOT_DIR)
176
+
177
+ # Prefers already-loaded HigpertextEngine from htx.py module to avoid
178
+ # a double-import cycle when CLI is invoked via the launcher shim.
179
+ @staticmethod
180
+ def _load_engine_cls():
181
+ try:
182
+ if "htx" in sys.modules and hasattr(sys.modules["htx"], "HigpertextEngine"):
183
+ return sys.modules["htx"].HigpertextEngine
184
+ from higpertext.kernel.engine import HigpertextEngine as _NE # noqa: F811
185
+
186
+ return _NE
187
+ except (ImportError, AttributeError):
188
+ from higpertext.kernel.engine import HigpertextEngine as _NE2 # noqa: F811
189
+
190
+ return _NE2
191
+
192
+ # --- Project initialization ---
193
+ # Creates htx.py launcher, writes ignore files, injects initial profile.
194
+
195
+ def init_project(
196
+ self, profile_name: str | None, assistant: str, target_dir: str | None = None
197
+ ) -> None:
198
+ """Inicializa la estructura del asistente en el proyecto destino."""
199
+ project_root = self._resolve_project_root(target_dir)
200
+ self._update_gitignore(project_root)
201
+
202
+ _log.info(f"[*] Inicializando entorno vacío para '{assistant}' en: " f"{project_root}")
203
+ if profile_name:
204
+ _log.warning(
205
+ f" [!] 'init' inicializa el workspace en blanco. Para "
206
+ f"inyectar el rol '{profile_name}' ejecuta: htx"
207
+ f" profile load {profile_name} --assistant {assistant}"
208
+ )
209
+
210
+ self.generate_user_guide(project_root, assistant)
211
+
212
+ env_mgr = _dep("EnvironmentManager")(project_root)
213
+ env_data = env_mgr.initialize_environment(profile_name or "global", assistant)
214
+ initial_profile = profile_name or "global"
215
+ from higpertext.adapters.adapter_utils import get_adapter_class
216
+
217
+ adapter_cls = get_adapter_class(assistant)
218
+ try:
219
+ context = self.engine.get_agent_context(initial_profile, env_data=env_data)
220
+ except FileNotFoundError as e:
221
+ _log.warning(
222
+ f"[!] Perfil '{initial_profile}' no disponible en este entorno,"
223
+ f" omitiendo contexto: {e}"
224
+ )
225
+ context = None
226
+ if adapter_cls and hasattr(adapter_cls, "on_init") and context:
227
+ adapter_cls.on_init(context, project_root)
228
+ self._generate_dynamic_playbooks(project_root, assistant, copy_resources=False)
229
+ if context:
230
+ self._render_native_hooks(project_root, assistant, initial_profile, context)
231
+ self._refresh_semantic_graph(project_root)
232
+
233
+ @staticmethod
234
+ def _update_gitignore(project_root: Path) -> None:
235
+ """Añade los patrones de higpertext al .gitignore del proyecto destino.
236
+
237
+ Si el bloque ya existe (detectado por el encabezado), no lo duplica.
238
+ """
239
+ gitignore_path = project_root / ".gitignore"
240
+ marker = "# higpertext Engine - archivos generados (no versionar)"
241
+ existing = ""
242
+ if gitignore_path.exists():
243
+ try:
244
+ existing = gitignore_path.read_text(encoding="utf-8")
245
+ except OSError: # nosec B110
246
+ pass
247
+ if marker in existing:
248
+ return
249
+ with gitignore_path.open("a", encoding="utf-8") as f:
250
+ f.write(_GITIGNORE_SECTION)
251
+ _log.ok(f"[*] .gitignore actualizado en: {gitignore_path}")
252
+
253
+ @staticmethod
254
+ def _resolve_project_root(target_dir: str | None) -> Path:
255
+ if target_dir:
256
+ root = Path(target_dir).expanduser().resolve()
257
+ root.mkdir(parents=True, exist_ok=True)
258
+ return root
259
+ return Path.cwd()
260
+
261
+ @staticmethod
262
+ def _refresh_semantic_graph(project_root: Path) -> None:
263
+ try:
264
+ from higpertext.kernel.infrastructure.parser.semantic_graph import (
265
+ SemanticGraphGenerator,
266
+ )
267
+
268
+ _log.info("[*] Generando grafo semántico inicial...")
269
+ SemanticGraphGenerator.generate(project_root)
270
+ _log.ok("[SUCCESS] Grafo semántico generado en " ".higpertext/state/semantic_graph.md")
271
+ except (ImportError, OSError) as e:
272
+ _log.warning(f"[WARNING] No se pudo generar el grafo semántico: {e}")
273
+
274
+ # --- Dynamic playbooks ---
275
+ # Reads session state and rebuilds playbook .md files with active
276
+ # skill/subagent links injected. Called after session-start and
277
+ # profile load to keep playbooks in sync with the active session.
278
+
279
+ def _generate_dynamic_playbooks(
280
+ self, project_root: Path, assistant: str, copy_resources: bool = True
281
+ ) -> None:
282
+ """Genera los playbooks primarios adaptados al asistente."""
283
+ from higpertext.kernel.infrastructure.compilers import CompilerFactory
284
+ CompilerFactory.create("playbook", project_root, engine_root=ROOT_DIR, pkg_data_dir=PKG_DATA_DIR, engine=self.engine).compile(assistant, copy_resources)
285
+
286
+ # --- User guide generation ---
287
+ # Generates a Markdown guide file for the selected assistant and writes
288
+ # it to the project root. Content is assembled from _GUIDE_SPECS and
289
+ # module-level section constants (_PROFILES_SECTION, _RULES_SECTION).
290
+
291
+ def generate_user_guide(self, target_path: Path, assistant: str) -> None:
292
+ """Genera la guía del asistente con capacidades globales."""
293
+ from higpertext.kernel.infrastructure.compilers import CompilerFactory
294
+ CompilerFactory.create("guide", target_path, engine=self.engine).compile(assistant)
295
+
296
+ # --- Profile loading ---
297
+ # Validates the environment is initialized, then delegates rule generation
298
+ # to the assistant-specific adapter from _ADAPTER_MAP.
299
+
300
+ def _check_assistant_initialized(
301
+ self, env_mgr: "EnvironmentManager", assistant: str, project_root: Path
302
+ ) -> "dict | None":
303
+ """Verifica inicialización y retorna env_data o None."""
304
+ if not env_mgr.is_initialized():
305
+ _log.warning(
306
+ f"[!] Proyecto en {project_root} no inicializado. "
307
+ f"Ejecuta: htx init --assistant {assistant}"
308
+ )
309
+ return None
310
+ env_data = env_mgr.load_environment()
311
+ if assistant not in env_data.get("assistants", []):
312
+ _log.warning(
313
+ f"[!] '{assistant}' no está inicializado. Ejecuta: "
314
+ f"htx init --assistant {assistant}"
315
+ )
316
+ return None
317
+ return env_data
318
+
319
+ def _inject_profile_rules(
320
+ self, context: dict, assistant: str, profile_name: str, project_root: Path
321
+ ) -> None:
322
+ _log.info(
323
+ f"\n[*] Inyectando perfil '{profile_name.upper()}' para "
324
+ f"'{assistant}' en: {project_root}"
325
+ )
326
+ _log.info(f" {context['profile']['description']}")
327
+ generator = _ADAPTER_MAP.get(assistant)
328
+ if generator:
329
+ rule_file = generator(context, project_root)
330
+ _log.ok(f"[SUCCESS] Reglas inyectadas en: {rule_file}")
331
+ _log.info("\n Capacidades activas del perfil:")
332
+ for cap in context["active_capabilities"]:
333
+ _log.info(f" - {cap['id']}: {cap['description']}")
334
+
335
+ def _resolve_assistant_for_profile(self, env_mgr: "EnvironmentManager", assistant: str | None) -> str:
336
+ if assistant:
337
+ return assistant
338
+ env_data = env_mgr.load_environment() if env_mgr.is_initialized() else {}
339
+ return env_data.get("assistant") or "gemini"
340
+
341
+ def _print_profile_validation(self, profile_name: str) -> None:
342
+ success, errors, warnings = self.engine.validate_profile(profile_name)
343
+ if not success or warnings:
344
+ if errors:
345
+ _log.warning(
346
+ f"[!] ADVERTENCIA: El perfil '{profile_name}'"
347
+ " tiene inconsistencias o errores:"
348
+ )
349
+ for err in errors:
350
+ _log.info(f" - {err}")
351
+ if warnings:
352
+ _log.info(f"[*] ALERTA: Optimización del perfil '{profile_name}':")
353
+ for warn in warnings:
354
+ _log.info(f" - {warn}")
355
+ _log.warning("[!] Cargando el perfil de todos modos...\n")
356
+
357
+ def _get_assistants_to_render(self, assistant: str, env_data: dict) -> list[str]:
358
+ assistants_to_render = [assistant]
359
+ for a in env_data.get("assistants", []):
360
+ if a not in assistants_to_render:
361
+ assistants_to_render.append(a)
362
+ return assistants_to_render
363
+
364
+ def load_agent_profile(
365
+ self,
366
+ profile_name: str,
367
+ assistant: str | None = None,
368
+ target_dir: str | None = None,
369
+ ) -> None:
370
+ """Carga un perfil e inyecta sus reglas en el proyecto."""
371
+ try:
372
+ project_root = self._resolve_project_root(target_dir)
373
+ env_mgr = _dep("EnvironmentManager")(project_root)
374
+ assistant = self._resolve_assistant_for_profile(env_mgr, assistant)
375
+
376
+ env_data = self._check_assistant_initialized(env_mgr, assistant, project_root)
377
+ if env_data is None:
378
+ return
379
+
380
+ self._print_profile_validation(profile_name)
381
+
382
+ context = self.engine.get_agent_context(profile_name, env_data=env_data)
383
+ if not context:
384
+ return
385
+
386
+ self._inject_profile_rules(context, assistant, profile_name, project_root)
387
+ env_mgr.add_active_profile(profile_name)
388
+ self._generate_dynamic_playbooks(project_root, assistant, copy_resources=False)
389
+
390
+ for ast in self._get_assistants_to_render(assistant, env_data):
391
+ self._render_native_hooks(project_root, ast, profile_name, context)
392
+
393
+ self._auto_start_session(project_root, profile_name, assistant)
394
+ except (OSError, KeyError, ValueError) as e:
395
+ _log.warning(f"[!] Error gestionando perfil: {e}")
396
+
397
+ def _render_native_hooks(
398
+ self, project_root: Path, assistant: str, profile_name: str, context: dict
399
+ ) -> None:
400
+ """Compila hooks del perfil y regenera config nativa del asistente."""
401
+ from higpertext.kernel.infrastructure.compilers import CompilerFactory
402
+ compiler = CompilerFactory.create("hook", project_root, pkg_data_dir=PKG_DATA_DIR)
403
+ compiler.compile(profile_name, assistant, context)
404
+
405
+ def _auto_start_session(self, project_root: Path, profile_name: str, assistant: str) -> None:
406
+ """Inicia sesión automáticamente al cargar perfil si no hay una activa."""
407
+ try:
408
+ session_file = project_root / WORKSPACE_DIR_NAME / "state" / "session.json"
409
+ if session_file.exists():
410
+ data = json.loads(session_file.read_text(encoding="utf-8"))
411
+ # Si ya hay sesión activa para el mismo perfil y asistente, no relanzar
412
+ if (
413
+ data.get("status") == "active"
414
+ and data.get("profile") == profile_name
415
+ and data.get("assistant") == assistant
416
+ ):
417
+ return
418
+ session_mgr = _dep("SessionManager")(project_root, ROOT_DIR)
419
+ session_mgr.start_session(profile_name, assistant, None, None)
420
+ except Exception as e:
421
+ _log.warning(f"[WARNING] No se pudo iniciar sesión automáticamente: {e}")
422
+
423
+ # --- Agent lifecycle (no assistant) ---
424
+ # Initializes .higpertext/ from src/config + src/hooks without generating
425
+ # any assistant-specific files (.claude/, .opencode/, etc.)
426
+
427
+ _RESERVED_PROFILES = _RESERVED_PROFILES_CFG
428
+
429
+ def agent_init(self, profile_name: str, target_dir: str | None = None) -> None:
430
+ """Crea la estructura base del agente e inicializa su ambiente."""
431
+ if profile_name in self._RESERVED_PROFILES:
432
+ _log.error(f"[ERROR] '{profile_name}' es un perfil reservado del motor higpertext.")
433
+ _log.info(f" Nombres reservados: {', '.join(sorted(self._RESERVED_PROFILES))}")
434
+ return None
435
+
436
+ project_root = self._resolve_project_root(target_dir)
437
+ _log.info(f"[*] Creando agente '{profile_name}' en: {project_root}")
438
+
439
+ self._scaffold_agent_structure(project_root)
440
+
441
+ env_mgr = _dep("EnvironmentManager")(project_root)
442
+ env_data = env_mgr.initialize_environment(profile_name, "agent")
443
+
444
+ self._compile_hook_layers(project_root, profile_name)
445
+ self._refresh_semantic_graph(project_root)
446
+
447
+ _log.ok(f"[SUCCESS] Agente listo en: {project_root}")
448
+ _log.info(f" Perfil : {profile_name}")
449
+ _log.info(f" Ejecuta : htx agent status")
450
+ return env_data
451
+
452
+ def _scaffold_agent_structure(self, project_root: Path) -> None:
453
+ """Crea la estructura de carpetas base e inyecta perfiles y hooks globales."""
454
+ dirs = [
455
+ "src/higpertext/capabilities",
456
+ "src/config/profiles",
457
+ "src/config/governance",
458
+ "src/config/environments",
459
+ "src/config/templates",
460
+ "src/config/workflows",
461
+ "src/config/hooks/profiles",
462
+ "src/config/hooks/custom",
463
+ "content",
464
+ ]
465
+ for d in dirs:
466
+ (project_root / d).mkdir(parents=True, exist_ok=True)
467
+
468
+ self._copy_base_profiles(project_root)
469
+ self._copy_base_governance(project_root)
470
+ _log.info(f"[*] Estructura base creada en: {project_root / 'src'}")
471
+
472
+ def _copy_base_governance(self, project_root: Path) -> None:
473
+ """Copia guidelines_contract.json y security_guardrails.json al agente."""
474
+ import shutil as _shutil
475
+
476
+ engine_gov = PKG_DATA_DIR / "config" / "governance"
477
+ dest_gov = project_root / "src" / "config" / "governance"
478
+ copied = []
479
+ for gov_file in ("guidelines_contract.json", "security_guardrails.json"):
480
+ src = engine_gov / gov_file
481
+ dst = dest_gov / gov_file
482
+ if src.exists() and not dst.exists():
483
+ _shutil.copy2(src, dst)
484
+ copied.append(gov_file)
485
+ if copied:
486
+ _log.info(f"[*] Gobernanza base copiada: {', '.join(copied)}")
487
+
488
+ def _copy_base_profiles(self, project_root: Path) -> None:
489
+ """Copia agent_designer.json al nuevo agente. base_agent es interno del motor."""
490
+ engine_profiles = PKG_DATA_DIR / "config" / "profiles"
491
+ dest_profiles = project_root / "src" / "config" / "profiles"
492
+ src = engine_profiles / "agent_designer.json"
493
+ dst = dest_profiles / "agent_designer.json"
494
+ if src.exists() and not dst.exists():
495
+ import shutil as _shutil
496
+
497
+ _shutil.copy2(src, dst)
498
+ _log.info(f"[*] Perfil base copiado: agent_designer")
499
+
500
+ @staticmethod
501
+ def _copy_resource_files(sub_src: Path, sub_dest: Path, label: str) -> None:
502
+ import shutil as _shutil
503
+
504
+ for f in sub_src.rglob("*"):
505
+ if not f.is_file():
506
+ continue
507
+ if f.suffix not in (".json", ".py"):
508
+ continue
509
+ rel = f.relative_to(sub_src)
510
+ dst = sub_dest / rel
511
+ dst.parent.mkdir(parents=True, exist_ok=True)
512
+ _shutil.copy2(f, dst)
513
+ if f.suffix == ".json":
514
+ _log.info(f"[*] {label} instalada: {rel}")
515
+
516
+ def _install_source_resources(self, project_root: Path, source_path: Path) -> None:
517
+ src_root = source_path / "src"
518
+ for sub_src, sub_dest, label in [
519
+ (
520
+ src_root / "capabilities",
521
+ project_root / WORKSPACE_DIR_NAME / "capabilities",
522
+ "Capability",
523
+ ),
524
+ (
525
+ src_root / "config" / "hooks",
526
+ project_root / "src" / "config" / "hooks",
527
+ "Hook",
528
+ ),
529
+ (
530
+ src_root / "config" / "governance",
531
+ project_root / "src" / "config" / "governance",
532
+ "Governance",
533
+ ),
534
+ ]:
535
+ if sub_src.exists():
536
+ self._copy_resource_files(sub_src, sub_dest, label)
537
+
538
+ @staticmethod
539
+ def _install_profile_rules(project_root: Path, src_root: Path, profile_name: str) -> None:
540
+ import shutil as _shutil
541
+
542
+ profile_rules_src = (
543
+ src_root / "config" / "hooks" / "profiles" / profile_name / "profile_rules.json"
544
+ )
545
+ if not profile_rules_src.exists():
546
+ return
547
+ for assistant_hooks in (
548
+ project_root / ".claude" / "hooks" / "_rules",
549
+ project_root / ".gemini" / "hooks" / "_rules",
550
+ ):
551
+ if assistant_hooks.parent.parent.exists():
552
+ assistant_hooks.mkdir(parents=True, exist_ok=True)
553
+ _shutil.copy2(profile_rules_src, assistant_hooks / "profile_rules.json")
554
+ _log.info(f"[*] Profile rules instaladas en: {assistant_hooks}")
555
+
556
+ def install_profile(
557
+ self,
558
+ profile_name: str,
559
+ target_dir: str | None = None,
560
+ source: str | None = None,
561
+ ) -> None:
562
+ """Copia un perfil a .higpertext/profiles/ del proyecto activo."""
563
+ import shutil as _shutil
564
+
565
+ project_root = self._resolve_project_root(target_dir)
566
+ sub_path = "src/config/profiles" if source else "src/higpertext/profiles"
567
+ src = Path(source or ROOT_DIR) / sub_path / f"{profile_name}.json"
568
+ if not src.exists():
569
+ _log.error(f"[ERROR] Perfil '{profile_name}' no encontrado en: {src}")
570
+ return
571
+ dest = project_root / WORKSPACE_DIR_NAME / "profiles" / f"{profile_name}.json"
572
+ dest.parent.mkdir(parents=True, exist_ok=True)
573
+ _shutil.copy2(src, dest)
574
+ _log.ok(f"[SUCCESS] Perfil '{profile_name}' instalado en: {dest}")
575
+
576
+ if source:
577
+ source_path = Path(source)
578
+ self._install_source_resources(project_root, source_path)
579
+ self._install_profile_rules(project_root, source_path / "src", profile_name)
580
+ self._register_agent_dir(project_root, source_path)
581
+ self._copy_templates(source_path / "src", project_root)
582
+
583
+ def _copy_templates(self, src_root: Path, project_root: Path) -> None:
584
+ """Copia src/templates/ del agente externo al motor para resolución de skills."""
585
+ import shutil as _shutil
586
+
587
+ templates_src = src_root / "templates"
588
+ if not templates_src.exists():
589
+ return
590
+ templates_dst = project_root / WORKSPACE_DIR_NAME / "agent_templates"
591
+ templates_dst.mkdir(parents=True, exist_ok=True)
592
+ for f in templates_src.rglob("*"):
593
+ if f.is_file():
594
+ dst = templates_dst / f.relative_to(templates_src)
595
+ dst.parent.mkdir(parents=True, exist_ok=True)
596
+ _shutil.copy2(f, dst)
597
+ _log.info(f"[*] Templates del agente copiadas en: {templates_dst}")
598
+
599
+ def _register_agent_dir(self, project_root: Path, agent_dir: Path) -> None:
600
+ """Persiste agent_dir en environment.json para resolución de recursos externos."""
601
+ env_file = project_root / WORKSPACE_DIR_NAME / "config" / "environment.json"
602
+ try:
603
+ data = json.loads(env_file.read_text(encoding="utf-8"))
604
+ data["agent_dir"] = str(agent_dir.resolve())
605
+ env_file.write_text(json.dumps(data, indent=4, ensure_ascii=False), encoding="utf-8")
606
+ _log.info(f"[*] agent_dir registrado: {agent_dir.resolve()}")
607
+ except Exception as exc:
608
+ _log.warning(f"[!] No se pudo registrar agent_dir: {exc}")
609
+
610
+ def agent_status(self, target_dir: str | None = None) -> None:
611
+ """Muestra el estado del ambiente del agente."""
612
+ project_root = self._resolve_project_root(target_dir)
613
+ env_mgr = _dep("EnvironmentManager")(project_root)
614
+ if not env_mgr.is_initialized():
615
+ _log.warning("[!] Ambiente no inicializado. Ejecuta: htx agent init --profile <nombre>")
616
+ return
617
+ env_data = env_mgr.load_environment()
618
+ profiles = env_data.get("active_profiles", env_data.get("active_profile", "—"))
619
+ _log.info(f"\n── Agent Status ───────────────────────────")
620
+ _log.info(f" Perfil(es) : {profiles}")
621
+ _log.info(f" Entorno : {project_root / WORKSPACE_DIR_NAME / 'config' / 'environment.json'}")
622
+ hooks_cfg = project_root / WORKSPACE_DIR_NAME / "config" / "hooks_config.json"
623
+ hooks_status = "compilados" if hooks_cfg.exists() else "no compilados"
624
+ _log.info(f" Hooks : {hooks_status}")
625
+ _log.info("")
626
+ def agent_clean(self, target_dir: str | None = None) -> None:
627
+ """Limpia el estado efímero del agente sin borrar configuración base."""
628
+ project_root = self._resolve_project_root(target_dir)
629
+ session_file = project_root / WORKSPACE_DIR_NAME / "state" / "session.json"
630
+ if session_file.exists():
631
+ session_file.unlink()
632
+ _log.ok("[SUCCESS] Sesión efímera eliminada.")
633
+ else:
634
+ _log.info("[*] No hay sesión activa que limpiar.")
635
+
636
+ @staticmethod
637
+ def _compile_hook_layers(project_root: Path, profile_name: str) -> None:
638
+ """Compila las capas de hooks (global + perfil + custom) en hooks_config.json."""
639
+ from higpertext.kernel.infrastructure.compilers import CompilerFactory
640
+ CompilerFactory.create("hook", project_root, pkg_data_dir=PKG_DATA_DIR).compile(profile_name)
641
+
642
+ # --- Knowledge search ---
643
+ # Delegates to HigpertextSearchMixin._walk_search which traverses governance,
644
+ # docs, .memory and src/config/governance paths. Results are capped at
645
+ # 5 files and 3 snippets per file to avoid context overflow.
646
+
647
+ def ask_knowledge(self, query: str) -> None:
648
+ """Busca respuestas en gobernanza, docs, memoria y grafo semántico."""
649
+ _log.info(f"[*] Buscando información sobre: '{query}'...")
650
+ query_terms = [t.lower() for t in query.split() if t]
651
+ if not query_terms:
652
+ _log.warning("[!] La consulta de búsqueda está vacía.")
653
+ return
654
+ project_dir = Path(os.getcwd()).resolve()
655
+ search_paths = [
656
+ GOVERNANCE_DIR,
657
+ DOCS_DIR,
658
+ ROOT_DIR / ".memory",
659
+ PKG_DATA_DIR / "config" / "governance",
660
+ project_dir / "src" / "config" / "governance",
661
+ project_dir / "docs",
662
+ project_dir / ".memory",
663
+ ]
664
+ search_paths = [p for p in search_paths if p.exists()]
665
+ matches = self._walk_search(search_paths, query_terms, project_dir)
666
+
667
+ # Búsqueda en el grafo semántico — proyecto primero, luego Engine
668
+ graph_path = project_dir / WORKSPACE_DIR_NAME / "state" / "semantic_graph.json"
669
+ if not graph_path.exists():
670
+ graph_path = ROOT_DIR / WORKSPACE_DIR_NAME / "state" / "semantic_graph.json"
671
+ graph_matches = self._search_semantic_graph(graph_path, query_terms, project_dir)
672
+ if graph_matches:
673
+ _log.info("\n🔍 Símbolos encontrados en el grafo semántico:")
674
+ self._print_search_results(graph_matches)
675
+
676
+ self._print_search_results(matches)
677
+
678
+ # --- Task runner ---
679
+ # Resolves capability → builds subprocess command → validates contract
680
+ # → persists result in .memory/ via memory-manager (skipped for itself).
681
+
682
+ @staticmethod
683
+ def _read_session_state(project_root: Path) -> tuple[bool, str | None]:
684
+ from higpertext.kernel.infrastructure.compilers.playbook_compiler import PlaybookCompiler
685
+ compiler = PlaybookCompiler(project_root, None, None, None)
686
+ return compiler._read_session_state()
687
+
688
+ @staticmethod
689
+ def _collect_skill_links(skills_dest: Path, project_root: Path) -> list[str]:
690
+ from higpertext.kernel.infrastructure.compilers.playbook_compiler import PlaybookCompiler
691
+ compiler = PlaybookCompiler(project_root, None, None, None)
692
+ return compiler._collect_skill_links(skills_dest)
693
+
694
+ @staticmethod
695
+ def _collect_subagent_links(subagents_dest: Path, project_root: Path) -> list[str]:
696
+ from higpertext.kernel.infrastructure.compilers.playbook_compiler import PlaybookCompiler
697
+ compiler = PlaybookCompiler(project_root, None, None, None)
698
+ return compiler._collect_subagent_links(subagents_dest)
699
+
700
+ def _build_playbook_strings(
701
+ self,
702
+ has_session: bool,
703
+ session_id: str | None,
704
+ skills_dest: Path,
705
+ subagents_dest: Path,
706
+ project_root: Path,
707
+ ) -> tuple[str, str]:
708
+ from higpertext.kernel.infrastructure.compilers.playbook_compiler import PlaybookCompiler
709
+ compiler = PlaybookCompiler(project_root, None, None, None)
710
+ return compiler._build_playbook_strings(has_session, session_id, skills_dest, subagents_dest)
711
+
712
+ def _task_service(self) -> CapabilityTaskService:
713
+ return CapabilityTaskService(self.engine, ROOT_DIR, PKG_DATA_DIR, _log, _dep)
714
+
715
+ # Capability resolution: profile search first, then custom fallback.
716
+ def _resolve_from_profiles(
717
+ self, task_name: str, profiles: list[str]
718
+ ) -> tuple[dict | None, str | None]:
719
+ return self._task_service().resolve_from_profiles(task_name, profiles)
720
+
721
+ def _resolve_custom_capability(self, task_name: str) -> tuple[dict | None, str | None]:
722
+ return self._task_service().resolve_custom_capability(task_name)
723
+
724
+ def _resolve_capability(
725
+ self, task_name: str, active_profile: str
726
+ ) -> tuple[dict | None, str | None]:
727
+ """Busca la capacidad en perfil activo, global y custom."""
728
+ return self._task_service().resolve_capability(task_name, active_profile)
729
+
730
+ @staticmethod
731
+ def _extra_args(params: dict, prefix: str = "--") -> list[str]:
732
+ return extra_args(params, prefix)
733
+
734
+ def _resolve_entrypoint(self, entrypoint: str, base_dir: str = "") -> Path:
735
+ """Resuelve el path del entrypoint con prioridad:
736
+ 1. Absoluto y existente → se usa tal cual
737
+ 2. Relativo a base_dir (capabilities custom con _base_dir anotado)
738
+ 3. Relativo a cwd/{WORKSPACE_DIR_NAME}/capabilities/ (extensiones instaladas)
739
+ 4. Relativo a ROOT_DIR/src (capabilities built-in)
740
+ """
741
+ return resolve_entrypoint(entrypoint, PKG_DATA_DIR, Path(os.getcwd()), base_dir)
742
+
743
+ def _build_cmd(
744
+ self,
745
+ entrypoint: str,
746
+ language: str,
747
+ params: dict,
748
+ base_dir: str = "",
749
+ cap_id: str = "",
750
+ ) -> list[str]:
751
+ return build_command(
752
+ entrypoint,
753
+ language,
754
+ params,
755
+ ROOT_DIR,
756
+ PKG_DATA_DIR,
757
+ Path(os.getcwd()),
758
+ base_dir=base_dir,
759
+ cap_id=cap_id,
760
+ )
761
+
762
+ def _build_ps_cmd(self, path_to_script: Path, params: dict) -> list[str]:
763
+ return build_powershell_command(path_to_script, params)
764
+
765
+ @staticmethod
766
+ def _build_memory_notes(
767
+ task_name: str,
768
+ params: dict,
769
+ result: subprocess.CompletedProcess,
770
+ contract_success: bool,
771
+ contract_errors: list,
772
+ ) -> str:
773
+ return build_memory_notes(task_name, params, result, contract_success, contract_errors)
774
+
775
+ def _save_memory(
776
+ self,
777
+ task_name: str,
778
+ params: dict,
779
+ result: subprocess.CompletedProcess,
780
+ contract_success: bool,
781
+ contract_errors: list,
782
+ ) -> None:
783
+ self._task_service().save_memory(
784
+ task_name, params, result, contract_success, contract_errors
785
+ )
786
+
787
+ def _get_active_profile(self) -> str:
788
+ return self._task_service().get_active_profile()
789
+
790
+ @staticmethod
791
+ def _stream_subprocess(cmd: list[str]) -> subprocess.CompletedProcess:
792
+ """Ejecuta un subproceso con streaming línea a línea hacia stdout."""
793
+ import io
794
+
795
+ stdout_lines: list[str] = []
796
+ stderr_lines: list[str] = []
797
+ try:
798
+ with subprocess.Popen( # nosec B603 B607
799
+ cmd,
800
+ cwd=os.getcwd(),
801
+ stdout=subprocess.PIPE,
802
+ stderr=subprocess.PIPE,
803
+ text=True,
804
+ bufsize=1,
805
+ ) as proc:
806
+ # Stream stdout en tiempo real
807
+ for line in proc.stdout or io.StringIO():
808
+ _log.info(line, end="", flush=True)
809
+ stdout_lines.append(line)
810
+ # Captura stderr al cerrar
811
+ stderr_data = proc.stderr.read() if proc.stderr else ""
812
+ if stderr_data:
813
+ stderr_lines.append(stderr_data)
814
+ proc.wait()
815
+ returncode = proc.returncode
816
+ except OSError as e:
817
+ return subprocess.CompletedProcess(args=cmd, returncode=1, stdout="", stderr=str(e))
818
+ return subprocess.CompletedProcess(
819
+ args=cmd,
820
+ returncode=returncode,
821
+ stdout="".join(stdout_lines),
822
+ stderr="".join(stderr_lines),
823
+ )
824
+
825
+ def _execute_task_cmd(
826
+ self, task_name: str, target_cap: dict, params: dict, stream: bool = False
827
+ ) -> subprocess.CompletedProcess:
828
+ """Construye y ejecuta el comando de la tarea con cache + retry + streaming."""
829
+ return self._task_service().execute_task_cmd(
830
+ task_name, target_cap, params, self._stream_subprocess, stream=stream
831
+ )
832
+
833
+ def _validate_contract(
834
+ self, target_cap: dict, params: dict, result: subprocess.CompletedProcess
835
+ ) -> tuple[bool, list]:
836
+ if result.returncode != 0:
837
+ return True, []
838
+ return self._task_service().validate_contract(target_cap, params, result)
839
+
840
+ @staticmethod
841
+ def _warn_missing_pat(task_name: str) -> None:
842
+ CapabilityTaskService(None, ROOT_DIR, PKG_DATA_DIR, _log, _dep).warn_missing_pat(task_name)
843
+
844
+ def run_task(
845
+ self, task_name: str, params: dict, stream: bool = False, no_cache: bool = False
846
+ ) -> bool:
847
+ """Ejecuta una capacidad técnica validando el perfil activo."""
848
+ try:
849
+ active_profile = self._get_active_profile()
850
+ target_cap, found_profile = self._resolve_capability(task_name, active_profile)
851
+ if not target_cap:
852
+ _log.warning(
853
+ f"[!] Capacidad '{task_name}' no disponible para el "
854
+ f"perfil '{active_profile.upper()}'."
855
+ )
856
+ return False
857
+ _log.info(f"[*] Capacidad '{task_name}' resuelta: {found_profile.upper()}")
858
+ validation = _dep("normalize_and_validate_params")(target_cap, params)
859
+ if not validation.ok:
860
+ _log.error(f"[ERROR] Parámetros inválidos para '{task_name}':")
861
+ for err in validation.errors:
862
+ _log.info(f" - {err}")
863
+ return False
864
+ for warning in validation.warnings:
865
+ _log.warning(f"[PARAM] {warning}")
866
+ params = validation.params
867
+ self._warn_missing_pat(task_name)
868
+ _log.info(f"[*] Ejecutando tarea: {task_name}")
869
+
870
+ if no_cache:
871
+ cache = _dep("get_capability_cache")()
872
+ cache.invalidate(target_cap.get("id", task_name))
873
+
874
+ result = self._execute_task_cmd(task_name, target_cap, params, stream=stream)
875
+ ok, errs = self._validate_contract(target_cap, params, result)
876
+ status = self._report_task_result(task_name, result, ok, errs)
877
+ if task_name != "memory-manager":
878
+ self._save_memory(task_name, params, result, ok, errs)
879
+ return status
880
+ except (OSError, ValueError) as e:
881
+ _log.warning(f"[!] Error inesperado: {e}")
882
+ return False
883
+
884
+ @staticmethod
885
+ def _print_task_failure(
886
+ task_name: str,
887
+ result: subprocess.CompletedProcess,
888
+ contract_success: bool,
889
+ contract_errors: list,
890
+ ) -> None:
891
+ print_task_failure(task_name, result, contract_success, contract_errors, _log)
892
+
893
+ @staticmethod
894
+ def _report_task_result(
895
+ task_name: str,
896
+ result: subprocess.CompletedProcess,
897
+ contract_success: bool,
898
+ contract_errors: list,
899
+ ) -> bool:
900
+ return report_task_result(task_name, result, contract_success, contract_errors, _log)
901
+
902
+ # --- Workflow runner ---
903
+ # Loads a workflow definition, resolves its parameters from CLI args,
904
+ # and executes each step in the chain sequentially, aborting on failure
905
+ # unless the step specifies on_failure != "abort".
906
+
907
+ def run_workflow(self, workflow_id: str, cli_params: dict) -> bool:
908
+ """Ejecuta un flujo de trabajo cargándolo y ejecutando su cadena."""
909
+ try:
910
+ workflow = self.engine.workflows.load_workflow(workflow_id)
911
+ except FileNotFoundError as e:
912
+ _log.warning(f"[!] {e}")
913
+ return False
914
+
915
+ _log.info(f"[*] Ejecutando flujo: {workflow.get('name', workflow_id)}")
916
+ params = self._resolve_workflow_params(workflow, cli_params)
917
+ return self._execute_workflow_chain(workflow.get("execution_chain", []), params)
918
+
919
+ def list_workflows_cli(self) -> None:
920
+ _log.info("[*] Flujos de trabajo disponibles:")
921
+ for wf in self.engine.workflows.list_workflows():
922
+ _log.info(f" - {wf['id']}: {wf['name']} ({wf['description']})")
923
+
924
+ @staticmethod
925
+ def _resolve_workflow_params(workflow: dict, cli_params: dict) -> dict:
926
+ return resolve_workflow_params(workflow, cli_params)
927
+
928
+ def _execute_workflow_chain(self, chain: list, params: dict) -> bool:
929
+ # --- Feature 2: Parallel Executor ---
930
+ executor = _dep("ParallelExecutor")(run_task_fn=self.run_task)
931
+ return executor.execute_chain(
932
+ chain=chain,
933
+ params=params,
934
+ interpolate_fn=self._interpolate_params,
935
+ )
936
+
937
+ def _interpolate_str(self, val: str, resolved_params: dict) -> str:
938
+ try:
939
+ return val.format(**resolved_params)
940
+ except KeyError as e:
941
+ _log.warning(f"[Warning] Placeholder {e} no provisto: {val}")
942
+ return val
943
+
944
+ def _interpolate_val(self, val: object, resolved_params: dict) -> object:
945
+ if isinstance(val, str):
946
+ return self._interpolate_str(val, resolved_params)
947
+ if isinstance(val, dict):
948
+ return {k: self._interpolate_val(v, resolved_params) for k, v in val.items()}
949
+ if isinstance(val, list):
950
+ return [self._interpolate_val(i, resolved_params) for i in val]
951
+ return val
952
+
953
+ def _interpolate_params(self, step_params: dict, resolved_params: dict) -> dict:
954
+ return {k: self._interpolate_val(v, resolved_params) for k, v in step_params.items()}
955
+
956
+
957
+ # --- CLI subcommand builder helpers ---
958
+ # Each helper registers one logical group of subcommands on the subparser.
959
+ # Split to keep _build_parser() under 30 lines.
960
+ def _add_task_ask_subparsers(subs: argparse.Action) -> None:
961
+ add_task_ask_subparsers(subs)
962
+
963
+
964
+ def _add_profile_init_subparsers(subs: argparse.Action) -> None:
965
+ add_profile_init_subparsers(subs, _ASSISTANTS)
966
+
967
+
968
+ def _add_session_workflow_subparsers(subs: argparse.Action) -> None:
969
+ add_session_workflow_subparsers(subs, _ASSISTANTS)
970
+
971
+
972
+ def _add_health_subparser(subs: argparse.Action) -> None:
973
+ add_health_subparser(subs)
974
+
975
+
976
+ def _add_agent_subparser(subs: argparse.Action) -> None:
977
+ add_agent_subparser(subs)
978
+
979
+
980
+ def _add_roadmap_subparser(subs: argparse.Action) -> None:
981
+ add_roadmap_subparser(subs)
982
+
983
+
984
+ def _build_parser() -> argparse.ArgumentParser:
985
+ return build_parser(_ASSISTANTS)
986
+
987
+
988
+ # --- Session lifecycle dispatchers ---
989
+ # Decoupled from HigpertextHub to keep the class focused on orchestration logic.
990
+ def _parse_session_resources(
991
+ args: argparse.Namespace,
992
+ ) -> tuple[list | None, list | None]:
993
+ return parse_session_resources(args)
994
+
995
+
996
+ def _start_session(
997
+ nexus: "HigpertextHub",
998
+ session_mgr: "SessionManager",
999
+ args: argparse.Namespace,
1000
+ default_profile: str,
1001
+ default_assistant: str,
1002
+ ) -> None:
1003
+ start_session(nexus, session_mgr, args, default_profile, default_assistant)
1004
+
1005
+
1006
+ def _dispatch_session(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1007
+ session_mgr = _dep("SessionManager")(Path.cwd(), ROOT_DIR)
1008
+ assistant, profile = session_mgr.get_assistant_and_profile()
1009
+ if args.action == "start":
1010
+ _dep("_start_session")(nexus, session_mgr, args, profile, assistant)
1011
+ elif args.action == "clean":
1012
+ session_mgr.clean_session()
1013
+ nexus.load_agent_profile(profile, assistant)
1014
+
1015
+
1016
+ def _dispatch_workflow(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1017
+ dispatch_workflow(nexus, args)
1018
+
1019
+
1020
+ def _pop_bool_flag(task_args: list[str], flag: str, default: bool) -> tuple[bool, list[str]]:
1021
+ return pop_bool_flag(task_args, flag, default)
1022
+
1023
+
1024
+ def _dispatch_task(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1025
+ dispatch_task(nexus, args)
1026
+
1027
+
1028
+ def _dispatch_health(args: argparse.Namespace) -> None:
1029
+ from higpertext.kernel.infrastructure.diagnostics.health import run_health_check
1030
+
1031
+ sys.exit(run_health_check(verbose=getattr(args, "verbose", False)))
1032
+
1033
+
1034
+ def _dispatch_doctor(args: argparse.Namespace) -> None:
1035
+ from higpertext.capabilities.common.scripts.doctor import main as doctor_main
1036
+
1037
+ doctor_main(["--json", "true"] if getattr(args, "json", False) else [])
1038
+
1039
+
1040
+ def _dispatch_command(
1041
+ nexus: "HigpertextHub", args: argparse.Namespace, parser: argparse.ArgumentParser
1042
+ ) -> None:
1043
+ """Despacha el subcomando al handler correspondiente."""
1044
+ handlers = {
1045
+ "task": lambda: _dispatch_task(nexus, args),
1046
+ "health": lambda: _dispatch_health(args),
1047
+ "doctor": lambda: _dispatch_doctor(args),
1048
+ "workflow": lambda: _dispatch_non_task(nexus, args),
1049
+ "ask": lambda: _dispatch_non_task(nexus, args),
1050
+ "profile": lambda: _dispatch_non_task(nexus, args),
1051
+ "init": lambda: _dispatch_non_task(nexus, args),
1052
+ "session": lambda: _dispatch_non_task(nexus, args),
1053
+ "roadmap": lambda: _dispatch_non_task(nexus, args),
1054
+ "agent": lambda: _dispatch_non_task(nexus, args),
1055
+ }
1056
+ handler = handlers.get(args.command)
1057
+ if handler is None:
1058
+ parser.print_help()
1059
+ return
1060
+ handler()
1061
+
1062
+
1063
+ def _dispatch_roadmap(args: argparse.Namespace) -> None:
1064
+ """Gestiona roadmaps en .higpertext/config/roadmaps/."""
1065
+ dispatch_roadmap(args, _log)
1066
+
1067
+
1068
+ def _dispatch_profile_learn(args: argparse.Namespace) -> None:
1069
+ """Extrae patrones, calibra pesos y actualiza learned_profile.json."""
1070
+ dispatch_profile_learn(args, ROOT_DIR)
1071
+
1072
+
1073
+ def _dispatch_profile_validate(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1074
+ dispatch_profile_validate(nexus, args, _log)
1075
+
1076
+
1077
+ def _dispatch_agent_register(args: argparse.Namespace) -> None:
1078
+ dispatch_agent_register(args, _log, ROOT_DIR)
1079
+
1080
+
1081
+ def _dispatch_agent_sync(args: argparse.Namespace) -> None:
1082
+ dispatch_agent_sync(args, _log, ROOT_DIR)
1083
+
1084
+
1085
+ def _dispatch_agent_list() -> None:
1086
+ dispatch_agent_list(_log, ROOT_DIR)
1087
+
1088
+
1089
+ def _dispatch_agent(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1090
+ dispatch_agent(nexus, args, _log, ROOT_DIR)
1091
+
1092
+
1093
+ def _dispatch_non_task(nexus: "HigpertextHub", args: argparse.Namespace) -> None:
1094
+ handlers = {
1095
+ "workflow": lambda: _dispatch_workflow(nexus, args),
1096
+ "ask": lambda: nexus.ask_knowledge(args.query),
1097
+ "profile": lambda: dispatch_profile(nexus, args, _log, ROOT_DIR),
1098
+ "init": lambda: nexus.init_project(args.profile, args.assistant, args.target),
1099
+ "agent": lambda: _dispatch_agent(nexus, args),
1100
+ "roadmap": lambda: _dispatch_roadmap(args),
1101
+ "session": lambda: _dispatch_session(nexus, args),
1102
+ }
1103
+ handlers[args.command]()
1104
+
1105
+
1106
+ def main() -> None:
1107
+ """Punto de entrada del CLI de higpertext Engine."""
1108
+ parser = _build_parser()
1109
+ args = parser.parse_args()
1110
+ _dispatch_command(HigpertextHub(), args, parser)