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,210 @@
1
+ """higpertext Memory Manager — persiste acciones, aprendizajes y contexto del agente en .memory/."""
2
+
3
+ import json
4
+ import argparse
5
+ import secrets
6
+ import time
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ from higpertext.kernel.infrastructure.logger import get_logger
11
+ _log = get_logger()
12
+
13
+
14
+ def generate_action_id() -> str:
15
+ """Genera un action_id único con timestamp y sufijo criptográfico."""
16
+ timestamp = int(time.time())
17
+ rand_suffix = secrets.token_hex(2)
18
+ return f"act_{timestamp}_{rand_suffix}"
19
+
20
+
21
+ def _parse_csv(value: str | None) -> list[str]:
22
+ """Parsea una cadena separada por comas en una lista limpia."""
23
+ if not value:
24
+ return []
25
+ return [item.strip() for item in value.split(",") if item.strip()]
26
+
27
+
28
+ def _build_entry(
29
+ action: str,
30
+ status: str,
31
+ notes: str,
32
+ action_id: str,
33
+ learned: list,
34
+ tags: list,
35
+ failure_root_cause: str | None,
36
+ ) -> dict:
37
+ """Construye el diccionario de entrada para el journal."""
38
+ return {
39
+ "action_id": action_id,
40
+ "timestamp": datetime.now().isoformat(),
41
+ "action": action,
42
+ "status": status,
43
+ "tags": tags,
44
+ "notes": notes,
45
+ "learned": learned,
46
+ "failure_root_cause": failure_root_cause if status == "failure" else None,
47
+ }
48
+
49
+
50
+ def _load_json_file(path: Path, default):
51
+ """Carga un archivo JSON; retorna default si no existe o hay error."""
52
+ if not path.exists():
53
+ return default
54
+ try:
55
+ return json.loads(path.read_text(encoding="utf-8"))
56
+ except (OSError, json.JSONDecodeError):
57
+ return default
58
+
59
+
60
+ def _update_learnings(learnings: dict, tags: list, entry: dict, learned: list, notes: str) -> None:
61
+ """Actualiza el dict de learnings con la nueva entrada por cada tag."""
62
+ for tag in tags:
63
+ if tag not in learnings:
64
+ learnings[tag] = []
65
+ if learned or notes:
66
+ learnings[tag].append(
67
+ {
68
+ "action_id": entry["action_id"],
69
+ "timestamp": entry["timestamp"],
70
+ "action": entry["action"],
71
+ "status": entry["status"],
72
+ "notes": notes,
73
+ "learned": learned,
74
+ "failure_root_cause": entry["failure_root_cause"],
75
+ }
76
+ )
77
+
78
+
79
+ def save_memory(
80
+ action: str,
81
+ status: str,
82
+ notes: str,
83
+ action_id: str | None = None,
84
+ learned_str: str | None = None,
85
+ failure_root_cause: str | None = None,
86
+ tags_str: str | None = None,
87
+ ) -> None:
88
+ """Persiste una acción en journal.json, learnings.json y regenera context.md."""
89
+ memory_dir = Path(".memory")
90
+ memory_dir.mkdir(exist_ok=True)
91
+
92
+ journal_file = memory_dir / "journal.json"
93
+ learnings_file = memory_dir / "learnings.json"
94
+ context_file = memory_dir / "context.md"
95
+
96
+ action_id = action_id or generate_action_id()
97
+ learned = _parse_csv(learned_str)
98
+ tags = _parse_csv(tags_str) or ["general"]
99
+ tags = [t.lower() for t in tags]
100
+
101
+ entry = _build_entry(action, status, notes, action_id, learned, tags, failure_root_cause)
102
+
103
+ journal: list = _load_json_file(journal_file, [])
104
+ journal.append(entry)
105
+ journal_file.write_text(json.dumps(journal, indent=2, ensure_ascii=False), encoding="utf-8")
106
+
107
+ learnings: dict = _load_json_file(learnings_file, {})
108
+ _update_learnings(learnings, tags, entry, learned, notes)
109
+ learnings_file.write_text(json.dumps(learnings, indent=2, ensure_ascii=False), encoding="utf-8")
110
+
111
+ regenerate_context_markdown(journal, learnings, context_file)
112
+ _log.ok(f"[SUCCESS] Memoria registrada en .memory/. Action ID: {action_id}")
113
+
114
+
115
+ def _render_learnings_section(learnings: dict) -> list[str]:
116
+ """Renderiza la sección de aprendizajes agrupados por categoría."""
117
+ lines = ["\n## Aprendizajes y Lecciones Clave"]
118
+ has_learnings = False
119
+ for tag, items in sorted(learnings.items()):
120
+ tag_entries = [lrn for item in items for lrn in item.get("learned", [])]
121
+ if not tag_entries:
122
+ continue
123
+ has_learnings = True
124
+ lines.append(f"\n### Categoría: `{tag.upper()}`")
125
+ for entry in sorted(set(tag_entries)):
126
+ lines.append(f"- {entry}")
127
+ if not has_learnings:
128
+ lines.append(
129
+ "\n*No se han registrado aprendizajes específicos aún. "
130
+ "Los aprendizajes se acumulan a través del parámetro `--learned`.*"
131
+ )
132
+ return lines
133
+
134
+
135
+ def _render_failures_section(journal: list) -> list[str]:
136
+ """Renderiza la sección de historial de fallas con causa raíz."""
137
+ lines = ["\n---\n\n## Historial de Fallas y Causa Raíz"]
138
+ failures = [j for j in journal if j["status"] == "failure"]
139
+ if not failures:
140
+ lines.append("\n*No se han registrado fallas de ejecución en el historial de memoria.*")
141
+ return lines
142
+ lines.append("\n| ID | Acción | Causa Raíz | Notas | Timestamp |")
143
+ lines.append("|---|---|---|---|---|")
144
+ for f in reversed(failures[-10:]):
145
+ root_cause = f.get("failure_root_cause") or "No especificada"
146
+ lines.append(
147
+ f"| `{f['action_id']}` | `{f['action']}` | **{root_cause}** "
148
+ f"| {f['notes']} | `{f['timestamp']}` |"
149
+ )
150
+ return lines
151
+
152
+
153
+ def _render_journal_section(journal: list) -> list[str]:
154
+ """Renderiza la sección del historial secuencial reciente."""
155
+ lines = ["\n---\n\n## Historial Reciente de Operaciones"]
156
+ lines.append("\n| ID | Timestamp | Acción | Estado | Tags |")
157
+ lines.append("|---|---|---|---|---|")
158
+ for j in reversed(journal[-15:]):
159
+ tags_str = ", ".join(f"`{t}`" for t in j.get("tags", []))
160
+ symbol = "SUCCESS" if j["status"] == "success" else "FAILURE"
161
+ lines.append(
162
+ f"| `{j['action_id']}` | `{j['timestamp'][:19]}` "
163
+ f"| `{j['action']}` | {symbol} | {tags_str} |"
164
+ )
165
+ return lines
166
+
167
+
168
+ def regenerate_context_markdown(journal: list, learnings: dict, context_file: Path) -> None:
169
+ """Genera un reporte Markdown limpio y estructurado de la memoria acumulada."""
170
+ lines = [
171
+ "# Registro de Memoria y Aprendizaje del Agente",
172
+ "\nEste archivo es autogenerado por el `memory-manager` de higpertext.",
173
+ "\n---",
174
+ ]
175
+ lines.extend(_render_learnings_section(learnings))
176
+ lines.extend(_render_failures_section(journal))
177
+ lines.extend(_render_journal_section(journal))
178
+ context_file.write_text("\n".join(lines), encoding="utf-8")
179
+
180
+
181
+ def _build_parser() -> argparse.ArgumentParser:
182
+ """Construye el parser CLI compatible con capabilities_runner."""
183
+ parser = argparse.ArgumentParser()
184
+ parser.add_argument("--action", required=True)
185
+ parser.add_argument("--status", required=True, choices=("success", "failure"))
186
+ parser.add_argument("--notes", required=True)
187
+ parser.add_argument("--action_id", required=False)
188
+ parser.add_argument("--learned", required=False)
189
+ parser.add_argument("--failure_root_cause", required=False)
190
+ parser.add_argument("--tags", required=False)
191
+ return parser
192
+
193
+
194
+ def main(argv: list[str] | None = None) -> None:
195
+ """Punto de entrada de la capability common.memory-manager."""
196
+ args = _build_parser().parse_args(argv)
197
+
198
+ save_memory(
199
+ action=args.action,
200
+ status=args.status,
201
+ notes=args.notes,
202
+ action_id=args.action_id,
203
+ learned_str=args.learned,
204
+ failure_root_cause=args.failure_root_cause,
205
+ tags_str=args.tags,
206
+ )
207
+
208
+
209
+ if __name__ == "__main__":
210
+ main()
@@ -0,0 +1,63 @@
1
+ """higpertext Presentation Engine — convierte Markdown en presentaciones HTML Premium."""
2
+
3
+ import argparse
4
+ import shutil
5
+ from pathlib import Path
6
+
7
+ from higpertext.kernel.infrastructure.logger import get_logger
8
+ _log = get_logger()
9
+
10
+ _SCRIPT_DIR = Path(__file__).parent.absolute()
11
+ _CAP_ROOT = _SCRIPT_DIR.parent
12
+ _ASSETS_DIR = _CAP_ROOT / "assets"
13
+
14
+
15
+ def _copy_js_assets(output_file: Path) -> None:
16
+ """Copia las librerías JS al directorio de salida."""
17
+ output_js_dir = output_file.parent / "assets" / "js"
18
+ output_js_dir.mkdir(parents=True, exist_ok=True)
19
+ source_js_dir = _ASSETS_DIR / "js"
20
+ if source_js_dir.exists():
21
+ for js_file in source_js_dir.glob("*.js"):
22
+ shutil.copy(js_file, output_js_dir)
23
+
24
+
25
+ def _copy_css_assets(output_file: Path) -> None:
26
+ """Copia los archivos CSS al directorio de salida."""
27
+ output_css_dir = output_file.parent / "assets" / "css"
28
+ output_css_dir.mkdir(parents=True, exist_ok=True)
29
+ source_css_dir = _ASSETS_DIR / "css"
30
+ if source_css_dir.exists():
31
+ for css_file in source_css_dir.glob("*.css"):
32
+ shutil.copy(css_file, output_css_dir)
33
+
34
+
35
+ def generate_presentation(md_path: str, output_path: str) -> None:
36
+ """Genera una presentación HTML a partir de un archivo Markdown."""
37
+ input_file = Path(md_path)
38
+ if not input_file.exists():
39
+ _log.error(f"Error: Archivo {md_path} no encontrado.")
40
+ return
41
+
42
+ template_path = _ASSETS_DIR / "base-template.html"
43
+ if not template_path.exists():
44
+ _log.error(f"Error: No se encuentra la plantilla base en {template_path}")
45
+ return
46
+
47
+ md_content = input_file.read_text(encoding="utf-8")
48
+ html_template = template_path.read_text(encoding="utf-8")
49
+ final_html = html_template.replace("{{SLIDES_CONTENT}}", md_content)
50
+
51
+ output_file = Path(output_path)
52
+ _copy_js_assets(output_file)
53
+ _copy_css_assets(output_file)
54
+ output_file.write_text(final_html, encoding="utf-8")
55
+ _log.ok(f"[SUCCESS] Presentación generada en: {output_path}")
56
+
57
+
58
+ if __name__ == "__main__":
59
+ parser = argparse.ArgumentParser()
60
+ parser.add_argument("--input", required=True)
61
+ parser.add_argument("--output", default="presentation.html")
62
+ args = parser.parse_args()
63
+ generate_presentation(args.input, args.output)
@@ -0,0 +1,132 @@
1
+ """common.quality-scan — genera un reporte Markdown básico de calidad Python."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import ast
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from higpertext.kernel.infrastructure.logger import get_logger
10
+ _log = get_logger()
11
+
12
+
13
+ @dataclass
14
+ class FileQuality:
15
+ path: Path
16
+ score: int
17
+ penalties: list[str]
18
+
19
+
20
+ def scan_python_file(path: Path, max_function_lines: int, max_class_lines: int) -> FileQuality:
21
+ penalties: list[str] = []
22
+ try:
23
+ source = path.read_text(encoding="utf-8")
24
+ tree = ast.parse(source)
25
+ except (OSError, SyntaxError) as exc:
26
+ return FileQuality(path, 50, [f"No se pudo parsear el archivo: {exc}"])
27
+
28
+ lines = source.splitlines()
29
+ for node in ast.walk(tree):
30
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
31
+ length = _node_length(node)
32
+ if length > max_function_lines:
33
+ penalties.append(f"Función `{
34
+ node.name}` excede {max_function_lines} líneas L{
35
+ node.lineno}-L{
36
+ getattr(
37
+ node,
38
+ 'end_lineno',
39
+ node.lineno)}")
40
+ if isinstance(node, ast.ClassDef):
41
+ length = _node_length(node)
42
+ if length > max_class_lines:
43
+ penalties.append(f"Clase `{
44
+ node.name}` excede {max_class_lines} líneas L{
45
+ node.lineno}-L{
46
+ getattr(
47
+ node,
48
+ 'end_lineno',
49
+ node.lineno)}")
50
+
51
+ if lines and not _has_module_docstring(tree):
52
+ penalties.append("Sin comentarios/docstring de módulo")
53
+
54
+ score = max(0, 100 - (len(penalties) * 10))
55
+ return FileQuality(path, score, penalties)
56
+
57
+
58
+ def scan_tree(root: Path, max_function_lines: int, max_class_lines: int) -> list[FileQuality]:
59
+ try:
60
+ from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
61
+ except ImportError:
62
+ WORKSPACE_DIR_NAME = ".higpertext"
63
+ ignored = {".git", ".venv", "venv", "__pycache__", "node_modules", WORKSPACE_DIR_NAME}
64
+ files = [path for path in root.rglob("*.py") if not ignored.intersection(path.parts)]
65
+ return [scan_python_file(path, max_function_lines, max_class_lines) for path in sorted(files)]
66
+
67
+
68
+ def write_report(results: list[FileQuality], root: Path, output: Path) -> None:
69
+ output.parent.mkdir(parents=True, exist_ok=True)
70
+ lines = ["# Code Quality Report", "", f"Archivos analizados: `{len(results)}`", ""]
71
+ for result in results:
72
+ rel = _relative(result.path, root)
73
+ icon = "🟢" if result.score >= 90 else "🟡" if result.score >= 70 else "🔴"
74
+ lines += [
75
+ f"### {icon} `{rel}` — {result.score}/100",
76
+ "",
77
+ "**Penalizaciones aplicadas:**",
78
+ ]
79
+ if result.penalties:
80
+ lines.extend(f"- {penalty}" for penalty in result.penalties)
81
+ else:
82
+ lines.append("- Sin penalizaciones")
83
+ lines += ["", "---", ""]
84
+ output.write_text("\n".join(lines), encoding="utf-8")
85
+
86
+
87
+ def _node_length(node: ast.AST) -> int:
88
+ return int(
89
+ getattr(node, "end_lineno", getattr(node, "lineno", 0)) - getattr(node, "lineno", 0) + 1
90
+ )
91
+
92
+
93
+ def _has_module_docstring(tree: ast.Module) -> bool:
94
+ return ast.get_docstring(tree) is not None
95
+
96
+
97
+ def _relative(path: Path, root: Path) -> str:
98
+ try:
99
+ return path.relative_to(root).as_posix()
100
+ except ValueError:
101
+ return path.as_posix()
102
+
103
+
104
+ def main() -> None:
105
+ parser = argparse.ArgumentParser(description="Genera reporte de calidad Python")
106
+ parser.add_argument("--target", default="src", help="Directorio o archivo objetivo")
107
+ parser.add_argument("--output", default=".higpertext/reports/code_quality_report.md")
108
+ parser.add_argument("--max_function_lines", type=int, default=30)
109
+ parser.add_argument("--max_class_lines", type=int, default=200)
110
+ args = parser.parse_args()
111
+
112
+ root = Path.cwd()
113
+ target = Path(args.target)
114
+ if not target.is_absolute():
115
+ target = root / target
116
+ output = Path(args.output)
117
+ if not output.is_absolute():
118
+ output = root / output
119
+
120
+ results = (
121
+ [scan_python_file(target, args.max_function_lines, args.max_class_lines)]
122
+ if target.is_file()
123
+ else scan_tree(target, args.max_function_lines, args.max_class_lines)
124
+ )
125
+ write_report(results, root, output)
126
+ violations = sum(1 for result in results if result.penalties)
127
+ _log.ok(f"[SUCCESS] Reporte de calidad generado en: {output}")
128
+ _log.info(f"[*] Archivos analizados: {len(results)} · con violaciones: {violations}")
129
+
130
+
131
+ if __name__ == "__main__":
132
+ main()
@@ -0,0 +1,39 @@
1
+ """Capacidad para indexar el proyecto para RAG Semántico."""
2
+
3
+ from __future__ import annotations
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ # Configurar path para importar higpertext
9
+ _ROOT = Path(__file__).resolve().parents[5]
10
+ sys.path.insert(0, str(_ROOT / "src"))
11
+
12
+ from higpertext.kernel.infrastructure.llm.providers.gemini_embeddings import GeminiEmbeddingProvider
13
+ from higpertext.kernel.infrastructure.database.local_vector_store import LocalVectorStore
14
+ from higpertext.kernel.application.rag_service import RAGService
15
+
16
+
17
+ def main() -> None:
18
+ parser = argparse.ArgumentParser(description="Indexa el proyecto para búsqueda semántica (RAG).")
19
+ parser.add_argument("--root", type=str, default=".", help="Ruta al proyecto.")
20
+ args = parser.parse_args()
21
+
22
+ project_root = Path(args.root).resolve()
23
+ print(f"[*] Iniciando indexación semántica en: {project_root}")
24
+
25
+ try:
26
+ embedder = GeminiEmbeddingProvider(project_root)
27
+ store = LocalVectorStore(project_root)
28
+ service = RAGService(embedder, store)
29
+
30
+ chunks_indexed = service.index_project(project_root)
31
+ print(f"[+] Indexación completada. {chunks_indexed} fragmentos semánticos indexados con éxito.")
32
+ sys.exit(0)
33
+ except Exception as e:
34
+ print(f"[ERROR] Falló la indexación: {e}", file=sys.stderr)
35
+ sys.exit(1)
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()
@@ -0,0 +1,106 @@
1
+ """Capacidad common.report-viewer — Dashboard HTML Interactivo de Reportes."""
2
+
3
+ from __future__ import annotations
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+ from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
8
+ from higpertext.kernel.infrastructure.logger import get_logger
9
+ _log = get_logger()
10
+
11
+ # Resuelve raíz del proyecto
12
+ _HERE = Path(__file__).resolve()
13
+ _ROOT = next(
14
+ (p for p in _HERE.parents if (p / "src/config/htx_config.json").exists()),
15
+ _HERE.parents[4],
16
+ )
17
+ sys.path.insert(0, str(_ROOT / "src"))
18
+
19
+ try:
20
+ from higpertext.kernel.infrastructure.output_store import OutputStore
21
+ except ImportError:
22
+ OutputStore = None
23
+
24
+
25
+ def main() -> None:
26
+ parser = argparse.ArgumentParser(description="higpertext Premium Report Dashboard Compiler")
27
+ parser.add_argument(
28
+ "--output",
29
+ default=".higpertext/reports/index.html",
30
+ help="Ruta de destino del dashboard HTML resultante.",
31
+ )
32
+ args = parser.parse_args()
33
+
34
+ out_path = _ROOT / args.output
35
+ out_path.parent.mkdir(parents=True, exist_ok=True)
36
+
37
+ # 1. Leer los componentes Markdown
38
+ telemetry_file = _ROOT / WORKSPACE_DIR_NAME / "reports" / "common_telemetry_report.txt"
39
+ rationale_file = _ROOT / WORKSPACE_DIR_NAME / "reports" / "commit_rationale_history.md"
40
+ drift_file = _ROOT / WORKSPACE_DIR_NAME / "reports" / "architecture_drift.md"
41
+
42
+ telemetry_content = (
43
+ telemetry_file.read_text(encoding="utf-8")
44
+ if telemetry_file.exists()
45
+ else "Sin datos de telemetría."
46
+ )
47
+ rationale_content = (
48
+ rationale_file.read_text(encoding="utf-8")
49
+ if rationale_file.exists()
50
+ else "Sin diario de decisiones."
51
+ )
52
+ drift_content = (
53
+ drift_file.read_text(encoding="utf-8")
54
+ if drift_file.exists()
55
+ else "Sin análisis de desviación."
56
+ )
57
+
58
+ # 2. Generar el documento Markdown consolidado con formato ATX
59
+ # Las secciones usan '##' para convertirse en slides del HTML Presentation Engine
60
+ slides = [
61
+ "# Dashboard de Entrega & Decisiones Técnicas",
62
+ "Resumen ejecutivo de telemetría, diario de decisiones de diseño y análisis de desviación del higpertext Engine.", # noqa: E501
63
+ "",
64
+ "## Resumen de Telemetría (Consumo & ROI)",
65
+ "<!-- tipo: grid -->",
66
+ f"```text\n{telemetry_content}\n```",
67
+ "",
68
+ "## Análisis de Desviación Arquitectónica",
69
+ "---",
70
+ drift_content,
71
+ "",
72
+ "## Diario Histórico de Decisiones",
73
+ "---",
74
+ rationale_content,
75
+ ]
76
+
77
+ temp_md_path = out_path.parent / "temp_dashboard.md"
78
+ temp_md_path.write_text("\n".join(slides), encoding="utf-8")
79
+
80
+ # 3. Invocar al motor de presentaciones HTML local
81
+ try:
82
+ from higpertext.capabilities.common.scripts.presentation_engine import (
83
+ generate_presentation,
84
+ )
85
+
86
+ generate_presentation(str(temp_md_path), str(out_path))
87
+ except Exception as e:
88
+ _log.error(f"[ERROR] No se pudo generar la presentación HTML: {e}")
89
+ finally:
90
+ # Limpiar archivo markdown temporal
91
+ if temp_md_path.exists():
92
+ try:
93
+ temp_md_path.unlink()
94
+ except OSError: # nosec B110
95
+ pass
96
+
97
+ if OutputStore:
98
+ OutputStore(_ROOT).write(
99
+ "common.report-viewer", out_path.read_text(encoding="utf-8"), fmt="html"
100
+ )
101
+
102
+ _log.ok(f"[SUCCESS] Dashboard Premium HTML compilado en: {args.output}")
103
+
104
+
105
+ if __name__ == "__main__":
106
+ main()
@@ -0,0 +1,73 @@
1
+ """Capability common.roadmap-report — reporte explicativo del roadmap activo."""
2
+
3
+ from __future__ import annotations
4
+ from higpertext.kernel.application.roadmap_reporter import RoadmapReportBuilder, ProgressAnalyzer, RoadmapParser
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+ from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
9
+
10
+ from higpertext.kernel.infrastructure.logger import get_logger
11
+ _log = get_logger()
12
+
13
+ _HERE = Path(__file__).resolve()
14
+ _ROOT = next((p for p in _HERE.parents if (p / "src/config/htx_config.json").exists()), _HERE.parents[4])
15
+ sys.path.insert(0, str(_ROOT / "src"))
16
+ sys.path.insert(0, str(_ROOT))
17
+
18
+ def _parse_args() -> argparse.Namespace:
19
+ parser = argparse.ArgumentParser(description="Genera reporte explicativo del roadmap.")
20
+ parser.add_argument("--roadmap", default=str(_ROOT / WORKSPACE_DIR_NAME / "roadmap.json"))
21
+ parser.add_argument("--format", dest="fmt", default="markdown", choices=["markdown", "html"])
22
+ parser.add_argument("--output", default=None)
23
+ return parser.parse_args()
24
+
25
+
26
+ def _resolve_output(args: argparse.Namespace, roadmap_id: str) -> Path:
27
+ if args.output:
28
+ return Path(args.output)
29
+ ext = "html" if args.fmt == "html" else "md"
30
+ out_dir = _ROOT / WORKSPACE_DIR_NAME / "reports" / "roadmap"
31
+ out_dir.mkdir(parents=True, exist_ok=True)
32
+ return out_dir / f"{roadmap_id}_report.{ext}"
33
+
34
+
35
+ def _print_summary(report, analysis, out: Path) -> None:
36
+ r = report.roadmap
37
+ _log.info("╔─ HIGPERTEXT · Roadmap Report ───────────────────────────────")
38
+ _log.info(f"│ Roadmap : {r.name} ({r.id})")
39
+ _log.info(f"│ Fases : {report.done_phases}/{report.total_phases} completadas")
40
+ _log.info(f"│ Progreso : {analysis.completion_pct:.0f}%")
41
+ _log.info(f"│ Tags : {', '.join(analysis.impact_tags)}")
42
+ _log.info(f"│ Skills : {', '.join(report.all_skills_used) or '—'}")
43
+ _log.info(f"│ Reporte : {out.relative_to(_ROOT)}")
44
+ _log.info("╚───────────────────────────────────────────────────────")
45
+ _log.info(f"\n{analysis.summary}")
46
+ _log.ok(f"\n[SUCCESS] Reporte generado: {out}")
47
+
48
+
49
+ def main() -> None:
50
+ args = _parse_args()
51
+ roadmap_path = Path(args.roadmap)
52
+ if not roadmap_path.exists():
53
+ _log.error(f"[ERROR] Roadmap no encontrado: {roadmap_path}")
54
+ sys.exit(1)
55
+ try:
56
+ report = RoadmapParser().parse_file(roadmap_path)
57
+ except ValueError as exc:
58
+ _log.error(f"[ERROR] {exc}")
59
+ sys.exit(1)
60
+ analysis = ProgressAnalyzer().analyze(report)
61
+ builder = RoadmapReportBuilder()
62
+ content = (
63
+ builder.to_html(report, analysis)
64
+ if args.fmt == "html"
65
+ else builder.to_markdown(report, analysis)
66
+ )
67
+ out = _resolve_output(args, report.roadmap.id)
68
+ out.write_text(content, encoding="utf-8")
69
+ _print_summary(report, analysis, out)
70
+
71
+
72
+ if __name__ == "__main__":
73
+ main()