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,648 @@
1
+ """higpertext Grep Search — busca patrones en archivos del proyecto con salida estructurada."""
2
+
3
+ import re
4
+ import sys
5
+ import json
6
+ import argparse
7
+ from fnmatch import fnmatch
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+
11
+ from higpertext.kernel.infrastructure.logger import get_logger
12
+ _log = get_logger()
13
+
14
+ _SECRET_PATTERNS = (".env", "secrets.json", ".key", ".pem", ".pfx")
15
+ _ALWAYS_EXCLUDES = {".venv", "venv", "__pycache__", ".git", "node_modules", ".tox"}
16
+ _DEFAULT_EXTRA_EXCLUDES = {
17
+ "dist",
18
+ "build",
19
+ "coverage",
20
+ "logs",
21
+ "tmp",
22
+ "target",
23
+ "vendor",
24
+ "__cache__",
25
+ "reports",
26
+ ".agents",
27
+ ".gemini",
28
+ ".claude",
29
+ ".opencode",
30
+ ".antigravitycli",
31
+ ".next",
32
+ ".nuxt",
33
+ ".turbo",
34
+ ".cache",
35
+ ".pytest_cache",
36
+ "*.min.js",
37
+ "*.bundle.min.js",
38
+ "*.min.css",
39
+ "*.map",
40
+ }
41
+ _TEST_PATH_PARTS = {"tests", "test", "__tests__", "spec", "specs"}
42
+ _PRESET_GLOBS = {
43
+ "code": [
44
+ "*.py",
45
+ "*.js",
46
+ "*.jsx",
47
+ "*.ts",
48
+ "*.tsx",
49
+ "*.go",
50
+ "*.rs",
51
+ "*.java",
52
+ "*.cs",
53
+ ],
54
+ "python": ["*.py"],
55
+ "web": ["*.js", "*.jsx", "*.ts", "*.tsx", "*.css", "*.scss", "*.html"],
56
+ "docs": ["*.md", "*.txt", "*.rst"],
57
+ "config": ["*.json", "*.jsonc", "*.yaml", "*.yml", "*.toml", "*.ini"],
58
+ "all": ["*"],
59
+ }
60
+ _GRAPH_CANDIDATES = (
61
+ Path(".higpertext/state/semantic_graph.json"),
62
+ Path(".nexus/semantic_graph.json"),
63
+ Path(".nexus/semantic_graph.md"),
64
+ )
65
+
66
+
67
+ @dataclass
68
+ class SearchSummary:
69
+ """Acumula métricas y resultados estructurados de búsqueda."""
70
+
71
+ found_count: list[int] = field(default_factory=lambda: [0])
72
+ files_matched: int = 0
73
+ json_matches: list[dict] = field(default_factory=list)
74
+
75
+
76
+ def _is_secret_file(path: Path) -> bool:
77
+ """Verifica si el archivo es candidato a contener secretos."""
78
+ return any(path.name.endswith(pat) for pat in _SECRET_PATTERNS)
79
+
80
+
81
+ def _is_binary_file(path: Path) -> bool:
82
+ """Verifica si el archivo es binario leyendo sus primeros bytes buscando bytes nulos."""
83
+ try:
84
+ with open(path, "rb") as f:
85
+ chunk = f.read(1024)
86
+ return b"\x00" in chunk
87
+ except OSError:
88
+ return True
89
+
90
+
91
+ def _build_excludes(custom_excludes: set[str], include_all: bool) -> set[str]:
92
+ """Construye exclusiones seguras; --all conserva dependencias, secretos y binarios fuera."""
93
+ excludes = set(_ALWAYS_EXCLUDES) | custom_excludes
94
+ if not include_all:
95
+ excludes |= _DEFAULT_EXTRA_EXCLUDES
96
+ return excludes
97
+
98
+
99
+ def _is_excluded(path: Path, excludes: set[str], root: Path) -> bool:
100
+ """Aplica exclusiones por parte de ruta, nombre exacto o glob simple."""
101
+ try:
102
+ scoped = path.relative_to(root)
103
+ except ValueError:
104
+ scoped = path
105
+ parts = set(scoped.parts)
106
+ if parts & excludes:
107
+ return True
108
+ as_posix = scoped.as_posix()
109
+ return any(fnmatch(path.name, item) or fnmatch(as_posix, item) for item in excludes)
110
+
111
+
112
+ def _is_test_path(path: Path) -> bool:
113
+ """Detecta rutas de tests comunes para búsquedas enfocadas en código fuente."""
114
+ lower_parts = {part.lower() for part in path.parts}
115
+ return bool(lower_parts & _TEST_PATH_PARTS) or path.name.startswith("test_")
116
+
117
+
118
+ def _collect_files(
119
+ search_path: Path,
120
+ include_globs: list[str],
121
+ excludes: set[str],
122
+ include_tests: bool = True,
123
+ max_file_size_kb: int = 1024,
124
+ ) -> list[Path]:
125
+ """Recopila archivos bajo search_path respetando filtros de inclusión y exclusión."""
126
+ results: list[Path] = []
127
+ if search_path.is_file():
128
+ if _is_secret_file(search_path) or _is_binary_file(search_path):
129
+ return []
130
+ if not include_tests and _is_test_path(search_path):
131
+ return []
132
+ if max_file_size_kb > 0 and search_path.stat().st_size > max_file_size_kb * 1024:
133
+ return []
134
+ return [search_path]
135
+
136
+ patterns = include_globs if include_globs else ["*"]
137
+ for pattern in patterns:
138
+ for f in search_path.rglob(pattern):
139
+ if not f.is_file():
140
+ continue
141
+ if _is_secret_file(f):
142
+ continue
143
+ if _is_excluded(f, excludes, search_path):
144
+ continue
145
+ if not include_tests and _is_test_path(f):
146
+ continue
147
+ if max_file_size_kb > 0 and f.stat().st_size > max_file_size_kb * 1024:
148
+ continue
149
+ if _is_binary_file(f):
150
+ continue
151
+ results.append(f)
152
+ return sorted(set(results))
153
+
154
+
155
+ def _compile_pattern(pattern: str, is_regex: bool, case_sensitive: bool) -> re.Pattern:
156
+ """Compila el patrón de búsqueda según los flags solicitados."""
157
+ flags = 0 if case_sensitive else re.IGNORECASE
158
+ if not is_regex:
159
+ pattern = re.escape(pattern)
160
+ return re.compile(pattern, flags)
161
+
162
+
163
+ def _format_path(path: Path, root: Path, absolute_paths: bool) -> str:
164
+ """Reduce ruido usando rutas relativas salvo que se pidan absolutas."""
165
+ if absolute_paths:
166
+ return str(path)
167
+ base = root if root.is_dir() else root.parent
168
+ try:
169
+ return f"./{path.relative_to(base).as_posix()}"
170
+ except ValueError:
171
+ return path.as_posix()
172
+
173
+
174
+ def _shorten_line(line: str, limit: int) -> str:
175
+ """Limita líneas largas para proteger el contexto."""
176
+ if limit <= 0 or len(line) <= limit:
177
+ return line
178
+ return line[: max(0, limit - 3)] + "..."
179
+
180
+
181
+ def _matching_lines(filepath: Path, compiled: re.Pattern) -> list[tuple[int, str]]:
182
+ """Devuelve coincidencias por línea para un archivo de texto."""
183
+ try:
184
+ raw_lines = filepath.read_text(encoding="utf-8", errors="ignore").splitlines()
185
+ except OSError:
186
+ return []
187
+ return [(i, line) for i, line in enumerate(raw_lines) if compiled.search(line)]
188
+
189
+
190
+ def _semantic_graph_paths(root_path: Path) -> list[Path]:
191
+ """Localiza grafos semánticos conocidos de Higpertext/Nexus."""
192
+ root = root_path if root_path.is_dir() else root_path.parent
193
+ return [root / candidate for candidate in _GRAPH_CANDIDATES if (root / candidate).exists()]
194
+
195
+
196
+ def _graph_entry_text(rel_path: str, file_data: dict) -> list[str]:
197
+ """Extrae texto indexado del grafo semántico para búsqueda simbólica."""
198
+ entries = [rel_path, file_data.get("summary", "")]
199
+ entries.extend(file_data.get("imports", []))
200
+ for item in file_data.get("classes", []):
201
+ entries.append(f"class {item.get('name', '')} {item.get('docstring', '')}")
202
+ for method in item.get("methods", []):
203
+ entries.append(f"def {method.get('name', '')} {method.get('docstring', '')}")
204
+ for item in file_data.get("functions", []):
205
+ entries.append(f"def {item.get('name', '')} {item.get('docstring', '')}")
206
+ return [entry for entry in entries if entry]
207
+
208
+
209
+ def _search_semantic_graph(graph_path: Path, compiled: re.Pattern) -> list[str]:
210
+ """Busca en el grafo semántico sin exponer blobs completos."""
211
+ if graph_path.suffix == ".md":
212
+ hits = _matching_lines(graph_path, compiled)
213
+ return [f" >>> L{line + 1:>4}: {text}" for line, text in hits]
214
+ try:
215
+ data = json.loads(graph_path.read_text(encoding="utf-8"))
216
+ except (OSError, json.JSONDecodeError):
217
+ return []
218
+ results: list[str] = []
219
+ for rel_path, file_data in data.get("files", {}).items():
220
+ for entry in _graph_entry_text(rel_path, file_data):
221
+ if compiled.search(entry):
222
+ results.append(f" >>> [symbol] {rel_path}: {entry[:180]}")
223
+ break
224
+ return results
225
+
226
+
227
+ def _search_file(
228
+ filepath: Path,
229
+ compiled: re.Pattern,
230
+ context_lines: int,
231
+ max_results: int,
232
+ max_per_file: int,
233
+ found_count: list[int],
234
+ files_only: bool,
235
+ count_only: bool = False,
236
+ display_path: str | None = None,
237
+ line_limit: int = 240,
238
+ ) -> list[str]:
239
+ """Busca el patrón en un archivo y retorna líneas de resultado formateadas."""
240
+ try:
241
+ raw_lines = filepath.read_text(encoding="utf-8", errors="ignore").splitlines()
242
+ except OSError:
243
+ return []
244
+
245
+ file_hits = [(i, line) for i, line in enumerate(raw_lines) if compiled.search(line)]
246
+
247
+ if not file_hits:
248
+ return []
249
+
250
+ output: list[str] = [f"\n### {display_path or str(filepath)}"]
251
+
252
+ if files_only or count_only:
253
+ found_count[0] += len(file_hits)
254
+ if count_only:
255
+ output.append(f" {len(file_hits)} coincidencia(s)")
256
+ return output
257
+
258
+ for lineno, _ in file_hits[:max_per_file]:
259
+ if found_count[0] >= max_results:
260
+ output.append(f" ... (límite de {max_results} resultados alcanzado)")
261
+ break
262
+ start = max(0, lineno - context_lines)
263
+ end = min(len(raw_lines), lineno + context_lines + 1)
264
+ for idx in range(start, end):
265
+ marker = ">>>" if idx == lineno else " "
266
+ output.append(f" {marker} L{idx + 1:>4}: {_shorten_line(raw_lines[idx], line_limit)}")
267
+ if context_lines > 0:
268
+ output.append(" ---")
269
+ found_count[0] += 1
270
+
271
+ if len(file_hits) > max_per_file and found_count[0] < max_results:
272
+ output.append(f" ... y {
273
+ len(file_hits) -
274
+ max_per_file} coincidencia(s) más en este archivo")
275
+
276
+ return output
277
+
278
+
279
+ def _parse_bool(value: str) -> bool:
280
+ """Convierte strings comunes de CLI a booleano."""
281
+ return value.lower() in ("true", "1", "yes")
282
+
283
+
284
+ def _normalize_include_globs(include_source: str) -> list[str]:
285
+ """Normaliza extensiones simples y globs separados por coma."""
286
+ if not include_source:
287
+ return []
288
+ globs = [glob.strip() for glob in include_source.split(",") if glob.strip()]
289
+ return [glob if glob.startswith("*") else f"*.{glob.lstrip('.')}" for glob in globs]
290
+
291
+
292
+ def _resolve_include_globs(include_source: str, preset: str) -> list[str]:
293
+ """Combina preset de búsqueda y globs explícitos."""
294
+ normalized = _normalize_include_globs(include_source)
295
+ if normalized:
296
+ return normalized
297
+ return _PRESET_GLOBS.get(preset, [])
298
+
299
+
300
+ def _print_header(
301
+ pattern: str, mode: str, flags: str, path: Path, files_count: int, files_only: bool
302
+ ) -> None:
303
+ _log.info(f"[GREP SEARCH] pattern='{pattern}' ({mode}, {flags})")
304
+ _log.info(f"[*] Buscando en: {path} — {files_count} archivo(s) escaneados")
305
+ if files_only:
306
+ _log.info("[*] Modo files_only: se listan archivos; el total conserva coincidencias.")
307
+ _log.info("=" * 60)
308
+
309
+
310
+ def _emit_semantic_matches(
311
+ paths: list[Path],
312
+ compiled: re.Pattern,
313
+ summary: SearchSummary,
314
+ max_results: int,
315
+ output_json: bool,
316
+ ) -> None:
317
+ for graph_path in paths:
318
+ graph_lines = _search_semantic_graph(graph_path, compiled)[:max_results]
319
+ if not graph_lines:
320
+ continue
321
+ summary.files_matched += 1
322
+ summary.found_count[0] += len(graph_lines)
323
+ if output_json:
324
+ summary.json_matches.append(
325
+ {
326
+ "path": str(graph_path),
327
+ "semantic": True,
328
+ "matches": graph_lines,
329
+ }
330
+ )
331
+ else:
332
+ _log.info(f"\n### {graph_path} [semantic-graph]")
333
+ print("\n".join(graph_lines))
334
+
335
+
336
+ def _rank_files(
337
+ files: list[Path], compiled: re.Pattern, sort_mode: str, source_first: bool
338
+ ) -> list[Path]:
339
+ """Ordena archivos para mostrar primero los más accionables."""
340
+ if sort_mode == "path" and not source_first:
341
+ return sorted(files)
342
+
343
+ def score(path: Path) -> tuple:
344
+ source_penalty = 1 if source_first and _is_test_path(path) else 0
345
+ if sort_mode == "relevance":
346
+ hits = len(_matching_lines(path, compiled))
347
+ return (source_penalty, -hits, len(path.parts), path.as_posix())
348
+ return (source_penalty, 0, 0, path.as_posix())
349
+
350
+ return sorted(files, key=score)
351
+
352
+
353
+ def _emit_file_matches(
354
+ files: list[Path], compiled: re.Pattern, summary: SearchSummary, options: dict
355
+ ) -> None:
356
+ ranked_files = _rank_files(
357
+ files,
358
+ compiled,
359
+ options["sort"],
360
+ options["source_first"],
361
+ )
362
+ for filepath in ranked_files:
363
+ if summary.found_count[0] >= options["max_results"]:
364
+ break
365
+ lines = _search_file(
366
+ filepath,
367
+ compiled,
368
+ options["context_lines"],
369
+ options["max_results"],
370
+ options["max_per_file"],
371
+ summary.found_count,
372
+ options["files_only"],
373
+ options["count_only"],
374
+ display_path=_format_path(filepath, options["search_path"], options["absolute_paths"]),
375
+ line_limit=options["line_limit"],
376
+ )
377
+ if not lines:
378
+ continue
379
+ summary.files_matched += 1
380
+ if options["output_json"]:
381
+ summary.json_matches.append(
382
+ {
383
+ "path": str(filepath),
384
+ "semantic": False,
385
+ "matches": lines[1:],
386
+ }
387
+ )
388
+ else:
389
+ print("\n".join(lines))
390
+
391
+
392
+ def _print_json_summary(
393
+ pattern: str,
394
+ mode: str,
395
+ case_sensitive: bool,
396
+ files_count: int,
397
+ summary: SearchSummary,
398
+ ) -> None:
399
+ print(
400
+ json.dumps(
401
+ {
402
+ "pattern": pattern,
403
+ "mode": mode.lower(),
404
+ "case_sensitive": case_sensitive,
405
+ "files_scanned": files_count,
406
+ "files_matched": summary.files_matched,
407
+ "total_matches": summary.found_count[0],
408
+ "matches": summary.json_matches,
409
+ },
410
+ ensure_ascii=False,
411
+ indent=2,
412
+ )
413
+ )
414
+
415
+
416
+ def _print_text_summary(pattern: str, summary: SearchSummary) -> None:
417
+ _log.info("\n" + "=" * 60)
418
+ if summary.found_count[0] == 0:
419
+ _log.info(f"[NOT FOUND] No se encontraron coincidencias para: '{pattern}'")
420
+ return
421
+ _log.info(f"[FOUND] {summary.found_count[0]} coincidencia(s) encontrada(s)")
422
+ _log.info(f"[FILES] {summary.files_matched} archivo(s)/grafo(s) con coincidencias")
423
+
424
+
425
+ def run_search(
426
+ pattern: str,
427
+ search_path: Path,
428
+ include_globs: list[str],
429
+ excludes: set[str],
430
+ is_regex: bool,
431
+ case_sensitive: bool,
432
+ context_lines: int,
433
+ max_results: int,
434
+ files_only: bool,
435
+ count_only: bool = False,
436
+ semantic: bool = False,
437
+ output_json: bool = False,
438
+ max_per_file: int = 20,
439
+ sort: str = "relevance",
440
+ include_tests: bool = True,
441
+ source_first: bool = True,
442
+ max_file_size_kb: int = 1024,
443
+ line_limit: int = 240,
444
+ absolute_paths: bool = False,
445
+ ) -> int:
446
+ """Ejecuta la búsqueda y devuelve el número total de coincidencias."""
447
+ try:
448
+ compiled = _compile_pattern(pattern, is_regex, case_sensitive)
449
+ except re.error as exc:
450
+ _log.error(f"[ERROR] Patrón regex inválido: {exc}")
451
+ sys.exit(2)
452
+
453
+ files = _collect_files(search_path, include_globs, excludes, include_tests, max_file_size_kb)
454
+ summary = SearchSummary()
455
+ header_mode = "REGEX" if is_regex else "LITERAL"
456
+ flags_str = "case-sensitive" if case_sensitive else "case-insensitive"
457
+ if not output_json:
458
+ _print_header(pattern, header_mode, flags_str, search_path, len(files), files_only)
459
+
460
+ if semantic:
461
+ _emit_semantic_matches(
462
+ _semantic_graph_paths(search_path),
463
+ compiled,
464
+ summary,
465
+ max_results,
466
+ output_json,
467
+ )
468
+
469
+ _emit_file_matches(
470
+ files,
471
+ compiled,
472
+ summary,
473
+ {
474
+ "context_lines": context_lines,
475
+ "max_results": max_results,
476
+ "max_per_file": max_per_file,
477
+ "files_only": files_only,
478
+ "count_only": count_only,
479
+ "output_json": output_json,
480
+ "sort": sort,
481
+ "source_first": source_first,
482
+ "search_path": search_path,
483
+ "absolute_paths": absolute_paths,
484
+ "line_limit": line_limit,
485
+ },
486
+ )
487
+
488
+ if output_json:
489
+ _print_json_summary(pattern, header_mode, case_sensitive, len(files), summary)
490
+ return summary.found_count[0]
491
+
492
+ _print_text_summary(pattern, summary)
493
+ return summary.found_count[0]
494
+
495
+
496
+ def _positive_int(value: str) -> int:
497
+ parsed = int(value)
498
+ if parsed <= 0:
499
+ raise argparse.ArgumentTypeError("debe ser mayor que 0")
500
+ return parsed
501
+
502
+
503
+ def _non_negative_int(value: str) -> int:
504
+ parsed = int(value)
505
+ if parsed < 0:
506
+ raise argparse.ArgumentTypeError("no puede ser negativo")
507
+ return parsed
508
+
509
+
510
+ def _sort_mode(value: str) -> str:
511
+ if value not in {"relevance", "path"}:
512
+ raise argparse.ArgumentTypeError("debe ser 'relevance' o 'path'")
513
+ return value
514
+
515
+
516
+ def _preset(value: str) -> str:
517
+ if value not in _PRESET_GLOBS:
518
+ allowed = ", ".join(sorted(_PRESET_GLOBS))
519
+ raise argparse.ArgumentTypeError(f"preset inválido; usa uno de: {allowed}")
520
+ return value
521
+
522
+
523
+ def main() -> None:
524
+ """Punto de entrada del grep search."""
525
+ parser = argparse.ArgumentParser(description="higpertext Grep Search")
526
+ parser.add_argument("--pattern", required=True, help="Patrón de búsqueda")
527
+ parser.add_argument("--path", default=".", help="Directorio o archivo donde buscar")
528
+ parser.add_argument("--include", default="", help="Globs a incluir (ej: *.py,*.ts)")
529
+ parser.add_argument(
530
+ "--extension", default="", help="Alias de --include (ej: py,ts o *.py,*.ts)"
531
+ )
532
+ parser.add_argument(
533
+ "--exclude",
534
+ default="",
535
+ help="Directorios/archivos a excluir (ej: __pycache__,.venv)",
536
+ )
537
+ parser.add_argument("--regex", default="false", help="Usar regex (true/false)")
538
+ parser.add_argument("--case_sensitive", default="false", help="Case-sensitive (true/false)")
539
+ parser.add_argument("--context", default="0", type=_non_negative_int, help="Líneas de contexto")
540
+ parser.add_argument(
541
+ "--before",
542
+ default=None,
543
+ type=_non_negative_int,
544
+ help="Líneas antes de cada match (alias específico)",
545
+ )
546
+ parser.add_argument(
547
+ "--after",
548
+ default=None,
549
+ type=_non_negative_int,
550
+ help="Líneas después de cada match; usa el mayor junto con --before",
551
+ )
552
+ parser.add_argument(
553
+ "--max_results", default="100", type=_positive_int, help="Máximo de resultados"
554
+ )
555
+ parser.add_argument(
556
+ "--max_per_file",
557
+ default="20",
558
+ type=_positive_int,
559
+ help="Máximo de resultados por archivo",
560
+ )
561
+ parser.add_argument(
562
+ "--line_limit",
563
+ default="240",
564
+ type=_non_negative_int,
565
+ help="Caracteres máximos por línea; 0 no trunca",
566
+ )
567
+ parser.add_argument(
568
+ "--max_file_size_kb",
569
+ default="1024",
570
+ type=_non_negative_int,
571
+ help="Ignora archivos mayores a este tamaño; 0 desactiva",
572
+ )
573
+ parser.add_argument(
574
+ "--sort", default="relevance", type=_sort_mode, help="Orden: relevance o path"
575
+ )
576
+ parser.add_argument(
577
+ "--preset",
578
+ default="all",
579
+ type=_preset,
580
+ help="Preset de includes: all, code, python, web, docs, config",
581
+ )
582
+ parser.add_argument("--include_tests", default="true", help="Incluye tests/specs (true/false)")
583
+ parser.add_argument(
584
+ "--source_first",
585
+ default="true",
586
+ help="Prioriza código fuente sobre tests al ordenar (true/false)",
587
+ )
588
+ parser.add_argument(
589
+ "--files_only", default="false", help="Solo nombres de archivo (true/false)"
590
+ )
591
+ parser.add_argument("--count", default="false", help="Muestra conteo por archivo (true/false)")
592
+ parser.add_argument(
593
+ "--semantic",
594
+ default="false",
595
+ help="Busca también en grafo semántico Nexus/Higpertext (true/false)",
596
+ )
597
+ parser.add_argument(
598
+ "--json", default="false", help="Emite salida JSON estructurada (true/false)"
599
+ )
600
+ parser.add_argument(
601
+ "--absolute_paths",
602
+ default="false",
603
+ help="Muestra rutas absolutas en vez de relativas (true/false)",
604
+ )
605
+ parser.add_argument(
606
+ "--all",
607
+ default="false",
608
+ help="Busca también en directorios de agentes/configuración excluidos por defecto (true/false)", # noqa: E501
609
+ )
610
+ args = parser.parse_args()
611
+
612
+ search_path = Path(args.path).resolve()
613
+ if not search_path.exists():
614
+ _log.error(f"[ERROR] El path no existe: {search_path}")
615
+ sys.exit(1)
616
+
617
+ include_globs = _resolve_include_globs(args.include or args.extension, args.preset)
618
+ custom_excludes = (
619
+ {e.strip() for e in args.exclude.split(",") if e.strip()} if args.exclude else set()
620
+ )
621
+ include_all = _parse_bool(args.all)
622
+ excludes = _build_excludes(custom_excludes, include_all)
623
+
624
+ run_search(
625
+ pattern=args.pattern,
626
+ search_path=search_path,
627
+ include_globs=include_globs,
628
+ excludes=excludes,
629
+ is_regex=_parse_bool(args.regex),
630
+ case_sensitive=_parse_bool(args.case_sensitive),
631
+ context_lines=max(args.context, args.before or 0, args.after or 0),
632
+ max_results=args.max_results,
633
+ files_only=_parse_bool(args.files_only),
634
+ count_only=_parse_bool(args.count),
635
+ semantic=_parse_bool(args.semantic),
636
+ output_json=_parse_bool(args.json),
637
+ max_per_file=args.max_per_file,
638
+ sort=args.sort,
639
+ include_tests=_parse_bool(args.include_tests),
640
+ source_first=_parse_bool(args.source_first),
641
+ max_file_size_kb=args.max_file_size_kb,
642
+ line_limit=args.line_limit,
643
+ absolute_paths=_parse_bool(args.absolute_paths),
644
+ )
645
+
646
+
647
+ if __name__ == "__main__":
648
+ main()