higpertext-cli 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- config/adapters_config.json +450 -0
- config/antigravity_agent_template.json +31 -0
- config/app_config.json +174 -0
- config/context_engine.json +33 -0
- config/environments/model_defaults.json +5 -0
- config/governance/branching_strategy.json +36 -0
- config/governance/deployment_gates.json +30 -0
- config/governance/guidelines_contract.json +54 -0
- config/governance/quality_gates.json +39 -0
- config/governance/section_rules.json +22 -0
- config/governance/security_guardrails.json +52 -0
- config/hooks/README.md +35 -0
- config/hooks/custom/test_output_limiter.json +9 -0
- config/hooks/global/session_prompt.json +9 -0
- config/htx_config.json +24 -0
- config/profile_learner.json +18 -0
- config/profiles/base_agent.json +40 -0
- config/profiles/base_auditor.json +19 -0
- config/profiles/base_developer.json +19 -0
- config/profiles/base_operator.json +16 -0
- config/profiles/global.json +33 -0
- config/profiles/software_developer.json +23 -0
- config/router_content.json +137 -0
- config/semantic_graph.json +66 -0
- config/workflows/ado_release_flow.json +38 -0
- config/workflows/docs-update.json +33 -0
- config/workflows/governance-check.yaml +26 -0
- config/workflows/guidelines-sync.json +40 -0
- config/workflows/higpertext-build.json +73 -0
- config/workflows/higpertext-plan.json +38 -0
- config/workflows/higpertext-review.json +41 -0
- config/workflows/pr-quality-check.json +56 -0
- config/workflows/quality-remediation.json +57 -0
- higpertext/__init__.py +18 -0
- higpertext/adapters/__init__.py +27 -0
- higpertext/adapters/adapter_utils.py +604 -0
- higpertext/adapters/claude_adapter/__init__.py +0 -0
- higpertext/adapters/claude_adapter/claude_adapter.py +154 -0
- higpertext/adapters/copilot_adapter/__init__.py +0 -0
- higpertext/adapters/copilot_adapter/copilot_adapter.py +231 -0
- higpertext/adapters/gemini_adapter/__init__.py +0 -0
- higpertext/adapters/gemini_adapter/gemini_adapter.py +211 -0
- higpertext/adapters/llm_formatter.py +46 -0
- higpertext/adapters/open_code_adapter/__init__.py +0 -0
- higpertext/adapters/open_code_adapter/open_code_adapter.py +480 -0
- higpertext/capabilities/capabilities_runner.py +216 -0
- higpertext/capabilities/common/agent-builder.json +54 -0
- higpertext/capabilities/common/agent-sync.json +34 -0
- higpertext/capabilities/common/code-skeletonizer.json +35 -0
- higpertext/capabilities/common/commit-report.json +42 -0
- higpertext/capabilities/common/context-assembler.json +37 -0
- higpertext/capabilities/common/context-budget-report.json +15 -0
- higpertext/capabilities/common/dep-manager.json +43 -0
- higpertext/capabilities/common/docs-sync.json +14 -0
- higpertext/capabilities/common/doctor.json +18 -0
- higpertext/capabilities/common/efficiency-meter.json +31 -0
- higpertext/capabilities/common/env-catalog.json +13 -0
- higpertext/capabilities/common/env-clean.json +14 -0
- higpertext/capabilities/common/env-logs.json +16 -0
- higpertext/capabilities/common/env-runner.json +23 -0
- higpertext/capabilities/common/env-status.json +13 -0
- higpertext/capabilities/common/env-stop.json +14 -0
- higpertext/capabilities/common/env-template.json +14 -0
- higpertext/capabilities/common/error-context-locator.json +23 -0
- higpertext/capabilities/common/eval-agent.json +33 -0
- higpertext/capabilities/common/file-map.json +17 -0
- higpertext/capabilities/common/governance-exception.json +54 -0
- higpertext/capabilities/common/graph-query.json +59 -0
- higpertext/capabilities/common/graph-rebuild.json +31 -0
- higpertext/capabilities/common/graph-visualize.json +37 -0
- higpertext/capabilities/common/grep-search.json +176 -0
- higpertext/capabilities/common/higpertext-tester.json +25 -0
- higpertext/capabilities/common/hook-health.json +19 -0
- higpertext/capabilities/common/hook-sync-check.json +19 -0
- higpertext/capabilities/common/hooks-manager.json +55 -0
- higpertext/capabilities/common/knowledge-asker.json +27 -0
- higpertext/capabilities/common/list-rules.json +27 -0
- higpertext/capabilities/common/llm-invoke.json +59 -0
- higpertext/capabilities/common/load-rules.json +37 -0
- higpertext/capabilities/common/memory-manager.json +65 -0
- higpertext/capabilities/common/quality-scan.json +21 -0
- higpertext/capabilities/common/quality-updater.json +35 -0
- higpertext/capabilities/common/rag-index.json +17 -0
- higpertext/capabilities/common/report-viewer.json +24 -0
- higpertext/capabilities/common/roadmap-report.json +37 -0
- higpertext/capabilities/common/scripts/_env_cli.py +65 -0
- higpertext/capabilities/common/scripts/agent_builder.py +60 -0
- higpertext/capabilities/common/scripts/agent_sync.py +56 -0
- higpertext/capabilities/common/scripts/ask_higpertext.py +38 -0
- higpertext/capabilities/common/scripts/code_skeletonizer.py +225 -0
- higpertext/capabilities/common/scripts/commit_report.py +134 -0
- higpertext/capabilities/common/scripts/context_assembler.py +70 -0
- higpertext/capabilities/common/scripts/context_budget_report.py +53 -0
- higpertext/capabilities/common/scripts/dep_manager.py +81 -0
- higpertext/capabilities/common/scripts/docs_sync.py +981 -0
- higpertext/capabilities/common/scripts/doctor.py +144 -0
- higpertext/capabilities/common/scripts/efficiency_meter.py +83 -0
- higpertext/capabilities/common/scripts/env_catalog.py +47 -0
- higpertext/capabilities/common/scripts/env_clean.py +30 -0
- higpertext/capabilities/common/scripts/env_logs.py +32 -0
- higpertext/capabilities/common/scripts/env_runner.py +53 -0
- higpertext/capabilities/common/scripts/env_status.py +38 -0
- higpertext/capabilities/common/scripts/env_stop.py +30 -0
- higpertext/capabilities/common/scripts/env_template.py +73 -0
- higpertext/capabilities/common/scripts/error_context_locator.py +138 -0
- higpertext/capabilities/common/scripts/eval_agent.py +80 -0
- higpertext/capabilities/common/scripts/file_map.py +95 -0
- higpertext/capabilities/common/scripts/governance_exception.py +116 -0
- higpertext/capabilities/common/scripts/graph_query.py +104 -0
- higpertext/capabilities/common/scripts/graph_rebuild.py +107 -0
- higpertext/capabilities/common/scripts/graph_visualize.py +76 -0
- higpertext/capabilities/common/scripts/grep_search.py +648 -0
- higpertext/capabilities/common/scripts/higpertext_tester.py +102 -0
- higpertext/capabilities/common/scripts/hook_health.py +149 -0
- higpertext/capabilities/common/scripts/hook_sync_check.py +134 -0
- higpertext/capabilities/common/scripts/hooks_manager.py +171 -0
- higpertext/capabilities/common/scripts/list_rules.py +175 -0
- higpertext/capabilities/common/scripts/llm_invoke.py +135 -0
- higpertext/capabilities/common/scripts/load_rules.py +379 -0
- higpertext/capabilities/common/scripts/memory_manager.py +210 -0
- higpertext/capabilities/common/scripts/presentation_engine.py +63 -0
- higpertext/capabilities/common/scripts/quality_scan.py +132 -0
- higpertext/capabilities/common/scripts/rag_index.py +39 -0
- higpertext/capabilities/common/scripts/report_viewer.py +106 -0
- higpertext/capabilities/common/scripts/roadmap_report.py +73 -0
- higpertext/capabilities/common/scripts/search_router.py +111 -0
- higpertext/capabilities/common/scripts/semantic_diff.py +166 -0
- higpertext/capabilities/common/scripts/semantic_search.py +43 -0
- higpertext/capabilities/common/scripts/session_control.py +136 -0
- higpertext/capabilities/common/scripts/smart_read.py +232 -0
- higpertext/capabilities/common/scripts/subagent_executor.py +143 -0
- higpertext/capabilities/common/scripts/sync_agents.py +353 -0
- higpertext/capabilities/common/scripts/task_decomposer.py +78 -0
- higpertext/capabilities/common/scripts/telemetry_report.py +36 -0
- higpertext/capabilities/common/search-router.json +24 -0
- higpertext/capabilities/common/semantic-diff.json +40 -0
- higpertext/capabilities/common/semantic-search.json +19 -0
- higpertext/capabilities/common/session-clean.json +20 -0
- higpertext/capabilities/common/session-start.json +44 -0
- higpertext/capabilities/common/smart-read.json +28 -0
- higpertext/capabilities/common/subagent-executor.json +25 -0
- higpertext/capabilities/common/sync-agents.json +32 -0
- higpertext/capabilities/common/task-decomposer.json +37 -0
- higpertext/capabilities/common/telemetry-report.json +23 -0
- higpertext/capabilities/git/__init__.py +0 -0
- higpertext/capabilities/git/committer.json +61 -0
- higpertext/capabilities/git/diff.json +33 -0
- higpertext/capabilities/git/ls-files.json +44 -0
- higpertext/capabilities/git/rm.json +27 -0
- higpertext/capabilities/git/scripts/__init__.py +0 -0
- higpertext/capabilities/git/scripts/commit_changes.py +1077 -0
- higpertext/capabilities/git/scripts/git_diff.py +171 -0
- higpertext/capabilities/git/scripts/git_ls_files.py +376 -0
- higpertext/capabilities/git/scripts/git_rm.py +62 -0
- higpertext/capabilities/security/k8s-auditor.json +33 -0
- higpertext/capabilities/security/scripts/k8s_auditor.py +307 -0
- higpertext/capabilities/security/scripts/secret_scanner.py +235 -0
- higpertext/capabilities/security/secret-scanner.json +32 -0
- higpertext/hooks/__init__.py +28 -0
- higpertext/hooks/_compat.py +27 -0
- higpertext/hooks/hook_tasks/__init__.py +1 -0
- higpertext/hooks/hook_tasks/_rules/__init__.py +0 -0
- higpertext/hooks/hook_tasks/_rules/bash_rules.py +635 -0
- higpertext/hooks/hook_tasks/_rules/context_engine_rule.py +79 -0
- higpertext/hooks/hook_tasks/_rules/context_rules.py +199 -0
- higpertext/hooks/hook_tasks/_rules/governance_adapter.py +72 -0
- higpertext/hooks/hook_tasks/_rules/profile_rules.json +25 -0
- higpertext/hooks/hook_tasks/_rules/quality_rules.py +86 -0
- higpertext/hooks/hook_tasks/_rules/security_rules.py +214 -0
- higpertext/hooks/hook_tasks/_rules/session_rules.py +316 -0
- higpertext/hooks/hook_tasks/_rules/telemetry_rules.py +121 -0
- higpertext/hooks/hook_tasks/audit_logger_hook.py +28 -0
- higpertext/hooks/hook_tasks/hook_bash_guard.py +101 -0
- higpertext/hooks/hook_tasks/hook_code_quality.py +48 -0
- higpertext/hooks/hook_tasks/hook_context_hint.py +46 -0
- higpertext/hooks/hook_tasks/hook_context_manager.py +44 -0
- higpertext/hooks/hook_tasks/hook_io.py +122 -0
- higpertext/hooks/hook_tasks/hook_loop_guard.py +182 -0
- higpertext/hooks/hook_tasks/hook_post_observer.py +54 -0
- higpertext/hooks/hook_tasks/hook_read_guard.py +85 -0
- higpertext/hooks/hook_tasks/hook_security_guard.py +81 -0
- higpertext/hooks/hook_tasks/hook_session_prompt.py +83 -0
- higpertext/hooks/hook_tasks/hook_session_stop.py +115 -0
- higpertext/hooks/hook_tasks/hook_utils.py +144 -0
- higpertext/hooks/hook_tasks/session_guard_hook.py +23 -0
- higpertext/hooks/hook_tasks/telemetry_utils.py +176 -0
- higpertext/hooks/hook_tasks/test_echo_hook.py +33 -0
- higpertext/hooks/hook_tasks/webhook_hook.py +54 -0
- higpertext/hooks/hook_tasks/workflow_runner_hook.py +49 -0
- higpertext/hooks/hooks_catalog.json +116 -0
- higpertext/kernel/__init__.py +63 -0
- higpertext/kernel/_compat.py +138 -0
- higpertext/kernel/app_config.py +117 -0
- higpertext/kernel/application/__init__.py +13 -0
- higpertext/kernel/application/agent_registry.py +102 -0
- higpertext/kernel/application/capability_manager.py +61 -0
- higpertext/kernel/application/commit_reporter.py +247 -0
- higpertext/kernel/application/context_builder.py +166 -0
- higpertext/kernel/application/context_engine.py +409 -0
- higpertext/kernel/application/engine.py +41 -0
- higpertext/kernel/application/env_runtime.py +174 -0
- higpertext/kernel/application/environment_manager.py +154 -0
- higpertext/kernel/application/governance.py +192 -0
- higpertext/kernel/application/hook_registry.py +102 -0
- higpertext/kernel/application/hook_renderer.py +720 -0
- higpertext/kernel/application/ports.py +49 -0
- higpertext/kernel/application/profile_learner.py +358 -0
- higpertext/kernel/application/profile_service.py +205 -0
- higpertext/kernel/application/profile_services.py +6 -0
- higpertext/kernel/application/profile_use_cases.py +93 -0
- higpertext/kernel/application/rag_service.py +75 -0
- higpertext/kernel/application/roadmap_reporter.py +178 -0
- higpertext/kernel/application/semantic_engine.py +258 -0
- higpertext/kernel/application/session_services.py +33 -0
- higpertext/kernel/application/skill_hook_compiler.py +85 -0
- higpertext/kernel/application/telemetry.py +326 -0
- higpertext/kernel/application/workflow_manager.py +176 -0
- higpertext/kernel/config_paths.py +66 -0
- higpertext/kernel/domain/__init__.py +12 -0
- higpertext/kernel/domain/agent_registry.py +23 -0
- higpertext/kernel/domain/commit_reporter.py +155 -0
- higpertext/kernel/domain/compilers.py +7 -0
- higpertext/kernel/domain/context_engine.py +319 -0
- higpertext/kernel/domain/entities.py +51 -0
- higpertext/kernel/domain/env_runtime.py +62 -0
- higpertext/kernel/domain/governance.py +198 -0
- higpertext/kernel/domain/hook_models.py +29 -0
- higpertext/kernel/domain/profile_learner.py +186 -0
- higpertext/kernel/domain/rag.py +70 -0
- higpertext/kernel/domain/repositories.py +8 -0
- higpertext/kernel/domain/roadmap_reporter.py +80 -0
- higpertext/kernel/domain/semantic_engine.py +107 -0
- higpertext/kernel/engine.py +42 -0
- higpertext/kernel/htx_resolver.py +69 -0
- higpertext/kernel/infrastructure/__init__.py +13 -0
- higpertext/kernel/infrastructure/agent_registry.py +40 -0
- higpertext/kernel/infrastructure/cache/capability_cache.py +319 -0
- higpertext/kernel/infrastructure/capability_helper.py +40 -0
- higpertext/kernel/infrastructure/cli/__init__.py +1 -0
- higpertext/kernel/infrastructure/cli/agent_commands.py +62 -0
- higpertext/kernel/infrastructure/cli/arguments.py +39 -0
- higpertext/kernel/infrastructure/cli/capability_command_builder.py +86 -0
- higpertext/kernel/infrastructure/cli/capability_task_service.py +234 -0
- higpertext/kernel/infrastructure/cli/cli_search.py +234 -0
- higpertext/kernel/infrastructure/cli/parameter_contracts.py +83 -0
- higpertext/kernel/infrastructure/cli/parser_builder.py +122 -0
- higpertext/kernel/infrastructure/cli/profile_commands.py +89 -0
- higpertext/kernel/infrastructure/cli/roadmap_commands.py +117 -0
- higpertext/kernel/infrastructure/cli/router.py +1110 -0
- higpertext/kernel/infrastructure/cli/session_commands.py +36 -0
- higpertext/kernel/infrastructure/cli/task_commands.py +23 -0
- higpertext/kernel/infrastructure/cli/task_result_reporter.py +56 -0
- higpertext/kernel/infrastructure/cli/workflow_commands.py +25 -0
- higpertext/kernel/infrastructure/compilers/__init__.py +3 -0
- higpertext/kernel/infrastructure/compilers/factory.py +27 -0
- higpertext/kernel/infrastructure/compilers/graph_compiler.py +20 -0
- higpertext/kernel/infrastructure/compilers/guide_compiler.py +50 -0
- higpertext/kernel/infrastructure/compilers/hook_compiler.py +69 -0
- higpertext/kernel/infrastructure/compilers/playbook_compiler.py +154 -0
- higpertext/kernel/infrastructure/context_engine.py +303 -0
- higpertext/kernel/infrastructure/database/local_vector_store.py +99 -0
- higpertext/kernel/infrastructure/deployment/__init__.py +1 -0
- higpertext/kernel/infrastructure/deployment/resource_deployer.py +283 -0
- higpertext/kernel/infrastructure/diagnostics/__init__.py +1 -0
- higpertext/kernel/infrastructure/diagnostics/health.py +191 -0
- higpertext/kernel/infrastructure/env_runtime.py +227 -0
- higpertext/kernel/infrastructure/execution/__init__.py +1 -0
- higpertext/kernel/infrastructure/execution/parallel.py +188 -0
- higpertext/kernel/infrastructure/execution/resilience.py +155 -0
- higpertext/kernel/infrastructure/file_repositories.py +213 -0
- higpertext/kernel/infrastructure/governance.py +198 -0
- higpertext/kernel/infrastructure/hook_config_loader.py +53 -0
- higpertext/kernel/infrastructure/hook_webhook_dispatcher.py +61 -0
- higpertext/kernel/infrastructure/hook_workflow_bridge.py +60 -0
- higpertext/kernel/infrastructure/llm/__init__.py +6 -0
- higpertext/kernel/infrastructure/llm/provider.py +46 -0
- higpertext/kernel/infrastructure/llm/providers/__init__.py +0 -0
- higpertext/kernel/infrastructure/llm/providers/anthropic_provider.py +94 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_embeddings.py +74 -0
- higpertext/kernel/infrastructure/llm/providers/gemini_provider.py +101 -0
- higpertext/kernel/infrastructure/llm/providers/ollama_provider.py +110 -0
- higpertext/kernel/infrastructure/llm/providers/openai_provider.py +98 -0
- higpertext/kernel/infrastructure/llm/registry.py +81 -0
- higpertext/kernel/infrastructure/logger.py +303 -0
- higpertext/kernel/infrastructure/output_store.py +70 -0
- higpertext/kernel/infrastructure/parser/__init__.py +1 -0
- higpertext/kernel/infrastructure/parser/code_chunker.py +144 -0
- higpertext/kernel/infrastructure/parser/language/__init__.py +14 -0
- higpertext/kernel/infrastructure/parser/language/base.py +41 -0
- higpertext/kernel/infrastructure/parser/language/powershell_parser.py +35 -0
- higpertext/kernel/infrastructure/parser/language/python_parser.py +98 -0
- higpertext/kernel/infrastructure/parser/language/typescript_parser.py +91 -0
- higpertext/kernel/infrastructure/parser/semantic_graph.py +409 -0
- higpertext/kernel/infrastructure/presentation/__init__.py +1 -0
- higpertext/kernel/infrastructure/presentation/html_renderer.py +137 -0
- higpertext/kernel/infrastructure/presentation/markdown_renderer.py +84 -0
- higpertext/kernel/infrastructure/presentation/markdown_report_renderer.py +97 -0
- higpertext/kernel/infrastructure/profile_store.py +28 -0
- higpertext/kernel/infrastructure/semantic_engine.py +289 -0
- higpertext/kernel/infrastructure/telemetry_reporter.py +132 -0
- higpertext/kernel/infrastructure/validation/__init__.py +1 -0
- higpertext/kernel/infrastructure/validation/contract_validator.py +163 -0
- higpertext/kernel/pkg_resources.py +38 -0
- higpertext/kernel/session_manager.py +319 -0
- higpertext/templates/env/generic-shell.yaml +21 -0
- higpertext/templates/env/node-vitest.yaml +27 -0
- higpertext/templates/env/python-pytest.yaml +29 -0
- higpertext/templates/html/commit_body.html +20 -0
- higpertext/templates/html/commit_diff.html +4 -0
- higpertext/templates/html/commit_index.html +29 -0
- higpertext/templates/html/commit_layer.html +11 -0
- higpertext/templates/html/commit_shell.html +28 -0
- higpertext/templates/html/graph_visualize.html +86 -0
- higpertext/templates/html/roadmap_body.html +12 -0
- higpertext/templates/html/roadmap_phase.html +5 -0
- higpertext/templates/html/roadmap_shell.html +29 -0
- higpertext/templates/markdown/commit_report.md +18 -0
- higpertext/templates/markdown/efficiency_report.md +12 -0
- higpertext/templates/markdown/roadmap_report.md +25 -0
- higpertext/templates/skills/best-practices.md +7 -0
- higpertext/templates/skills/clean-code.md +8 -0
- higpertext/templates/skills/ddd-standards.md +7 -0
- higpertext/templates/skills/tdd-practices.md +7 -0
- higpertext/templates/subagents/architect.md +7 -0
- higpertext/templates/subagents/test-engineer.md +7 -0
- higpertext/templates/workflows/build.json +23 -0
- higpertext/templates/workflows/compact.json +21 -0
- higpertext/templates/workflows/plan.json +59 -0
- higpertext/templates/workflows/review.json +26 -0
- higpertext/templates/workflows/spec.json +27 -0
- higpertext_cli-0.8.0.dist-info/METADATA +35 -0
- higpertext_cli-0.8.0.dist-info/RECORD +335 -0
- higpertext_cli-0.8.0.dist-info/WHEEL +5 -0
- higpertext_cli-0.8.0.dist-info/entry_points.txt +2 -0
- higpertext_cli-0.8.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,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()
|