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,44 @@
|
|
|
1
|
+
"""Hook PreCompact — preserva estado higpertext crítico antes de compresión de contexto."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.hooks.hook_tasks._rules.context_rules import check_context_pressure
|
|
5
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
11
|
+
_log = get_logger()
|
|
12
|
+
|
|
13
|
+
_SRC = Path(__file__).resolve().parents[3] # src/
|
|
14
|
+
if str(_SRC) not in sys.path:
|
|
15
|
+
sys.path.insert(0, str(_SRC))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> None:
|
|
22
|
+
try:
|
|
23
|
+
root = get_project_root()
|
|
24
|
+
result = check_context_pressure(root)
|
|
25
|
+
if result.severity == "context":
|
|
26
|
+
print(
|
|
27
|
+
json.dumps(
|
|
28
|
+
{
|
|
29
|
+
"continue": True,
|
|
30
|
+
"hookSpecificOutput": {
|
|
31
|
+
"hookEventName": "PreCompact",
|
|
32
|
+
"additionalContext": result.message,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
print(json.dumps({"continue": True}))
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
print(json.dumps({"continue": True, "error": str(exc)}))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
main()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Protocolo I/O centralizado para hook tasks de higpertext.
|
|
2
|
+
|
|
3
|
+
Single source of truth para:
|
|
4
|
+
- Leer el payload de stdin (read_payload, read_tool_command)
|
|
5
|
+
- Leer archivos JSON del disco (read_json_file)
|
|
6
|
+
- Emitir respuestas JSON al runtime del asistente
|
|
7
|
+
(emit_continue, emit_context, emit_block, emit_stop_reason)
|
|
8
|
+
- Whitelist cross-platform de comandos permitidos (WHITELIST)
|
|
9
|
+
- Directorios de skills/subagentes por asistente (SKILL_DIRS, AGENT_DIRS)
|
|
10
|
+
- Decorador @hook_main que envuelve cualquier main() con manejo de errores
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
from typing import Callable
|
|
15
|
+
import functools
|
|
16
|
+
import json
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
_SRC = Path(__file__).resolve().parents[3] # src/
|
|
22
|
+
if str(_SRC) not in sys.path:
|
|
23
|
+
sys.path.insert(0, str(_SRC))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Directorios efímeros de sesión por asistente — single source para todos los hooks
|
|
27
|
+
SKILL_DIRS: dict[str, str] = {
|
|
28
|
+
"claude": ".claude/skills",
|
|
29
|
+
"gemini": ".gemini/skills",
|
|
30
|
+
"antigravity": ".agents/skills",
|
|
31
|
+
"opencode": ".opencode/skills",
|
|
32
|
+
"copilot": ".github/skills",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
AGENT_DIRS: dict[str, str] = {
|
|
36
|
+
"claude": ".claude/subagents",
|
|
37
|
+
"gemini": ".gemini/subagents",
|
|
38
|
+
"antigravity": ".agents/subagents",
|
|
39
|
+
"opencode": ".opencode/subagents",
|
|
40
|
+
"copilot": ".github/subagents",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Single source: permite htx.py directo o venv en Linux/Windows
|
|
44
|
+
WHITELIST = re.compile(r"python htx\.py|\.venv[/\\](bin|Scripts)[/\\]python")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def read_json_file(path: Path) -> dict:
|
|
48
|
+
"""Lee y parsea un archivo JSON del disco. Retorna {} ante cualquier error."""
|
|
49
|
+
try:
|
|
50
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
51
|
+
except (OSError, json.JSONDecodeError):
|
|
52
|
+
return {}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def read_payload() -> dict:
|
|
56
|
+
"""Lee y parsea el JSON de stdin. Retorna {} ante cualquier error."""
|
|
57
|
+
try:
|
|
58
|
+
return json.load(sys.stdin)
|
|
59
|
+
except (json.JSONDecodeError, ValueError, EOFError):
|
|
60
|
+
return {}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def read_tool_command(payload: dict) -> str:
|
|
64
|
+
"""Extrae el comando de tool_input, con fallback a CommandLine (PowerShell)."""
|
|
65
|
+
tool_input = payload.get("tool_input", {})
|
|
66
|
+
return tool_input.get("command") or tool_input.get("CommandLine") or ""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def emit_continue(error: str = "") -> None:
|
|
70
|
+
"""Emite continue:True, opcionalmente con campo error."""
|
|
71
|
+
out: dict = {"continue": True}
|
|
72
|
+
if error:
|
|
73
|
+
out["error"] = error
|
|
74
|
+
print(json.dumps(out))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def emit_context(event: str, text: str) -> None:
|
|
78
|
+
"""Emite continue:True con additionalContext (hook informativo)."""
|
|
79
|
+
print(
|
|
80
|
+
json.dumps(
|
|
81
|
+
{
|
|
82
|
+
"continue": True,
|
|
83
|
+
"hookSpecificOutput": {
|
|
84
|
+
"hookEventName": event,
|
|
85
|
+
"additionalContext": text,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def emit_block(event: str, text: str) -> None:
|
|
93
|
+
"""Emite continue:False bloqueando la herramienta (hard block)."""
|
|
94
|
+
print(
|
|
95
|
+
json.dumps(
|
|
96
|
+
{
|
|
97
|
+
"continue": False,
|
|
98
|
+
"hookSpecificOutput": {
|
|
99
|
+
"hookEventName": event,
|
|
100
|
+
"additionalContext": text,
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def emit_stop_reason(text: str) -> None:
|
|
108
|
+
"""Emite continue:True con stopReason para el evento Stop."""
|
|
109
|
+
print(json.dumps({"continue": True, "stopReason": text}))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def hook_main(fn: Callable) -> Callable:
|
|
113
|
+
"""Decorador que envuelve main() capturando excepciones → emit_continue+error."""
|
|
114
|
+
|
|
115
|
+
@functools.wraps(fn)
|
|
116
|
+
def wrapper(*args, **kwargs):
|
|
117
|
+
try:
|
|
118
|
+
fn(*args, **kwargs)
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
emit_continue(str(exc))
|
|
121
|
+
|
|
122
|
+
return wrapper
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Hook PostToolUse + UserPromptSubmit — detección de bucle LLM en tiempo de ejecución.
|
|
2
|
+
|
|
3
|
+
Detecta dos síntomas de repetition-collapse:
|
|
4
|
+
|
|
5
|
+
1. **Repetición de tool_input** (PostToolUse): el agente invocó exactamente la misma
|
|
6
|
+
herramienta con exactamente los mismos parámetros N veces consecutivas sin éxito.
|
|
7
|
+
→ emit_block al 3er intento idéntico.
|
|
8
|
+
|
|
9
|
+
2. **Saturación de contexto** (PostToolUse): la respuesta de una herramienta supera
|
|
10
|
+
LARGE_OUTPUT_CHARS caracteres (dump masivo de error o archivo completo).
|
|
11
|
+
→ emit_context con aviso al modelo para que no re-emita el blob.
|
|
12
|
+
|
|
13
|
+
El estado de repetición se persiste en .higpertext/state/loop_guard.json dentro de la
|
|
14
|
+
sesión activa, de modo que sobrevive entre turnos del mismo asistente.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
import higpertext.hooks.hook_tasks.telemetry_utils as telem
|
|
19
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root
|
|
20
|
+
from higpertext.hooks.hook_tasks.hook_io import (
|
|
21
|
+
emit_block,
|
|
22
|
+
emit_context,
|
|
23
|
+
emit_continue,
|
|
24
|
+
hook_main,
|
|
25
|
+
read_json_file,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
import hashlib
|
|
29
|
+
import json
|
|
30
|
+
import sys
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
_SRC = Path(__file__).resolve().parents[3] # src/
|
|
34
|
+
if str(_SRC) not in sys.path:
|
|
35
|
+
sys.path.insert(0, str(_SRC))
|
|
36
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
37
|
+
from higpertext.kernel.app_config import IGNORE_TOOLS as _IGNORE_TOOLS
|
|
38
|
+
from higpertext.kernel.infrastructure.logger import render_box
|
|
39
|
+
|
|
40
|
+
MAX_IDENTICAL = 3
|
|
41
|
+
LARGE_OUTPUT_CHARS = 8_000
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _state_path(root: Path) -> Path:
|
|
45
|
+
return root / WORKSPACE_DIR_NAME / "state" / "loop_guard.json"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _load_state(root: Path) -> dict:
|
|
49
|
+
return read_json_file(_state_path(root))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _save_state(root: Path, state: dict) -> None:
|
|
53
|
+
try:
|
|
54
|
+
_state_path(root).write_text(
|
|
55
|
+
json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8"
|
|
56
|
+
)
|
|
57
|
+
except OSError: # nosec B110
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _tool_fingerprint(tool_name: str, tool_input: dict) -> str:
|
|
62
|
+
"""Hash determinista del par (herramienta, parámetros)."""
|
|
63
|
+
raw = json.dumps({"t": tool_name, "i": tool_input}, sort_keys=True, ensure_ascii=False)
|
|
64
|
+
return hashlib.sha1(raw.encode()).hexdigest()[:16] # nosec B324
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _session_id(root: Path) -> str:
|
|
68
|
+
try:
|
|
69
|
+
data = json.loads(
|
|
70
|
+
(root / WORKSPACE_DIR_NAME / "state" / "session.json").read_text(encoding="utf-8")
|
|
71
|
+
)
|
|
72
|
+
return data.get("session_id", "unknown")
|
|
73
|
+
except (OSError, json.JSONDecodeError):
|
|
74
|
+
return "unknown"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _handle_post_tool(root: Path, payload: dict) -> None:
|
|
78
|
+
tool_name = payload.get("tool_name", "")
|
|
79
|
+
tool_input = payload.get("tool_input", {}) or {}
|
|
80
|
+
tool_response = payload.get("tool_response", {}) or {}
|
|
81
|
+
|
|
82
|
+
if tool_name in _IGNORE_TOOLS:
|
|
83
|
+
emit_continue()
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
sid = _session_id(root)
|
|
87
|
+
state = _load_state(root)
|
|
88
|
+
fp = _tool_fingerprint(tool_name, tool_input)
|
|
89
|
+
|
|
90
|
+
# ── Check 1: Repetición de invocaciones idénticas ──────────────────────
|
|
91
|
+
last_fp = state.get("last_fp", "")
|
|
92
|
+
count = state.get("repeat_count", 0)
|
|
93
|
+
|
|
94
|
+
if fp == last_fp:
|
|
95
|
+
count += 1
|
|
96
|
+
else:
|
|
97
|
+
count = 1
|
|
98
|
+
|
|
99
|
+
state["last_fp"] = fp
|
|
100
|
+
state["repeat_count"] = count
|
|
101
|
+
state["last_tool"] = tool_name
|
|
102
|
+
_save_state(root, state)
|
|
103
|
+
|
|
104
|
+
if count >= MAX_IDENTICAL:
|
|
105
|
+
telem.record(
|
|
106
|
+
root,
|
|
107
|
+
"loop_guard_block",
|
|
108
|
+
session_id=sid,
|
|
109
|
+
tool=tool_name,
|
|
110
|
+
repeat_count=count,
|
|
111
|
+
fingerprint=fp,
|
|
112
|
+
)
|
|
113
|
+
state["repeat_count"] = 0
|
|
114
|
+
_save_state(root, state)
|
|
115
|
+
emit_block(
|
|
116
|
+
"PostToolUse",
|
|
117
|
+
render_box("HIGPERTEXT · Loop Guard · REPETICIÓN BLOQUEADA", [
|
|
118
|
+
f"│ Herramienta : {tool_name}",
|
|
119
|
+
f"│ Intentos : {count} invocaciones idénticas consecutivas",
|
|
120
|
+
"│",
|
|
121
|
+
"│ El agente está en bucle. DEBES hacer UNA de estas acciones:",
|
|
122
|
+
"│ (a) Cambiar el enfoque — usa una capacidad higpertext diferente",
|
|
123
|
+
"│ (b) Preguntar al usuario qué archivo/comando confirmar",
|
|
124
|
+
"│ (c) Escribir [LOOP DETECTED] y detener la tarea",
|
|
125
|
+
]),
|
|
126
|
+
)
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# ── Check 2: Saturación de contexto por output masivo ──────────────────
|
|
130
|
+
output_str = json.dumps(tool_response, ensure_ascii=False)
|
|
131
|
+
if len(output_str) > LARGE_OUTPUT_CHARS:
|
|
132
|
+
telem.record(
|
|
133
|
+
root,
|
|
134
|
+
"loop_guard_large_output",
|
|
135
|
+
session_id=sid,
|
|
136
|
+
tool=tool_name,
|
|
137
|
+
output_chars=len(output_str),
|
|
138
|
+
)
|
|
139
|
+
emit_context(
|
|
140
|
+
"PostToolUse",
|
|
141
|
+
render_box("HIGPERTEXT · Loop Guard · OUTPUT MASIVO DETECTADO", [
|
|
142
|
+
f"│ Herramienta : {tool_name}",
|
|
143
|
+
f"│ Tamaño : {len(output_str):,} caracteres",
|
|
144
|
+
"│",
|
|
145
|
+
"│ INSTRUCCIÓN ANTI-LOOP:",
|
|
146
|
+
"│ • NO re-emitas este contenido en tu respuesta.",
|
|
147
|
+
"│ • Referencia el archivo/resultado solo por nombre.",
|
|
148
|
+
"│ • Extrae ÚNICAMENTE la primera línea de error significativa.",
|
|
149
|
+
"│ • Si necesitas reprocesar este output, usa una capacidad",
|
|
150
|
+
"│ higpertext específica (common.grep-search, common.knowledge-asker).",
|
|
151
|
+
]),
|
|
152
|
+
)
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
emit_continue()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _handle_user_prompt(root: Path) -> None:
|
|
159
|
+
"""En cada nuevo prompt del usuario, reinicia el contador de repetición."""
|
|
160
|
+
state = _load_state(root)
|
|
161
|
+
if state.get("repeat_count", 0) > 0:
|
|
162
|
+
state["repeat_count"] = 0
|
|
163
|
+
state["last_fp"] = ""
|
|
164
|
+
_save_state(root, state)
|
|
165
|
+
emit_continue()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@hook_main
|
|
169
|
+
def main() -> None:
|
|
170
|
+
payload = json.load(sys.stdin)
|
|
171
|
+
root = get_project_root()
|
|
172
|
+
|
|
173
|
+
# El hook se registra para PostToolUse y UserPromptSubmit.
|
|
174
|
+
# Distinguimos por la presencia de tool_name.
|
|
175
|
+
if "tool_name" in payload:
|
|
176
|
+
_handle_post_tool(root, payload)
|
|
177
|
+
else:
|
|
178
|
+
_handle_user_prompt(root)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
main()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Hook PostToolUse — registra telemetría de tokens y actividad por tool call."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.hooks.hook_tasks._rules.context_rules import (
|
|
5
|
+
check_large_output,
|
|
6
|
+
check_window_pressure,
|
|
7
|
+
)
|
|
8
|
+
from higpertext.hooks.hook_tasks._rules.telemetry_rules import record_telemetry
|
|
9
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
15
|
+
_log = get_logger()
|
|
16
|
+
|
|
17
|
+
_SRC = Path(__file__).resolve().parents[3] # src/
|
|
18
|
+
if str(_SRC) not in sys.path:
|
|
19
|
+
sys.path.insert(0, str(_SRC))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main() -> None:
|
|
26
|
+
try:
|
|
27
|
+
payload = json.load(sys.stdin)
|
|
28
|
+
root = get_project_root()
|
|
29
|
+
record_telemetry(payload, root)
|
|
30
|
+
|
|
31
|
+
large = check_large_output(payload)
|
|
32
|
+
pressure = check_window_pressure(payload, root)
|
|
33
|
+
|
|
34
|
+
extra_parts = [r.message for r in (large, pressure) if r is not None]
|
|
35
|
+
if extra_parts:
|
|
36
|
+
print(
|
|
37
|
+
json.dumps(
|
|
38
|
+
{
|
|
39
|
+
"continue": True,
|
|
40
|
+
"hookSpecificOutput": {
|
|
41
|
+
"hookEventName": "PostToolUse",
|
|
42
|
+
"additionalContext": "\n".join(extra_parts),
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
print(json.dumps({"continue": True}))
|
|
49
|
+
except Exception as exc:
|
|
50
|
+
print(json.dumps({"continue": True, "error": str(exc)}))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Hook PreToolUse:Read — bloquea lecturas completas de archivos grandes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root
|
|
5
|
+
from higpertext.hooks.hook_tasks.hook_io import (
|
|
6
|
+
hook_main,
|
|
7
|
+
read_payload,
|
|
8
|
+
emit_continue,
|
|
9
|
+
emit_block,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
_SRC = Path(__file__).resolve().parents[3]
|
|
17
|
+
if str(_SRC) not in sys.path:
|
|
18
|
+
sys.path.insert(0, str(_SRC))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _threshold_bytes() -> int:
|
|
22
|
+
try:
|
|
23
|
+
return int(os.environ.get("HIGPERTEXT_READ_GUARD_BYTES", str(100 * 1024)))
|
|
24
|
+
except ValueError:
|
|
25
|
+
return 100 * 1024
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _extract_path(tool_input: dict) -> str:
|
|
29
|
+
return (
|
|
30
|
+
tool_input.get("filePath")
|
|
31
|
+
or tool_input.get("filepath")
|
|
32
|
+
or tool_input.get("path")
|
|
33
|
+
or tool_input.get("file")
|
|
34
|
+
or ""
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _has_range(tool_input: dict) -> bool:
|
|
39
|
+
return any(
|
|
40
|
+
key in tool_input and tool_input.get(key) not in (None, "") for key in ("offset", "limit")
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def evaluate_read_guard(tool_input: dict, root: Path) -> str:
|
|
45
|
+
"""Devuelve mensaje de bloqueo o cadena vacía si la lectura es segura."""
|
|
46
|
+
raw_path = _extract_path(tool_input)
|
|
47
|
+
if not raw_path or _has_range(tool_input):
|
|
48
|
+
return ""
|
|
49
|
+
path = Path(raw_path)
|
|
50
|
+
if not path.is_absolute():
|
|
51
|
+
path = root / path
|
|
52
|
+
if not path.exists() or not path.is_file():
|
|
53
|
+
return ""
|
|
54
|
+
threshold = _threshold_bytes()
|
|
55
|
+
size = path.stat().st_size
|
|
56
|
+
if size <= threshold:
|
|
57
|
+
return ""
|
|
58
|
+
display = raw_path
|
|
59
|
+
return "\n".join(
|
|
60
|
+
[
|
|
61
|
+
"╔─ HIGPERTEXT · Bloqueo de Read masivo ───────────────────",
|
|
62
|
+
f"│ Archivo : {display} ({size / 1024:.1f} KB)",
|
|
63
|
+
f"│ Límite : {threshold / 1024:.1f} KB",
|
|
64
|
+
"│ ⚠ Evita leer el archivo completo para no saturar contexto.",
|
|
65
|
+
"│ → Usa lectura inteligente:",
|
|
66
|
+
f"│ htx task common.smart-read --path {display} --mode auto",
|
|
67
|
+
"│ → O inspecciona firmas:",
|
|
68
|
+
f"│ htx task common.code-skeletonizer --path {display}",
|
|
69
|
+
"╚────────────────────────────────────────────────────────────",
|
|
70
|
+
]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@hook_main
|
|
75
|
+
def main() -> None:
|
|
76
|
+
payload = read_payload()
|
|
77
|
+
message = evaluate_read_guard(payload.get("tool_input", {}), get_project_root())
|
|
78
|
+
if message:
|
|
79
|
+
emit_block("PreToolUse", message)
|
|
80
|
+
return
|
|
81
|
+
emit_continue()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Hook común de seguridad para PreToolUse y PostToolUse."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
_SRC = Path(__file__).resolve().parents[3]
|
|
10
|
+
if str(_SRC) not in sys.path:
|
|
11
|
+
sys.path.insert(0, str(_SRC))
|
|
12
|
+
|
|
13
|
+
from higpertext.hooks.hook_tasks.hook_io import ( # noqa: E402
|
|
14
|
+
hook_main,
|
|
15
|
+
read_payload,
|
|
16
|
+
read_tool_command,
|
|
17
|
+
emit_block,
|
|
18
|
+
emit_context,
|
|
19
|
+
emit_continue,
|
|
20
|
+
)
|
|
21
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root # noqa: E402
|
|
22
|
+
from higpertext.hooks.hook_tasks._rules.security_rules import ( # noqa: E402
|
|
23
|
+
evaluate_command_guard,
|
|
24
|
+
evaluate_path_guard,
|
|
25
|
+
mask_tool_output,
|
|
26
|
+
)
|
|
27
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
28
|
+
|
|
29
|
+
_log = get_logger()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _tool_name(payload: dict) -> str:
|
|
33
|
+
return str(payload.get("tool_name") or payload.get("tool") or "")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _emit_masked_output(message: str, replacement_output: str) -> None:
|
|
37
|
+
print(
|
|
38
|
+
json.dumps(
|
|
39
|
+
{
|
|
40
|
+
"continue": True,
|
|
41
|
+
"hookSpecificOutput": {
|
|
42
|
+
"hookEventName": "PostToolUse",
|
|
43
|
+
"additionalContext": message,
|
|
44
|
+
"replacementOutput": replacement_output,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@hook_main
|
|
52
|
+
def main() -> None:
|
|
53
|
+
payload = read_payload()
|
|
54
|
+
event = payload.get("event", "PreToolUse")
|
|
55
|
+
tool_name = _tool_name(payload)
|
|
56
|
+
root = get_project_root()
|
|
57
|
+
|
|
58
|
+
if event == "PreToolUse":
|
|
59
|
+
if tool_name in {"Bash", "PowerShell"}:
|
|
60
|
+
result = evaluate_command_guard(read_tool_command(payload), root)
|
|
61
|
+
else:
|
|
62
|
+
result = evaluate_path_guard(tool_name, payload.get("tool_input", {}))
|
|
63
|
+
if result:
|
|
64
|
+
if result.severity == "warn":
|
|
65
|
+
emit_context("PreToolUse", result.message)
|
|
66
|
+
return
|
|
67
|
+
emit_block("PreToolUse", result.message)
|
|
68
|
+
return
|
|
69
|
+
emit_continue()
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if event == "PostToolUse":
|
|
73
|
+
result = mask_tool_output(payload.get("tool_response", {}), root)
|
|
74
|
+
if result:
|
|
75
|
+
_emit_masked_output(result.message, result.replacement_output)
|
|
76
|
+
return
|
|
77
|
+
emit_continue()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
main()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Hook UserPromptSubmit — inyecta estado de sesión y contexto de skills activas."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from higpertext.hooks.hook_tasks._rules.context_engine_rule import inject_context_pack
|
|
5
|
+
from higpertext.hooks.hook_tasks._rules.session_rules import (
|
|
6
|
+
auto_start_session,
|
|
7
|
+
reset_window_accumulator,
|
|
8
|
+
inject_session_status,
|
|
9
|
+
inject_skills_context,
|
|
10
|
+
handle_compact_command,
|
|
11
|
+
)
|
|
12
|
+
from higpertext.hooks.hook_tasks.hook_io import read_payload
|
|
13
|
+
from higpertext.hooks.hook_tasks.hook_utils import get_project_root
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
_SRC = Path(__file__).resolve().parents[3] # src/
|
|
19
|
+
if str(_SRC) not in sys.path:
|
|
20
|
+
sys.path.insert(0, str(_SRC))
|
|
21
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
22
|
+
|
|
23
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
24
|
+
_log = get_logger()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _emit_hook_context(message: str) -> None:
|
|
28
|
+
print(
|
|
29
|
+
json.dumps(
|
|
30
|
+
{
|
|
31
|
+
"continue": True,
|
|
32
|
+
"hookSpecificOutput": {
|
|
33
|
+
"hookEventName": "UserPromptSubmit",
|
|
34
|
+
"additionalContext": message,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _handle_slash_commands(root: Path, prompt: str) -> bool:
|
|
42
|
+
if prompt == "/compact" or prompt.startswith("/compact "):
|
|
43
|
+
_emit_hook_context(handle_compact_command(root).message)
|
|
44
|
+
return True
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _ensure_active_session(root: Path) -> None:
|
|
49
|
+
session_file = root / WORKSPACE_DIR_NAME / "state" / "session.json"
|
|
50
|
+
session = json.loads(session_file.read_text(encoding="utf-8")) if session_file.exists() else {}
|
|
51
|
+
if session.get("status") != "active":
|
|
52
|
+
auto_start_session(root)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _build_context(root: Path, prompt: str) -> str:
|
|
56
|
+
reset_window_accumulator(root)
|
|
57
|
+
parts = [inject_session_status(root).message]
|
|
58
|
+
ctx = inject_skills_context(root)
|
|
59
|
+
if ctx and ctx.message:
|
|
60
|
+
parts.append(ctx.message)
|
|
61
|
+
pack = inject_context_pack(root, prompt)
|
|
62
|
+
if pack and pack.message:
|
|
63
|
+
parts.append(pack.message)
|
|
64
|
+
return "\n".join(parts)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main() -> None:
|
|
68
|
+
try:
|
|
69
|
+
root = get_project_root()
|
|
70
|
+
payload = read_payload()
|
|
71
|
+
prompt = (payload.get("prompt", "") or payload.get("user_prompt", "") or "").strip().lower()
|
|
72
|
+
|
|
73
|
+
if _handle_slash_commands(root, prompt):
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
_ensure_active_session(root)
|
|
77
|
+
_emit_hook_context(_build_context(root, prompt))
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
print(json.dumps({"continue": True, "error": str(exc)}))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
main()
|