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,111 @@
1
+ """common.search-router — recomienda la capacidad óptima para buscar/leer contexto."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+
9
+ from higpertext.kernel.infrastructure.logger import get_logger
10
+ _log = get_logger()
11
+
12
+
13
+ def route(query: str, intent: str, scope: str, preset: str, budget: int) -> list[dict]:
14
+ """Construye plan de búsqueda/lectura según intención."""
15
+ steps: list[dict] = []
16
+ if intent == "error" or any(
17
+ token in query.lower() for token in ("traceback", "error", "exception", "failed")
18
+ ):
19
+ steps.append(
20
+ {
21
+ "capability": "common.error-context-locator",
22
+ "params": {"error": query, "max_context": 5},
23
+ }
24
+ )
25
+ if _looks_like_path(query):
26
+ steps.append(
27
+ {
28
+ "capability": "common.smart-read",
29
+ "params": {"path": query, "mode": "auto", "max_tokens": budget},
30
+ }
31
+ )
32
+ if intent in {"feature", "refactor", "symbol"}:
33
+ steps.append(
34
+ {
35
+ "capability": "common.graph-query",
36
+ "params": {"symbol": query, "depth": 2, "budget": budget},
37
+ }
38
+ )
39
+ # RAG Semántico como primer fallback general
40
+ steps.append(
41
+ {
42
+ "capability": "common.semantic-search",
43
+ "params": {"query": query, "limit": 5, "root": scope or "."},
44
+ }
45
+ )
46
+ steps.append(
47
+ {
48
+ "capability": "common.grep-search",
49
+ "params": {
50
+ "pattern": query,
51
+ "path": scope or ".",
52
+ "preset": preset,
53
+ "max_results": 50,
54
+ "max_per_file": 5,
55
+ },
56
+ }
57
+ )
58
+ return _dedupe(steps)
59
+
60
+
61
+ def _dedupe(steps: list[dict]) -> list[dict]:
62
+ seen = set()
63
+ result = []
64
+ for step in steps:
65
+ key = (step["capability"], json.dumps(step["params"], sort_keys=True))
66
+ if key not in seen:
67
+ seen.add(key)
68
+ result.append(step)
69
+ return result
70
+
71
+
72
+ def _looks_like_path(query: str) -> bool:
73
+ value = query.strip().strip("\"'")
74
+ if any(space in value for space in (" ", "\n", "\t")):
75
+ return False
76
+ return "/" in value or Path(value).suffix != ""
77
+
78
+
79
+ def _format_plan(steps: list[dict]) -> str:
80
+ lines = ["╔─ HIGPERTEXT · Search Router ───────────────────────────────"]
81
+ for index, step in enumerate(steps, 1):
82
+ params = " ".join(
83
+ f"--{k} {json.dumps(v, ensure_ascii=False)}" for k, v in step["params"].items()
84
+ )
85
+ lines.append(f"│ {index}. htx task {step['capability']} {params}")
86
+ lines.append("╚────────────────────────────────────────────────────────────")
87
+ return "\n".join(lines)
88
+
89
+
90
+ def main() -> None:
91
+ parser = argparse.ArgumentParser(description="Orquesta búsqueda inteligente")
92
+ parser.add_argument("--query", required=True)
93
+ parser.add_argument(
94
+ "--intent",
95
+ default="general",
96
+ choices=["error", "feature", "refactor", "docs", "symbol", "general"],
97
+ )
98
+ parser.add_argument("--scope", default=".")
99
+ parser.add_argument("--preset", default="code")
100
+ parser.add_argument("--budget", type=int, default=4000)
101
+ parser.add_argument("--json", default="false")
102
+ args = parser.parse_args()
103
+ steps = route(args.query, args.intent, args.scope, args.preset, args.budget)
104
+ if args.json.lower() in {"true", "1", "yes"}:
105
+ print(json.dumps({"steps": steps}, ensure_ascii=False, indent=2))
106
+ return
107
+ _log.info(_format_plan(steps))
108
+
109
+
110
+ if __name__ == "__main__":
111
+ main()
@@ -0,0 +1,166 @@
1
+ """higpertext Semantic Diff — detecta funciones/clases cambiadas entre commits via AST."""
2
+
3
+ import ast
4
+ import sys
5
+ import json
6
+ import argparse
7
+ import subprocess # nosec B404
8
+
9
+ from higpertext.kernel.infrastructure.logger import get_logger
10
+ _log = get_logger()
11
+
12
+
13
+ def run_cmd(cmd: list[str]) -> tuple[int, str, str]:
14
+ res = subprocess.run(cmd, capture_output=True, text=True) # nosec B603
15
+ return res.returncode, res.stdout, res.stderr
16
+
17
+
18
+ def _extract_symbols(source: str) -> dict[str, set[str]]:
19
+ """Extrae nombres de funciones y clases de código fuente Python via AST."""
20
+ symbols: dict[str, set[str]] = {
21
+ "functions": set(),
22
+ "classes": set(),
23
+ "methods": set(),
24
+ }
25
+ try:
26
+ tree = ast.parse(source)
27
+ except SyntaxError:
28
+ return symbols
29
+ for node in ast.walk(tree):
30
+ if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
31
+ symbols["functions"].add(node.name)
32
+ elif isinstance(node, ast.ClassDef):
33
+ symbols["classes"].add(node.name)
34
+ for item in node.body:
35
+ if isinstance(item, ast.FunctionDef | ast.AsyncFunctionDef):
36
+ symbols["methods"].add(f"{node.name}.{item.name}")
37
+ return symbols
38
+
39
+
40
+ def _get_file_content_at(ref: str, filepath: str) -> str | None:
41
+ """Obtiene el contenido de un archivo en un commit dado."""
42
+ ret, out, _ = run_cmd(["git", "show", f"{ref}:{filepath}"])
43
+ return out if ret == 0 else None
44
+
45
+
46
+ def _get_changed_files(base: str, head: str, files_filter: list[str] | None) -> list[str]:
47
+ """Lista archivos .py que cambiaron entre base y head."""
48
+ ret, out, _ = run_cmd(["git", "diff", "--name-only", base, head])
49
+ if ret != 0:
50
+ return []
51
+ changed = [f for f in out.splitlines() if f.endswith(".py")]
52
+ if files_filter:
53
+ changed = [f for f in changed if any(f.endswith(ff) or ff in f for ff in files_filter)]
54
+ return changed
55
+
56
+
57
+ def _diff_symbols(base: str, head: str, filepath: str) -> dict:
58
+ """Compara símbolos AST entre dos versiones de un archivo."""
59
+ content_base = _get_file_content_at(base, filepath)
60
+ content_head = _get_file_content_at(head, filepath)
61
+
62
+ if content_base is None and content_head is None:
63
+ return {}
64
+
65
+ sym_base = _extract_symbols(content_base or "")
66
+ sym_head = _extract_symbols(content_head or "")
67
+
68
+ result: dict[str, list] = {}
69
+ for kind in ("functions", "classes", "methods"):
70
+ b = sym_base[kind]
71
+ h = sym_head[kind]
72
+ added = sorted(h - b)
73
+ removed = sorted(b - h)
74
+ # Modified = same name but content changed (line-level heuristic per symbol)
75
+ common = b & h
76
+ modified = _detect_modified(content_base or "", content_head or "", common, kind)
77
+
78
+ if added or removed or modified:
79
+ result[kind] = {
80
+ "added": added,
81
+ "removed": removed,
82
+ "modified": sorted(modified),
83
+ }
84
+ return result
85
+
86
+
87
+ def _detect_modified(src_base: str, src_head: str, names: set[str], kind: str) -> set[str]:
88
+ """Heurística: extrae el bloque de cada símbolo y compara su hash."""
89
+ modified = set()
90
+ for name in names:
91
+ block_b = _extract_block(src_base, name, kind)
92
+ block_h = _extract_block(src_head, name, kind)
93
+ if block_b != block_h:
94
+ modified.add(name)
95
+ return modified
96
+
97
+
98
+ def _extract_block(source: str, name: str, kind: str) -> str:
99
+ """Extrae el cuerpo de una función/clase por nombre."""
100
+ try:
101
+ tree = ast.parse(source)
102
+ except SyntaxError:
103
+ return ""
104
+ lines = source.splitlines()
105
+ for node in ast.walk(tree):
106
+ node_name = getattr(node, "name", None)
107
+ if node_name != name.split(".")[-1]:
108
+ continue
109
+ if kind == "functions" and not isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
110
+ continue
111
+ if kind == "classes" and not isinstance(node, ast.ClassDef):
112
+ continue
113
+ start = node.lineno - 1
114
+ end = node.end_lineno if hasattr(node, "end_lineno") else start + 1
115
+ return "\n".join(lines[start:end])
116
+ return ""
117
+
118
+
119
+ def _print_text(results: dict[str, dict]) -> None:
120
+ if not results:
121
+ _log.info("[*] No se detectaron cambios semánticos en símbolos Python.")
122
+ return
123
+ _log.info("=" * 60)
124
+ _log.info("DIFF SEMÁNTICO — Símbolos modificados")
125
+ _log.info("=" * 60)
126
+ for filepath, kinds in results.items():
127
+ _log.info(f"\n📄 {filepath}")
128
+ for kind, changes in kinds.items():
129
+ for action, names in changes.items():
130
+ if names:
131
+ icon = {"added": "✚", "removed": "✖", "modified": "~"}.get(action, "?")
132
+ label = f" [{icon} {action.upper()} {kind}]"
133
+ for n in names:
134
+ _log.info(f"{label}: {n}")
135
+ _log.info("\n" + "=" * 60)
136
+
137
+
138
+ def main() -> None:
139
+ parser = argparse.ArgumentParser(description="higpertext Semantic Diff")
140
+ parser.add_argument("--base", default="HEAD~1")
141
+ parser.add_argument("--head", default="HEAD")
142
+ parser.add_argument("--files", default=None)
143
+ parser.add_argument("--format", default="text", choices=["text", "json"])
144
+ args = parser.parse_args()
145
+
146
+ files_filter = [f.strip() for f in args.files.split(",") if f.strip()] if args.files else None
147
+ changed_files = _get_changed_files(args.base, args.head, files_filter)
148
+
149
+ if not changed_files:
150
+ _log.info("[*] No hay archivos Python modificados entre los commits especificados.")
151
+ sys.exit(0)
152
+
153
+ results: dict[str, dict] = {}
154
+ for fp in changed_files:
155
+ diff = _diff_symbols(args.base, args.head, fp)
156
+ if diff:
157
+ results[fp] = diff
158
+
159
+ if args.format == "json":
160
+ print(json.dumps(results, indent=2))
161
+ else:
162
+ _print_text(results)
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()
@@ -0,0 +1,43 @@
1
+ """Capacidad para buscar fragmentos de código semánticamente mediante RAG."""
2
+
3
+ from __future__ import annotations
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ # Configurar path para importar higpertext
10
+ _ROOT = Path(__file__).resolve().parents[5]
11
+ sys.path.insert(0, str(_ROOT / "src"))
12
+
13
+ from higpertext.kernel.infrastructure.llm.providers.gemini_embeddings import GeminiEmbeddingProvider
14
+ from higpertext.kernel.infrastructure.database.local_vector_store import LocalVectorStore
15
+ from higpertext.kernel.application.rag_service import RAGService
16
+
17
+
18
+ def main() -> None:
19
+ parser = argparse.ArgumentParser(description="Busca código semánticamente mediante RAG.")
20
+ parser.add_argument("--query", type=str, required=True, help="Texto o concepto a buscar.")
21
+ parser.add_argument("--limit", type=int, default=5, help="Límite de resultados.")
22
+ parser.add_argument("--root", type=str, default=".", help="Ruta al proyecto.")
23
+ args = parser.parse_args()
24
+
25
+ project_root = Path(args.root).resolve()
26
+
27
+ try:
28
+ embedder = GeminiEmbeddingProvider(project_root)
29
+ store = LocalVectorStore(project_root)
30
+ service = RAGService(embedder, store)
31
+
32
+ hits = service.search_context(args.query, limit=args.limit)
33
+
34
+ # Output en JSON estructurado para el Framework
35
+ print(json.dumps({"hits": hits}, indent=2, ensure_ascii=False))
36
+ sys.exit(0)
37
+ except Exception as e:
38
+ print(json.dumps({"error": str(e)}), file=sys.stderr)
39
+ sys.exit(1)
40
+
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,136 @@
1
+ """higpertext Session Control — inicia y limpia sesiones de desarrollo del agente."""
2
+
3
+ import sys
4
+ import json
5
+ import argparse
6
+ from higpertext.kernel.session_manager import SessionManager # noqa: E402
7
+ from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
8
+ from pathlib import Path
9
+ from higpertext.kernel.infrastructure.logger import get_logger
10
+ _log = get_logger()
11
+
12
+ ROOT_DIR = Path(__file__).parents[4]
13
+ if str(ROOT_DIR / "src" / "core") not in sys.path:
14
+ sys.path.append(str(ROOT_DIR / "src" / "core"))
15
+
16
+ def _load_roadmap_resources(project_root: Path) -> tuple:
17
+ """Lee session_resources mergeando roadmaps activos en roadmaps/active.json.
18
+
19
+ Fallback a roadmap.json único para compatibilidad hacia atrás.
20
+ """
21
+ roadmaps_dir = project_root / WORKSPACE_DIR_NAME / "config" / "roadmaps"
22
+ active_index = roadmaps_dir / "active.json"
23
+
24
+ if active_index.exists():
25
+ try:
26
+ active_ids: list = json.loads(active_index.read_text(encoding="utf-8")).get(
27
+ "active", []
28
+ )
29
+ except (OSError, json.JSONDecodeError):
30
+ return None, None
31
+
32
+ if not active_ids:
33
+ return None, None
34
+
35
+ merged_skills: list = []
36
+ merged_subagents: list = []
37
+ loaded: list = []
38
+
39
+ for rid in active_ids:
40
+ rf = roadmaps_dir / f"{rid}.json"
41
+ if not rf.exists():
42
+ continue
43
+ try:
44
+ data = json.loads(rf.read_text(encoding="utf-8"))
45
+ res = data.get("session_resources", {})
46
+ for s in res.get("skills", []):
47
+ if s not in merged_skills:
48
+ merged_skills.append(s)
49
+ for sa in res.get("subagents", []):
50
+ if sa not in merged_subagents:
51
+ merged_subagents.append(sa)
52
+ loaded.append(data.get("project", rid))
53
+ except (OSError, json.JSONDecodeError):
54
+ continue
55
+
56
+ if not loaded:
57
+ return None, None
58
+
59
+ projects_str = ", ".join(f"'{p}'" for p in loaded)
60
+ _log.info(
61
+ f"[*] Roadmaps activos: {projects_str}"
62
+ f" — montando {len(merged_skills)} skill(s) y {len(merged_subagents)} subagente(s)."
63
+ )
64
+ return merged_skills or None, merged_subagents or None
65
+
66
+ # Fallback: roadmap.json único
67
+ roadmap_file = project_root / WORKSPACE_DIR_NAME / "config" / "roadmap.json"
68
+ if not roadmap_file.exists():
69
+ return None, None
70
+ try:
71
+ data = json.loads(roadmap_file.read_text(encoding="utf-8"))
72
+ resources = data.get("session_resources", {})
73
+ skills = resources.get("skills", [])
74
+ subagents = resources.get("subagents", [])
75
+ if skills or subagents:
76
+ _log.info(
77
+ f"[*] Roadmap detectado: '{data.get('project', '')}' "
78
+ f"— montando {len(skills)} skill(s) y {len(subagents)} subagente(s)."
79
+ )
80
+ return skills, subagents
81
+ return None, None
82
+ except (OSError, json.JSONDecodeError):
83
+ return None, None
84
+
85
+
86
+ def _load_profile_session_resources(profile_name: str, root_dir: Path) -> tuple:
87
+ """Fallback: lee session_skills y session_subagents del perfil JSON."""
88
+ profile_file = root_dir / "src" / "config" / "profiles" / f"{profile_name}.json"
89
+ if not profile_file.exists():
90
+ return [], []
91
+ try:
92
+ data = json.loads(profile_file.read_text(encoding="utf-8"))
93
+ return data.get("session_skills", []), data.get("session_subagents", [])
94
+ except (OSError, json.JSONDecodeError):
95
+ return [], []
96
+
97
+
98
+ def main() -> None:
99
+ """Punto de entrada de session-control."""
100
+ parser = argparse.ArgumentParser(description="higpertext Session Control Capability")
101
+ parser.add_argument("--action", required=True, choices=["start", "clean"])
102
+ parser.add_argument("--profile", help="Active profile name")
103
+ parser.add_argument("--assistant", help="Assistant name (claude, gemini, antigravity, copilot)")
104
+ parser.add_argument("--skills", help="Comma-separated skills (overrides roadmap and profile)")
105
+ parser.add_argument(
106
+ "--subagents", help="Comma-separated subagents (overrides roadmap and profile)"
107
+ )
108
+ args = parser.parse_args()
109
+
110
+ project_root = Path.cwd()
111
+ session_mgr = SessionManager(project_root, ROOT_DIR)
112
+
113
+ if args.action == "start":
114
+ env_assistant, active_profile = session_mgr.get_assistant_and_profile()
115
+ assistant = args.assistant or env_assistant
116
+ profile = args.profile or active_profile
117
+
118
+ # Prioridad: args explícitos, de lo contrario SessionManager resolverá
119
+ # roadmap/perfil/fallback
120
+ skills_list = (
121
+ [s.strip() for s in args.skills.split(",") if s.strip()] if args.skills else None
122
+ )
123
+ subagents_list = (
124
+ [sa.strip() for sa in args.subagents.split(",") if sa.strip()]
125
+ if args.subagents
126
+ else None
127
+ )
128
+
129
+ session_mgr.start_session(profile, assistant, skills_list, subagents_list)
130
+
131
+ elif args.action == "clean":
132
+ session_mgr.clean_session()
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main()
@@ -0,0 +1,232 @@
1
+ """common.smart-read — lectura segura y compacta para LLM."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import ast
7
+ import json
8
+ import sys
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+
12
+ try:
13
+ from higpertext.capabilities.common.scripts.code_skeletonizer import (
14
+ skeletonize_file,
15
+ )
16
+ except ImportError: # pragma: no cover - fallback para ejecución directa
17
+ from code_skeletonizer import skeletonize_file # type: ignore
18
+
19
+ from higpertext.kernel.infrastructure.logger import get_logger
20
+ _log = get_logger()
21
+
22
+ _CODE_SUFFIXES = {".py", ".js", ".jsx", ".ts", ".tsx", ".go", ".java", ".cs", ".rs"}
23
+ _DEFAULT_MAX_BYTES = 100 * 1024
24
+ _DEFAULT_LIMIT = 120
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class SymbolSpan:
29
+ """Rango aproximado de un símbolo dentro de un archivo."""
30
+
31
+ name: str
32
+ kind: str
33
+ start: int
34
+ end: int
35
+
36
+
37
+ def _read_lines(path: Path) -> list[str]:
38
+ return path.read_text(encoding="utf-8", errors="ignore").splitlines()
39
+
40
+
41
+ def _estimate_tokens(text: str) -> int:
42
+ return max(1, len(text) // 4)
43
+
44
+
45
+ def _is_large(path: Path, max_bytes: int) -> bool:
46
+ return max_bytes > 0 and path.stat().st_size > max_bytes
47
+
48
+
49
+ def _line_map(path: Path) -> list[SymbolSpan]:
50
+ if path.suffix != ".py":
51
+ return _regex_line_map(_read_lines(path))
52
+ try:
53
+ tree = ast.parse(path.read_text(encoding="utf-8", errors="ignore"))
54
+ except SyntaxError:
55
+ return _regex_line_map(_read_lines(path))
56
+ spans: list[SymbolSpan] = []
57
+ for node in ast.walk(tree):
58
+ if isinstance(node, ast.ClassDef):
59
+ spans.append(
60
+ SymbolSpan(
61
+ node.name,
62
+ "class",
63
+ node.lineno,
64
+ getattr(node, "end_lineno", node.lineno),
65
+ )
66
+ )
67
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
68
+ kind = "async def" if isinstance(node, ast.AsyncFunctionDef) else "def"
69
+ spans.append(
70
+ SymbolSpan(
71
+ node.name,
72
+ kind,
73
+ node.lineno,
74
+ getattr(node, "end_lineno", node.lineno),
75
+ )
76
+ )
77
+ return sorted(spans, key=lambda item: item.start)
78
+
79
+
80
+ def _regex_line_map(lines: list[str]) -> list[SymbolSpan]:
81
+ spans: list[SymbolSpan] = []
82
+ for index, line in enumerate(lines, 1):
83
+ stripped = line.strip()
84
+ if stripped.startswith("class "):
85
+ spans.append(
86
+ SymbolSpan(stripped.split()[1].split("(")[0].rstrip(":"), "class", index, index)
87
+ )
88
+ elif stripped.startswith(("def ", "async def ", "function ")):
89
+ name = stripped.replace("async def ", "def ", 1).split()[1].split("(")[0]
90
+ spans.append(SymbolSpan(name, "def", index, index))
91
+ return spans
92
+
93
+
94
+ def _format_range(lines: list[str], start: int, end: int, line_limit: int = 240) -> str:
95
+ start = max(1, start)
96
+ end = min(len(lines), end)
97
+ rendered = []
98
+ for lineno in range(start, end + 1):
99
+ text = lines[lineno - 1]
100
+ if line_limit and len(text) > line_limit:
101
+ text = text[: line_limit - 3] + "..."
102
+ rendered.append(f"{lineno:>5}: {text}")
103
+ return "\n".join(rendered)
104
+
105
+
106
+ def _symbol_range(path: Path, symbol: str, around: int) -> tuple[int, int] | None:
107
+ for span in _line_map(path):
108
+ if span.name == symbol or symbol.lower() in span.name.lower():
109
+ return max(1, span.start - around), span.end + around
110
+ return None
111
+
112
+
113
+ def _summary(path: Path, max_bytes: int) -> str:
114
+ spans = _line_map(path)
115
+ lines = [
116
+ "╔─ HIGPERTEXT · Smart Read Summary ─────────────────────────",
117
+ f"│ Archivo : {path}",
118
+ f"│ Tamaño : {path.stat().st_size} bytes",
119
+ f"│ Grande : {'sí' if _is_large(path, max_bytes) else 'no'}",
120
+ f"│ Símbolos: {len(spans)}",
121
+ "╠────────────────────────────────────────────────────────────",
122
+ ]
123
+ for span in spans[:80]:
124
+ lines.append(f"│ L{span.start}-{span.end}: {span.kind} {span.name}")
125
+ if len(spans) > 80:
126
+ lines.append(f"│ ... {len(spans) - 80} símbolo(s) omitidos")
127
+ lines.append("╚────────────────────────────────────────────────────────────")
128
+ return "\n".join(lines)
129
+
130
+
131
+ def smart_read(
132
+ path: Path,
133
+ mode: str,
134
+ symbol: str = "",
135
+ offset: int = 1,
136
+ limit: int = _DEFAULT_LIMIT,
137
+ around_line: int = 0,
138
+ max_bytes: int = _DEFAULT_MAX_BYTES,
139
+ ) -> str:
140
+ """Devuelve contenido curado según modo solicitado."""
141
+ if not path.exists() or not path.is_file():
142
+ raise FileNotFoundError(str(path))
143
+ resolved_mode = mode
144
+ if mode == "auto":
145
+ resolved_mode = (
146
+ "skeleton" if path.suffix in _CODE_SUFFIXES and _is_large(path, max_bytes) else "range"
147
+ )
148
+ if resolved_mode == "summary":
149
+ return _summary(path, max_bytes)
150
+ if resolved_mode == "skeleton":
151
+ return skeletonize_file(path)
152
+
153
+ lines = _read_lines(path)
154
+ if resolved_mode == "symbol":
155
+ if not symbol:
156
+ raise ValueError("--symbol es requerido para mode=symbol")
157
+ span = _symbol_range(path, symbol, around_line or 3)
158
+ if not span:
159
+ return f"[NOT FOUND] Símbolo no encontrado: {symbol}\n\n{_summary(path, max_bytes)}"
160
+ return _format_range(lines, span[0], span[1])
161
+ if resolved_mode == "full":
162
+ if _is_large(path, max_bytes):
163
+ return "\n".join(
164
+ [
165
+ "[BLOCKED] Archivo grande; usa --mode skeleton, summary, symbol o range.",
166
+ f"Archivo: {path}",
167
+ f"Tamaño: {path.stat().st_size} bytes > {max_bytes} bytes",
168
+ ]
169
+ )
170
+ return _format_range(lines, 1, len(lines))
171
+ if around_line > 0:
172
+ return _format_range(lines, around_line - limit, around_line + limit)
173
+ return _format_range(lines, offset, offset + limit - 1)
174
+
175
+
176
+ def _json_payload(path: Path, content: str, mode: str) -> dict:
177
+ return {
178
+ "path": str(path),
179
+ "mode": mode,
180
+ "bytes": path.stat().st_size,
181
+ "estimated_tokens": _estimate_tokens(content),
182
+ "content": content,
183
+ }
184
+
185
+
186
+ def main() -> None:
187
+ parser = argparse.ArgumentParser(description="Lectura segura y optimizada para LLM")
188
+ parser.add_argument("--path", required=True)
189
+ parser.add_argument(
190
+ "--mode",
191
+ default="auto",
192
+ choices=["auto", "skeleton", "range", "symbol", "full", "summary"],
193
+ )
194
+ parser.add_argument("--symbol", default="")
195
+ parser.add_argument("--offset", type=int, default=1)
196
+ parser.add_argument("--limit", type=int, default=_DEFAULT_LIMIT)
197
+ parser.add_argument("--around_line", type=int, default=0)
198
+ parser.add_argument("--max_bytes", type=int, default=_DEFAULT_MAX_BYTES)
199
+ parser.add_argument("--max_tokens", type=int, default=0)
200
+ parser.add_argument("--json", default="false")
201
+ args = parser.parse_args()
202
+
203
+ try:
204
+ path = Path(args.path)
205
+ content = smart_read(
206
+ path,
207
+ args.mode,
208
+ args.symbol,
209
+ args.offset,
210
+ args.limit,
211
+ args.around_line,
212
+ args.max_bytes,
213
+ )
214
+ if args.max_tokens > 0 and _estimate_tokens(content) > args.max_tokens:
215
+ content = smart_read(path, "summary", max_bytes=args.max_bytes)
216
+ if args.json.lower() in {"true", "1", "yes"}:
217
+ _log.info(
218
+ json.dumps(
219
+ _json_payload(path, content, args.mode),
220
+ ensure_ascii=False,
221
+ indent=2,
222
+ )
223
+ )
224
+ else:
225
+ _log.info(content)
226
+ except Exception as exc:
227
+ _log.error(f"[ERROR] {exc}")
228
+ sys.exit(1)
229
+
230
+
231
+ if __name__ == "__main__":
232
+ main()