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,303 @@
|
|
|
1
|
+
"""Context Engine Infrastructure layer — GraphIndex, PackStore, skeletonizer, and TelemetryReader."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import ast
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
9
|
+
from higpertext.kernel.domain.context_engine import SymbolRef, ContextPack
|
|
10
|
+
|
|
11
|
+
# Patrones del formato generado por SemanticGraphGenerator.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _parse_file_line(line: str) -> str | None:
|
|
15
|
+
prefix = "### 📂 ["
|
|
16
|
+
if not line.startswith(prefix):
|
|
17
|
+
return None
|
|
18
|
+
end = line.find("]", len(prefix))
|
|
19
|
+
return line[len(prefix):end] if end != -1 else None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _parse_doc_suffix(line: str) -> str:
|
|
23
|
+
marker = "— *"
|
|
24
|
+
start = line.find(marker)
|
|
25
|
+
if start == -1:
|
|
26
|
+
return ""
|
|
27
|
+
doc_start = start + len(marker)
|
|
28
|
+
doc_end = line.find("*", doc_start)
|
|
29
|
+
return line[doc_start:doc_end].strip() if doc_end != -1 else ""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_symbol_line(line: str, prefix: str, kind: str) -> tuple[str, str, str] | None:
|
|
33
|
+
stripped = line.strip()
|
|
34
|
+
if not stripped.startswith(prefix):
|
|
35
|
+
return None
|
|
36
|
+
name_start = len(prefix)
|
|
37
|
+
name_end = stripped.find("`", name_start)
|
|
38
|
+
if name_end == -1:
|
|
39
|
+
return None
|
|
40
|
+
signature = stripped[name_start:name_end]
|
|
41
|
+
name = signature.split("(", 1)[0].strip()
|
|
42
|
+
return (name, kind, _parse_doc_suffix(stripped)) if name.isidentifier() else None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class GraphIndex:
|
|
46
|
+
"""Lee el semantic graph y expone búsqueda de símbolos por keyword."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, project_root: Path) -> None:
|
|
49
|
+
self._json_path = project_root / WORKSPACE_DIR_NAME / "state" / "semantic_graph.json"
|
|
50
|
+
self._md_path = project_root / WORKSPACE_DIR_NAME / "state" / "semantic_graph.md"
|
|
51
|
+
self._symbols: list[SymbolRef] = []
|
|
52
|
+
self._parse()
|
|
53
|
+
|
|
54
|
+
def _parse(self) -> None:
|
|
55
|
+
if self._json_path.exists():
|
|
56
|
+
self._parse_json()
|
|
57
|
+
return
|
|
58
|
+
self._parse_markdown()
|
|
59
|
+
|
|
60
|
+
def _parse_json(self) -> None:
|
|
61
|
+
raw = json.loads(self._json_path.read_text(encoding="utf-8"))
|
|
62
|
+
if "symbols" in raw:
|
|
63
|
+
self._parse_symbols_json(raw["symbols"])
|
|
64
|
+
return
|
|
65
|
+
self._parse_files_json(raw.get("files", {}))
|
|
66
|
+
|
|
67
|
+
def _parse_symbols_json(self, symbols: list[dict]) -> None:
|
|
68
|
+
for symbol in symbols:
|
|
69
|
+
self._symbols.append(
|
|
70
|
+
SymbolRef(
|
|
71
|
+
file=symbol.get("file", ""),
|
|
72
|
+
name=symbol.get("name", ""),
|
|
73
|
+
kind=symbol.get("type", "symbol"),
|
|
74
|
+
doc=symbol.get("docstring", ""),
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def _parse_files_json(self, files: dict) -> None:
|
|
79
|
+
for file_path, data in files.items():
|
|
80
|
+
for cls in data.get("classes", []):
|
|
81
|
+
self._symbols.append(
|
|
82
|
+
SymbolRef(
|
|
83
|
+
file=file_path,
|
|
84
|
+
name=cls.get("name", ""),
|
|
85
|
+
kind="class",
|
|
86
|
+
doc=cls.get("docstring", ""),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
for method in cls.get("methods", []):
|
|
90
|
+
self._symbols.append(
|
|
91
|
+
SymbolRef(
|
|
92
|
+
file=file_path,
|
|
93
|
+
name=method.get("name", ""),
|
|
94
|
+
kind="method",
|
|
95
|
+
doc=method.get("docstring", ""),
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
for func in data.get("functions", []):
|
|
99
|
+
self._symbols.append(
|
|
100
|
+
SymbolRef(
|
|
101
|
+
file=file_path,
|
|
102
|
+
name=func.get("name", ""),
|
|
103
|
+
kind="function",
|
|
104
|
+
doc=func.get("docstring", ""),
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def _parse_markdown(self) -> None:
|
|
109
|
+
if not self._md_path.exists():
|
|
110
|
+
return
|
|
111
|
+
current_file = ""
|
|
112
|
+
for line in self._md_path.read_text(encoding="utf-8").splitlines():
|
|
113
|
+
parsed_file = _parse_file_line(line)
|
|
114
|
+
if parsed_file:
|
|
115
|
+
current_file = parsed_file
|
|
116
|
+
continue
|
|
117
|
+
if not current_file:
|
|
118
|
+
continue
|
|
119
|
+
parsed = _parse_symbol_line(line, "- `def ", "method")
|
|
120
|
+
if parsed and line[:1].isspace():
|
|
121
|
+
name, kind, doc = parsed
|
|
122
|
+
self._symbols.append(SymbolRef(file=current_file, name=name, kind=kind, doc=doc))
|
|
123
|
+
continue
|
|
124
|
+
parsed = _parse_symbol_line(line, "- **`class ", "class")
|
|
125
|
+
if parsed:
|
|
126
|
+
name, kind, doc = parsed
|
|
127
|
+
self._symbols.append(SymbolRef(file=current_file, name=name, kind=kind, doc=doc))
|
|
128
|
+
continue
|
|
129
|
+
parsed = _parse_symbol_line(line, "- `def ", "function")
|
|
130
|
+
if parsed:
|
|
131
|
+
name, kind, doc = parsed
|
|
132
|
+
self._symbols.append(SymbolRef(file=current_file, name=name, kind=kind, doc=doc))
|
|
133
|
+
|
|
134
|
+
def all_symbols(self) -> list[SymbolRef]:
|
|
135
|
+
return list(self._symbols)
|
|
136
|
+
|
|
137
|
+
def search(self, keywords: list[str]) -> list[SymbolRef]:
|
|
138
|
+
"""Devuelve símbolos cuyo nombre, archivo o doc contiene algún keyword."""
|
|
139
|
+
if not keywords:
|
|
140
|
+
return []
|
|
141
|
+
kws = [k.lower() for k in keywords]
|
|
142
|
+
hits: list[SymbolRef] = []
|
|
143
|
+
for s in self._symbols:
|
|
144
|
+
haystack = f"{s.name} {s.file} {s.doc}".lower()
|
|
145
|
+
if any(k in haystack for k in kws):
|
|
146
|
+
hits.append(s)
|
|
147
|
+
return hits
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class PackStore:
|
|
151
|
+
"""Guarda y referencia context packs como artefactos Markdown en disco."""
|
|
152
|
+
|
|
153
|
+
def __init__(self, project_root: Path) -> None:
|
|
154
|
+
self._dir = project_root / WORKSPACE_DIR_NAME / "state" / "context_packs"
|
|
155
|
+
|
|
156
|
+
def save(self, pack: ContextPack) -> Path:
|
|
157
|
+
self._dir.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
task_id = self._slug(pack.intent.goal)
|
|
159
|
+
path = self._dir / f"{task_id}.md"
|
|
160
|
+
path.write_text(pack.to_markdown(), encoding="utf-8")
|
|
161
|
+
return path
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _slug(goal: str) -> str:
|
|
165
|
+
slug = re.sub(r"[^a-z0-9]+", "-", goal.lower()).strip("-")
|
|
166
|
+
return slug[:50] or "context-pack"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# Umbral a partir del cual un archivo se considera 'grande' y candidato a esqueletización.
|
|
170
|
+
_SKELETON_THRESHOLD_CHARS = 3000
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def should_skeletonize(source: str) -> bool:
|
|
174
|
+
"""Indica si un archivo supera el umbral para ser esqueletizado."""
|
|
175
|
+
return len(source) > _SKELETON_THRESHOLD_CHARS
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def extract_skeleton(source: str) -> str:
|
|
179
|
+
"""Extrae firmas de clases y funciones de un fragmento de código Python."""
|
|
180
|
+
try:
|
|
181
|
+
return _extract_via_ast(source)
|
|
182
|
+
except SyntaxError:
|
|
183
|
+
return _extract_via_regex(source)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _extract_via_ast(source: str) -> str:
|
|
187
|
+
"""Extrae firmas usando el módulo ast estándar de Python."""
|
|
188
|
+
tree = ast.parse(source)
|
|
189
|
+
lines: list[str] = []
|
|
190
|
+
for node in ast.walk(tree):
|
|
191
|
+
if isinstance(node, ast.ClassDef):
|
|
192
|
+
bases = ", ".join(ast.unparse(b) for b in node.bases)
|
|
193
|
+
header = f"class {node.name}({bases}):" if bases else f"class {node.name}:"
|
|
194
|
+
lines.append(header)
|
|
195
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
196
|
+
prefix = "async def" if isinstance(node, ast.AsyncFunctionDef) else "def"
|
|
197
|
+
args = ast.unparse(node.args)
|
|
198
|
+
ret = f" -> {ast.unparse(node.returns)}" if node.returns else ""
|
|
199
|
+
lines.append(f" {prefix} {node.name}({args}){ret}: ...")
|
|
200
|
+
return "\n".join(lines)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _extract_via_regex(source: str) -> str:
|
|
204
|
+
"""Fallback lineal: extrae líneas que definen clases o funciones sin regex."""
|
|
205
|
+
signatures: list[str] = []
|
|
206
|
+
for line in source.splitlines():
|
|
207
|
+
stripped = line.strip()
|
|
208
|
+
if not stripped.endswith(":"):
|
|
209
|
+
continue
|
|
210
|
+
if _is_signature_line(stripped):
|
|
211
|
+
signatures.append(f"{stripped} ...")
|
|
212
|
+
return "\n".join(signatures)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _is_signature_line(line: str) -> bool:
|
|
216
|
+
if line.startswith("class "):
|
|
217
|
+
name = line.removeprefix("class ").split("(", 1)[0].removesuffix(":").strip()
|
|
218
|
+
return name.isidentifier()
|
|
219
|
+
if line.startswith("async def "):
|
|
220
|
+
name = line.removeprefix("async def ").split("(", 1)[0].strip()
|
|
221
|
+
return _has_balanced_signature_tail(line) and name.isidentifier()
|
|
222
|
+
if line.startswith("def "):
|
|
223
|
+
name = line.removeprefix("def ").split("(", 1)[0].strip()
|
|
224
|
+
return _has_balanced_signature_tail(line) and name.isidentifier()
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _has_balanced_signature_tail(line: str) -> bool:
|
|
229
|
+
open_index = line.find("(")
|
|
230
|
+
close_index = line.rfind(")")
|
|
231
|
+
colon_index = line.rfind(":")
|
|
232
|
+
return open_index != -1 and open_index < close_index < colon_index
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class TelemetryReader:
|
|
236
|
+
"""Lee telemetry.jsonl y expone queries por sesión."""
|
|
237
|
+
|
|
238
|
+
def __init__(self, project_root: Path) -> None:
|
|
239
|
+
self._path = project_root / WORKSPACE_DIR_NAME / "state" / "telemetry.jsonl"
|
|
240
|
+
|
|
241
|
+
def _all_events(self) -> list[dict]:
|
|
242
|
+
if not self._path.exists():
|
|
243
|
+
return []
|
|
244
|
+
events = []
|
|
245
|
+
for line in self._path.read_text(encoding="utf-8").splitlines():
|
|
246
|
+
line = line.strip()
|
|
247
|
+
if not line:
|
|
248
|
+
continue
|
|
249
|
+
try:
|
|
250
|
+
events.append(json.loads(line))
|
|
251
|
+
except json.JSONDecodeError:
|
|
252
|
+
continue
|
|
253
|
+
return events
|
|
254
|
+
|
|
255
|
+
def events_for_session(self, session_id: str) -> list[dict]:
|
|
256
|
+
return [e for e in self._all_events() if e.get("session_id") == session_id]
|
|
257
|
+
|
|
258
|
+
def total_tokens(self, session_id: str) -> int:
|
|
259
|
+
return sum(
|
|
260
|
+
e.get("estimated_tokens", 0)
|
|
261
|
+
for e in self.events_for_session(session_id)
|
|
262
|
+
if e.get("event") == "tool_call"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def total_cost(self, session_id: str) -> float:
|
|
266
|
+
return sum(
|
|
267
|
+
e.get("estimated_cost_usd", 0.0)
|
|
268
|
+
for e in self.events_for_session(session_id)
|
|
269
|
+
if e.get("event") == "tool_call"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def exploration_reads(self, session_id: str) -> int:
|
|
273
|
+
"""Lecturas de archivo sin higpertext_related=True (exploración ciega)."""
|
|
274
|
+
return sum(
|
|
275
|
+
1
|
|
276
|
+
for e in self.events_for_session(session_id)
|
|
277
|
+
if e.get("event") == "activity"
|
|
278
|
+
and e.get("op_type") == "file-read"
|
|
279
|
+
and not e.get("higpertext_related", False)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def higpertext_reads(self, session_id: str) -> int:
|
|
283
|
+
"""Lecturas de archivo con higpertext_related=True (uso correcto)."""
|
|
284
|
+
return sum(
|
|
285
|
+
1
|
|
286
|
+
for e in self.events_for_session(session_id)
|
|
287
|
+
if e.get("event") == "activity"
|
|
288
|
+
and e.get("op_type") == "file-read"
|
|
289
|
+
and e.get("higpertext_related", False)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
def hook_intercepts(self, session_id: str) -> int:
|
|
293
|
+
return sum(
|
|
294
|
+
1 for e in self.events_for_session(session_id) if e.get("event") == "hook_intercept"
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
def files_read(self, session_id: str) -> set[str]:
|
|
298
|
+
"""Conjunto de archivos leídos en la sesión (targets de activity)."""
|
|
299
|
+
return {
|
|
300
|
+
e["target"]
|
|
301
|
+
for e in self.events_for_session(session_id)
|
|
302
|
+
if e.get("event") == "activity" and e.get("target")
|
|
303
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""LocalVectorStore - Base de datos vectorial persistida en archivo JSON."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
import math
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from higpertext.kernel.domain.rag import IVectorStore, VectorEntry, DocumentChunk
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LocalVectorStore(IVectorStore):
|
|
11
|
+
"""Almacén vectorial en JSON local con búsqueda por similitud coseno."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, project_root: Path) -> None:
|
|
14
|
+
self.project_root = project_root
|
|
15
|
+
self.store_file = project_root / ".higpertext" / "state" / "vector_store.json"
|
|
16
|
+
self.entries: list[VectorEntry] = []
|
|
17
|
+
|
|
18
|
+
def add_entries(self, entries: list[VectorEntry]) -> None:
|
|
19
|
+
self.entries.extend(entries)
|
|
20
|
+
|
|
21
|
+
def search(
|
|
22
|
+
self, query_vector: list[float], limit: int = 5
|
|
23
|
+
) -> list[tuple[DocumentChunk, float]]:
|
|
24
|
+
if not self.entries or not query_vector:
|
|
25
|
+
return []
|
|
26
|
+
|
|
27
|
+
results: list[tuple[DocumentChunk, float]] = []
|
|
28
|
+
for entry in self.entries:
|
|
29
|
+
score = self._cosine_similarity(query_vector, entry.embedding)
|
|
30
|
+
results.append((entry.chunk, score))
|
|
31
|
+
|
|
32
|
+
# Ordenar por score de similitud descendente
|
|
33
|
+
results.sort(key=lambda x: x[1], reverse=True)
|
|
34
|
+
return results[:limit]
|
|
35
|
+
|
|
36
|
+
def clear(self) -> None:
|
|
37
|
+
self.entries = []
|
|
38
|
+
|
|
39
|
+
def save(self) -> None:
|
|
40
|
+
self.store_file.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
serializable_data = []
|
|
42
|
+
for entry in self.entries:
|
|
43
|
+
serializable_data.append(
|
|
44
|
+
{
|
|
45
|
+
"chunk": {
|
|
46
|
+
"file": entry.chunk.file,
|
|
47
|
+
"content": entry.chunk.content,
|
|
48
|
+
"chunk_type": entry.chunk.chunk_type,
|
|
49
|
+
"start_line": entry.chunk.start_line,
|
|
50
|
+
"end_line": entry.chunk.end_line,
|
|
51
|
+
"metadata": entry.chunk.metadata,
|
|
52
|
+
},
|
|
53
|
+
"embedding": entry.embedding,
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
tmp_file = self.store_file.with_suffix(".tmp")
|
|
58
|
+
tmp_file.write_text(
|
|
59
|
+
json.dumps(serializable_data, indent=2, ensure_ascii=False),
|
|
60
|
+
encoding="utf-8",
|
|
61
|
+
)
|
|
62
|
+
tmp_file.replace(self.store_file)
|
|
63
|
+
|
|
64
|
+
def load(self) -> None:
|
|
65
|
+
if not self.store_file.exists():
|
|
66
|
+
self.entries = []
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
data = json.loads(self.store_file.read_text(encoding="utf-8"))
|
|
71
|
+
self.entries = []
|
|
72
|
+
for item in data:
|
|
73
|
+
c = item["chunk"]
|
|
74
|
+
chunk = DocumentChunk(
|
|
75
|
+
file=c["file"],
|
|
76
|
+
content=c["content"],
|
|
77
|
+
chunk_type=c["chunk_type"],
|
|
78
|
+
start_line=c["start_line"],
|
|
79
|
+
end_line=c["end_line"],
|
|
80
|
+
metadata=c.get("metadata", {}),
|
|
81
|
+
)
|
|
82
|
+
self.entries.append(VectorEntry(chunk=chunk, embedding=item["embedding"]))
|
|
83
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
84
|
+
self.entries = []
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _cosine_similarity(v1: list[float], v2: list[float]) -> float:
|
|
88
|
+
"""Calcula la similitud coseno entre dos vectores."""
|
|
89
|
+
if len(v1) != len(v2) or not v1:
|
|
90
|
+
return 0.0
|
|
91
|
+
|
|
92
|
+
dot_product = sum(a * b for a, b in zip(v1, v2))
|
|
93
|
+
norm_a = math.sqrt(sum(a * a for a in v1))
|
|
94
|
+
norm_b = math.sqrt(sum(b * b for b in v2))
|
|
95
|
+
|
|
96
|
+
if norm_a == 0.0 or norm_b == 0.0:
|
|
97
|
+
return 0.0
|
|
98
|
+
|
|
99
|
+
return dot_product / (norm_a * norm_b)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Infrastructure deployment init
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""ResourceDeployer — despliega y restaura skills, subagentes y playbooks (Infraestructura)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from higpertext.kernel.infrastructure.presentation.markdown_renderer import (
|
|
7
|
+
MarkdownRenderer,
|
|
8
|
+
)
|
|
9
|
+
from higpertext.kernel.pkg_resources import resolve_resource
|
|
10
|
+
|
|
11
|
+
from higpertext.kernel.infrastructure.logger import get_logger
|
|
12
|
+
from higpertext.kernel.app_config import (
|
|
13
|
+
PLAYBOOKS as _PLAYBOOKS,
|
|
14
|
+
AGENT_JSON as _AGENT_JSON,
|
|
15
|
+
NO_SKILLS_MSG as _NO_SKILLS_MSG,
|
|
16
|
+
NO_SUBAGENTS_MSG as _NO_SUBAGENTS_MSG,
|
|
17
|
+
ANTIGRAVITY_AGENT_TEMPLATE as _AGT,
|
|
18
|
+
)
|
|
19
|
+
_log = get_logger()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResourceDeployer:
|
|
23
|
+
"""Responsabilidad única: copiar/eliminar recursos efímeros de sesión."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, project_root: Path, root_dir: Path) -> None:
|
|
26
|
+
self.project_root = project_root
|
|
27
|
+
self.root_dir = root_dir
|
|
28
|
+
|
|
29
|
+
def _resolve_template_dirs(self, subdir: str) -> list[Path]:
|
|
30
|
+
from higpertext.kernel.config_paths import WORKSPACE_DIR_NAME
|
|
31
|
+
dirs = []
|
|
32
|
+
agent_templates = self.project_root / WORKSPACE_DIR_NAME / "agent_templates" / subdir
|
|
33
|
+
if agent_templates.exists():
|
|
34
|
+
dirs.append(agent_templates)
|
|
35
|
+
dirs.append(resolve_resource(self.root_dir, "src", "higpertext", "templates", subdir))
|
|
36
|
+
return dirs
|
|
37
|
+
|
|
38
|
+
def deploy_skills(self, skills: list, skills_dest: Path) -> None:
|
|
39
|
+
if not skills:
|
|
40
|
+
return
|
|
41
|
+
skills_dirs = self._resolve_template_dirs("skills")
|
|
42
|
+
skills_dest.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
# Construir índice de rglob de todos los directorios ordenados por prioridad
|
|
45
|
+
index = {}
|
|
46
|
+
for d in reversed(skills_dirs):
|
|
47
|
+
if d.exists():
|
|
48
|
+
for f in d.rglob("*.json"):
|
|
49
|
+
index[f.parent.name] = f
|
|
50
|
+
# También buscar archivos Markdown directos
|
|
51
|
+
for f in d.rglob("*.md"):
|
|
52
|
+
index[f.stem] = f
|
|
53
|
+
|
|
54
|
+
for name in skills:
|
|
55
|
+
src = index.get(name)
|
|
56
|
+
if not (src and src.exists()):
|
|
57
|
+
continue
|
|
58
|
+
try:
|
|
59
|
+
dest = skills_dest / name
|
|
60
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
if src.suffix == ".json":
|
|
62
|
+
data = json.loads(src.read_text(encoding="utf-8"))
|
|
63
|
+
(dest / "SKILL.md").write_text(data.get("content", ""), encoding="utf-8")
|
|
64
|
+
else:
|
|
65
|
+
# Si es Markdown directo (.md)
|
|
66
|
+
(dest / "SKILL.md").write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
|
|
67
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
68
|
+
_log.warning(f"[!] Error generando skill '{name}': {e}")
|
|
69
|
+
|
|
70
|
+
def deploy_subagents(self, subagents: list, subagents_dest: Path, skills_str: str = "") -> None:
|
|
71
|
+
if not subagents:
|
|
72
|
+
return
|
|
73
|
+
subagents_dirs = self._resolve_template_dirs("subagents")
|
|
74
|
+
subagents_dest.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
|
|
76
|
+
index = {}
|
|
77
|
+
for d in reversed(subagents_dirs):
|
|
78
|
+
if d.exists():
|
|
79
|
+
for f in d.rglob("*.json"):
|
|
80
|
+
index[f.stem] = f
|
|
81
|
+
for f in d.rglob("*.md"):
|
|
82
|
+
index[f.stem] = f
|
|
83
|
+
|
|
84
|
+
is_antigravity = subagents_dest.name == "agents"
|
|
85
|
+
for name in subagents:
|
|
86
|
+
src = index.get(name)
|
|
87
|
+
if not (src and src.exists()):
|
|
88
|
+
continue
|
|
89
|
+
try:
|
|
90
|
+
if src.suffix == ".json":
|
|
91
|
+
self._process_subagent_file(src, subagents_dest, name, is_antigravity, skills_str)
|
|
92
|
+
else:
|
|
93
|
+
# Si es Markdown directo (.md)
|
|
94
|
+
if is_antigravity:
|
|
95
|
+
# Generar el JSON mock de Antigravity
|
|
96
|
+
mock_data = {
|
|
97
|
+
"description": f"Subagent {name}",
|
|
98
|
+
"system_prompt": src.read_text(encoding="utf-8")
|
|
99
|
+
}
|
|
100
|
+
self._generate_antigravity_agent_json(subagents_dest, name, mock_data)
|
|
101
|
+
else:
|
|
102
|
+
(subagents_dest / f"{name}.md").write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
|
|
103
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
104
|
+
_log.warning(f"[!] Error generando subagente '{name}': {e}")
|
|
105
|
+
|
|
106
|
+
def _process_subagent_file(
|
|
107
|
+
self, src: Path, subagents_dest: Path, name: str, is_antigravity: bool, skills_str: str
|
|
108
|
+
) -> None:
|
|
109
|
+
data = json.loads(src.read_text(encoding="utf-8"))
|
|
110
|
+
if is_antigravity:
|
|
111
|
+
self._generate_antigravity_agent_json(subagents_dest, name, data)
|
|
112
|
+
else:
|
|
113
|
+
md = MarkdownRenderer.generate_subagent_md(data, skills_str)
|
|
114
|
+
(subagents_dest / f"{name}.md").write_text(md, encoding="utf-8")
|
|
115
|
+
|
|
116
|
+
def _generate_antigravity_agent_json(
|
|
117
|
+
self, subagents_dest: Path, name: str, data: dict
|
|
118
|
+
) -> None:
|
|
119
|
+
agent_dir = subagents_dest / name
|
|
120
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
system_prompt = self._build_antigravity_system_prompt(data)
|
|
122
|
+
agent_json = {
|
|
123
|
+
"name": name,
|
|
124
|
+
"description": data.get("description", ""),
|
|
125
|
+
"hidden": _AGT["hidden"],
|
|
126
|
+
"config": {
|
|
127
|
+
"customAgent": {
|
|
128
|
+
"systemPromptSections": [
|
|
129
|
+
{
|
|
130
|
+
"title": _AGT["system_prompt_section_title"],
|
|
131
|
+
"content": system_prompt.strip(),
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"toolNames": _AGT["tool_names"],
|
|
135
|
+
"systemPromptConfig": _AGT["system_prompt_config"],
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
(agent_dir / _AGENT_JSON).write_text(
|
|
140
|
+
json.dumps(agent_json, indent=2, ensure_ascii=False),
|
|
141
|
+
encoding="utf-8",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def _build_antigravity_system_prompt(data: dict) -> str:
|
|
146
|
+
system_prompt = f"{data.get('description', '')}\n\n"
|
|
147
|
+
for entry in _AGT["system_prompt_keys"]:
|
|
148
|
+
key, title = entry["key"], entry["title"]
|
|
149
|
+
if key in data:
|
|
150
|
+
system_prompt += f"{title}\n" + "\n".join(f"- {x}" for x in data[key]) + "\n\n"
|
|
151
|
+
return system_prompt
|
|
152
|
+
|
|
153
|
+
def deploy_playbooks(
|
|
154
|
+
self,
|
|
155
|
+
workflows_dest: Path,
|
|
156
|
+
skills_str: str,
|
|
157
|
+
subagents_str: str,
|
|
158
|
+
session_id: str,
|
|
159
|
+
assistant: str = "gemini",
|
|
160
|
+
get_model_fn=None,
|
|
161
|
+
get_profile_fn=None,
|
|
162
|
+
) -> None:
|
|
163
|
+
tmpl_dir = resolve_resource(self.root_dir, "src", "higpertext", "templates", "workflows")
|
|
164
|
+
workflows_dest.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
session_block = (
|
|
166
|
+
f"\n# Session Info\n- **Session ID**: `{session_id}`\n- **Status**: `active`"
|
|
167
|
+
)
|
|
168
|
+
for name in _PLAYBOOKS:
|
|
169
|
+
src = tmpl_dir / f"{name}.json"
|
|
170
|
+
if not src.exists():
|
|
171
|
+
continue
|
|
172
|
+
try:
|
|
173
|
+
data = json.loads(src.read_text(encoding="utf-8"))
|
|
174
|
+
profile_model = self._resolve_model(data, assistant, get_model_fn, get_profile_fn)
|
|
175
|
+
content = MarkdownRenderer.generate_workflow_base_md(data, assistant, profile_model)
|
|
176
|
+
content = content.replace("{required_skills_str}", skills_str)
|
|
177
|
+
content = content.replace("{related_subagents_str}", subagents_str)
|
|
178
|
+
content += session_block
|
|
179
|
+
(workflows_dest / f"{name}.md").write_text(content, encoding="utf-8")
|
|
180
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
181
|
+
_log.warning(f"[!] Error compilando playbook '{name}': {e}")
|
|
182
|
+
|
|
183
|
+
def restore_playbooks(
|
|
184
|
+
self,
|
|
185
|
+
workflows_dest: Path,
|
|
186
|
+
assistant: str = "gemini",
|
|
187
|
+
get_model_fn=None,
|
|
188
|
+
get_profile_fn=None,
|
|
189
|
+
) -> None:
|
|
190
|
+
tmpl_dir = resolve_resource(self.root_dir, "src", "higpertext", "templates", "workflows")
|
|
191
|
+
if not (tmpl_dir.exists() and workflows_dest.exists()):
|
|
192
|
+
return
|
|
193
|
+
for name in _PLAYBOOKS:
|
|
194
|
+
src = tmpl_dir / f"{name}.json"
|
|
195
|
+
dest = workflows_dest / f"{name}.md"
|
|
196
|
+
if not src.exists():
|
|
197
|
+
continue
|
|
198
|
+
try:
|
|
199
|
+
data = json.loads(src.read_text(encoding="utf-8"))
|
|
200
|
+
profile_model = self._resolve_model(data, assistant, get_model_fn, get_profile_fn)
|
|
201
|
+
content = MarkdownRenderer.generate_workflow_base_md(data, assistant, profile_model)
|
|
202
|
+
content = content.replace("{required_skills_str}", _NO_SKILLS_MSG)
|
|
203
|
+
content = content.replace("{related_subagents_str}", _NO_SUBAGENTS_MSG)
|
|
204
|
+
dest.write_text(content, encoding="utf-8")
|
|
205
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
206
|
+
_log.warning(f"[!] Error restaurando playbook '{name}': {e}")
|
|
207
|
+
|
|
208
|
+
def build_resource_links(
|
|
209
|
+
self,
|
|
210
|
+
skills: list,
|
|
211
|
+
subagents: list,
|
|
212
|
+
skills_dest: Path,
|
|
213
|
+
subagents_dest: Path,
|
|
214
|
+
) -> tuple[str, str]:
|
|
215
|
+
tmpl_skills = resolve_resource(self.root_dir, "src", "higpertext", "templates", "skills")
|
|
216
|
+
tmpl_subagents = resolve_resource(
|
|
217
|
+
self.root_dir, "src", "higpertext", "templates", "subagents"
|
|
218
|
+
)
|
|
219
|
+
skill_index = (
|
|
220
|
+
{f.parent.name for f in tmpl_skills.rglob("*.json")} if tmpl_skills.exists() else set()
|
|
221
|
+
)
|
|
222
|
+
agent_index = (
|
|
223
|
+
{f.stem for f in tmpl_subagents.rglob("*.json")} if tmpl_subagents.exists() else set()
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def _skill_link(name: str) -> str:
|
|
227
|
+
d = skills_dest / name
|
|
228
|
+
try:
|
|
229
|
+
rel = d.relative_to(self.project_root).as_posix()
|
|
230
|
+
return f"- [{name}](file:///{rel}/SKILL.md)"
|
|
231
|
+
except ValueError:
|
|
232
|
+
return f"- `{name}`"
|
|
233
|
+
|
|
234
|
+
def _subagent_link(name: str) -> str:
|
|
235
|
+
is_antigravity = subagents_dest.name == "agents"
|
|
236
|
+
f = (
|
|
237
|
+
subagents_dest / name / _AGENT_JSON
|
|
238
|
+
if is_antigravity
|
|
239
|
+
else subagents_dest / f"{name}.md"
|
|
240
|
+
)
|
|
241
|
+
try:
|
|
242
|
+
rel = f.relative_to(self.project_root).as_posix()
|
|
243
|
+
return f"- [{name}](file:///{rel})"
|
|
244
|
+
except ValueError:
|
|
245
|
+
return f"- `{name}`"
|
|
246
|
+
|
|
247
|
+
skills_lines = [
|
|
248
|
+
(_skill_link(s) if (s in skill_index or (skills_dest / s).exists()) else f"- `{s}`")
|
|
249
|
+
for s in sorted(skills)
|
|
250
|
+
]
|
|
251
|
+
sub_lines = [
|
|
252
|
+
(
|
|
253
|
+
_subagent_link(sa)
|
|
254
|
+
if (
|
|
255
|
+
sa in agent_index
|
|
256
|
+
or (subagents_dest / f"{sa}.md").exists()
|
|
257
|
+
or (subagents_dest / sa / _AGENT_JSON).exists()
|
|
258
|
+
)
|
|
259
|
+
else f"- `{sa}`"
|
|
260
|
+
)
|
|
261
|
+
for sa in sorted(subagents)
|
|
262
|
+
]
|
|
263
|
+
skills_str = "\n".join(skills_lines) if skills_lines else "- `00-quick-reference`"
|
|
264
|
+
subs_str = "\n".join(sub_lines) if sub_lines else "- `research`"
|
|
265
|
+
return skills_str, subs_str
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def _resolve_model(data: dict, assistant: str, get_model_fn, get_profile_fn) -> str:
|
|
269
|
+
if get_profile_fn:
|
|
270
|
+
try:
|
|
271
|
+
profile_data = get_profile_fn()
|
|
272
|
+
if profile_data and "model" in profile_data:
|
|
273
|
+
return profile_data["model"]
|
|
274
|
+
except Exception: # nosec B110
|
|
275
|
+
pass
|
|
276
|
+
if get_model_fn:
|
|
277
|
+
try:
|
|
278
|
+
model = get_model_fn(assistant)
|
|
279
|
+
if model:
|
|
280
|
+
return model
|
|
281
|
+
except Exception: # nosec B110
|
|
282
|
+
pass
|
|
283
|
+
return data.get("model", "")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Infrastructure diagnostics init
|