htmlgraph 0.9.3__py3-none-any.whl → 0.27.5__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.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +173 -17
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +127 -0
- htmlgraph/agent_registry.py +45 -30
- htmlgraph/agents.py +160 -107
- htmlgraph/analytics/__init__.py +9 -2
- htmlgraph/analytics/cli.py +190 -51
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +192 -100
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +190 -14
- htmlgraph/analytics_index.py +135 -51
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +208 -0
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/__init__.py +14 -0
- htmlgraph/builders/base.py +118 -29
- htmlgraph/builders/bug.py +150 -0
- htmlgraph/builders/chore.py +119 -0
- htmlgraph/builders/epic.py +150 -0
- htmlgraph/builders/feature.py +31 -6
- htmlgraph/builders/insight.py +195 -0
- htmlgraph/builders/metric.py +217 -0
- htmlgraph/builders/pattern.py +202 -0
- htmlgraph/builders/phase.py +162 -0
- htmlgraph/builders/spike.py +52 -19
- htmlgraph/builders/track.py +148 -72
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +18 -0
- htmlgraph/collections/base.py +415 -98
- htmlgraph/collections/bug.py +53 -0
- htmlgraph/collections/chore.py +53 -0
- htmlgraph/collections/epic.py +53 -0
- htmlgraph/collections/feature.py +12 -26
- htmlgraph/collections/insight.py +100 -0
- htmlgraph/collections/metric.py +92 -0
- htmlgraph/collections/pattern.py +97 -0
- htmlgraph/collections/phase.py +53 -0
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +56 -16
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +511 -0
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +344 -0
- htmlgraph/converter.py +216 -28
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2406 -307
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +19 -2
- htmlgraph/deploy.py +142 -125
- htmlgraph/deployment_models.py +474 -0
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +182 -27
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +100 -52
- htmlgraph/event_migration.py +13 -4
- htmlgraph/exceptions.py +49 -0
- htmlgraph/file_watcher.py +101 -28
- htmlgraph/find_api.py +75 -63
- htmlgraph/git_events.py +145 -63
- htmlgraph/graph.py +1122 -106
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +45 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +1314 -0
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/hooks-config.example.json +12 -0
- htmlgraph/hooks/installer.py +343 -0
- htmlgraph/hooks/orchestrator.py +674 -0
- htmlgraph/hooks/orchestrator_reflector.py +223 -0
- htmlgraph/hooks/post-checkout.sh +28 -0
- htmlgraph/hooks/post-commit.sh +24 -0
- htmlgraph/hooks/post-merge.sh +26 -0
- htmlgraph/hooks/post_tool_use_failure.py +273 -0
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +408 -0
- htmlgraph/hooks/pre-commit.sh +94 -0
- htmlgraph/hooks/pre-push.sh +28 -0
- htmlgraph/hooks/pretooluse.py +819 -0
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +255 -0
- htmlgraph/hooks/task_validator.py +177 -0
- htmlgraph/hooks/validator.py +628 -0
- htmlgraph/ids.py +41 -27
- htmlgraph/index.d.ts +286 -0
- htmlgraph/learning.py +767 -0
- htmlgraph/mcp_server.py +69 -23
- htmlgraph/models.py +1586 -87
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/orchestration/task_coordination.py +343 -0
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +669 -0
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +328 -0
- htmlgraph/orchestrator_validator.py +133 -0
- htmlgraph/parallel.py +646 -0
- htmlgraph/parser.py +160 -35
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/planning.py +147 -52
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +109 -72
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/routing.py +8 -19
- htmlgraph/scripts/deploy.py +1 -2
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +685 -180
- htmlgraph/services/__init__.py +10 -0
- htmlgraph/services/claiming.py +199 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +1392 -175
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +201 -0
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/setup.py +34 -17
- htmlgraph/spike_index.py +143 -0
- htmlgraph/sync_docs.py +12 -15
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph/templates/GEMINI.md.template +87 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +146 -15
- htmlgraph/track_manager.py +69 -21
- htmlgraph/transcript.py +890 -0
- htmlgraph/transcript_analytics.py +699 -0
- htmlgraph/types.py +323 -0
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +8 -5
- htmlgraph/work_type_utils.py +3 -2
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2406 -307
- htmlgraph-0.27.5.data/data/htmlgraph/templates/AGENTS.md.template +366 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/CLAUDE.md.template +97 -0
- htmlgraph-0.27.5.data/data/htmlgraph/templates/GEMINI.md.template +87 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +97 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -2688
- htmlgraph/sdk.py +0 -709
- htmlgraph-0.9.3.dist-info/RECORD +0 -61
- {htmlgraph-0.9.3.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.9.3.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SubagentStop Hook - Update parent events when subagents complete.
|
|
3
|
+
|
|
4
|
+
This module handles the SubagentStop hook event, which fires when a subagent
|
|
5
|
+
(spawned via Task()) completes. It updates the parent event with completion
|
|
6
|
+
status and counts child spikes created during the subagent's execution.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
- Reads HTMLGRAPH_PARENT_EVENT from environment (set by PreToolUse hook)
|
|
10
|
+
- Queries database for spikes created since parent event start
|
|
11
|
+
- Updates parent event: status="completed", child_spike_count=N
|
|
12
|
+
- Handles graceful degradation if parent event not found
|
|
13
|
+
|
|
14
|
+
Parent-Child Event Nesting:
|
|
15
|
+
- Parent: evt-abc (Task delegation) created by PreToolUse
|
|
16
|
+
- Child events: spikes created by subagent during task execution
|
|
17
|
+
- Result: Full trace of delegation work visible in dashboard
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import sqlite3
|
|
24
|
+
import sys
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_parent_event_id() -> str | None:
|
|
33
|
+
"""
|
|
34
|
+
Get the parent event ID from environment.
|
|
35
|
+
|
|
36
|
+
Set by PreToolUse hook when Task() is detected.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Parent event ID (evt-XXXXX) or None if not found
|
|
40
|
+
"""
|
|
41
|
+
return os.environ.get("HTMLGRAPH_PARENT_EVENT")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_session_id() -> str | None:
|
|
45
|
+
"""
|
|
46
|
+
Get the current session ID from environment.
|
|
47
|
+
|
|
48
|
+
Set by SessionStart hook.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Session ID or None if not found
|
|
52
|
+
"""
|
|
53
|
+
return os.environ.get("HTMLGRAPH_SESSION_ID")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def count_child_spikes(
|
|
57
|
+
db_path: str, parent_event_id: str, parent_start_time: str
|
|
58
|
+
) -> int:
|
|
59
|
+
"""
|
|
60
|
+
Count spikes created after the parent event started.
|
|
61
|
+
|
|
62
|
+
Queries the features table for spikes with created_at > parent start time.
|
|
63
|
+
Uses a narrow time window (5 minutes) to avoid counting unrelated spikes
|
|
64
|
+
from other sessions.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
db_path: Path to SQLite database
|
|
68
|
+
parent_event_id: Parent event ID
|
|
69
|
+
parent_start_time: ISO8601 timestamp when parent event started
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Count of child spikes (0 if none found)
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
conn = sqlite3.connect(db_path)
|
|
76
|
+
cursor = conn.cursor()
|
|
77
|
+
|
|
78
|
+
# Validate parent start time format (ISO8601)
|
|
79
|
+
try:
|
|
80
|
+
datetime.fromisoformat(parent_start_time)
|
|
81
|
+
except (ValueError, TypeError):
|
|
82
|
+
# If parsing fails, return 0 (couldn't validate time window)
|
|
83
|
+
logger.warning(f"Could not parse parent start time: {parent_start_time}")
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
# Query spikes created within 5 minutes after parent event
|
|
87
|
+
# This avoids counting unrelated spikes from other sessions
|
|
88
|
+
query = """
|
|
89
|
+
SELECT COUNT(*) FROM features
|
|
90
|
+
WHERE type = 'spike'
|
|
91
|
+
AND created_at >= ?
|
|
92
|
+
AND created_at <= datetime(?, '+5 minutes')
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
cursor.execute(query, (parent_start_time, parent_start_time))
|
|
96
|
+
result = cursor.fetchone()
|
|
97
|
+
count = result[0] if result else 0
|
|
98
|
+
|
|
99
|
+
conn.close()
|
|
100
|
+
logger.debug(f"Found {count} child spikes for parent event {parent_event_id}")
|
|
101
|
+
return count
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.warning(f"Error counting child spikes: {e}")
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def update_parent_event(
|
|
109
|
+
db_path: str,
|
|
110
|
+
parent_event_id: str,
|
|
111
|
+
child_spike_count: int,
|
|
112
|
+
completion_time: str | None = None,
|
|
113
|
+
) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Update parent event with completion status and child spike count.
|
|
116
|
+
|
|
117
|
+
Updates agent_events table:
|
|
118
|
+
- status: "started" → "completed"
|
|
119
|
+
- child_spike_count: Count of spikes created by subagent
|
|
120
|
+
- output_summary: JSON with completion info
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
db_path: Path to SQLite database
|
|
124
|
+
parent_event_id: Parent event ID to update
|
|
125
|
+
child_spike_count: Number of child spikes created
|
|
126
|
+
completion_time: ISO8601 timestamp (optional, defaults to now)
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
True if update successful, False otherwise
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
if completion_time is None:
|
|
133
|
+
completion_time = datetime.now(timezone.utc).isoformat()
|
|
134
|
+
|
|
135
|
+
conn = sqlite3.connect(db_path)
|
|
136
|
+
cursor = conn.cursor()
|
|
137
|
+
|
|
138
|
+
# Build output summary
|
|
139
|
+
output_summary = json.dumps(
|
|
140
|
+
{
|
|
141
|
+
"status": "completed",
|
|
142
|
+
"child_spike_count": child_spike_count,
|
|
143
|
+
"completion_time": completion_time,
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Update parent event
|
|
148
|
+
query = """
|
|
149
|
+
UPDATE agent_events
|
|
150
|
+
SET status = ?, child_spike_count = ?, output_summary = ?, updated_at = CURRENT_TIMESTAMP
|
|
151
|
+
WHERE event_id = ?
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
cursor.execute(
|
|
155
|
+
query,
|
|
156
|
+
("completed", child_spike_count, output_summary, parent_event_id),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if cursor.rowcount == 0:
|
|
160
|
+
logger.warning(f"Parent event not found: {parent_event_id}")
|
|
161
|
+
conn.close()
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
conn.commit()
|
|
165
|
+
conn.close()
|
|
166
|
+
|
|
167
|
+
logger.info(
|
|
168
|
+
f"Updated parent event {parent_event_id}: "
|
|
169
|
+
f"status=completed, child_spike_count={child_spike_count}"
|
|
170
|
+
)
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.warning(f"Error updating parent event: {e}")
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_parent_event_start_time(db_path: str, parent_event_id: str) -> str | None:
|
|
179
|
+
"""
|
|
180
|
+
Get the start time of the parent event.
|
|
181
|
+
|
|
182
|
+
Used to set the time window for counting child spikes.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
db_path: Path to SQLite database
|
|
186
|
+
parent_event_id: Parent event ID
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
ISO8601 timestamp or None if not found
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
conn = sqlite3.connect(db_path)
|
|
193
|
+
cursor = conn.cursor()
|
|
194
|
+
|
|
195
|
+
query = "SELECT timestamp FROM agent_events WHERE event_id = ?"
|
|
196
|
+
cursor.execute(query, (parent_event_id,))
|
|
197
|
+
result = cursor.fetchone()
|
|
198
|
+
|
|
199
|
+
conn.close()
|
|
200
|
+
return result[0] if result else None
|
|
201
|
+
|
|
202
|
+
except Exception as e:
|
|
203
|
+
logger.warning(f"Error getting parent event start time: {e}")
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_parent_event_from_db(db_path: str) -> str | None:
|
|
208
|
+
"""
|
|
209
|
+
Query database for the most recent task_delegation event.
|
|
210
|
+
|
|
211
|
+
Used when HTMLGRAPH_PARENT_EVENT environment variable is not available
|
|
212
|
+
(due to inter-process communication limitations).
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
db_path: Path to SQLite database
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Parent event ID (evt-XXXXX) or None if not found
|
|
219
|
+
"""
|
|
220
|
+
try:
|
|
221
|
+
conn = sqlite3.connect(db_path)
|
|
222
|
+
cursor = conn.cursor()
|
|
223
|
+
|
|
224
|
+
# Query for the most recent task_delegation with status='started'
|
|
225
|
+
# This is the task that spawned the current subagent
|
|
226
|
+
query = """
|
|
227
|
+
SELECT event_id FROM agent_events
|
|
228
|
+
WHERE event_type = 'task_delegation'
|
|
229
|
+
AND status = 'started'
|
|
230
|
+
ORDER BY timestamp DESC
|
|
231
|
+
LIMIT 1
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
cursor.execute(query)
|
|
235
|
+
result = cursor.fetchone()
|
|
236
|
+
conn.close()
|
|
237
|
+
|
|
238
|
+
if result:
|
|
239
|
+
parent_event_id: str = result[0]
|
|
240
|
+
logger.debug(
|
|
241
|
+
f"Found parent task_delegation from database: {parent_event_id}"
|
|
242
|
+
)
|
|
243
|
+
return parent_event_id
|
|
244
|
+
|
|
245
|
+
logger.debug("No active task_delegation found in database")
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.warning(f"Error querying for parent event: {e}")
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def handle_subagent_stop(hook_input: dict[str, Any]) -> dict[str, Any]:
|
|
254
|
+
"""
|
|
255
|
+
Handle SubagentStop hook event.
|
|
256
|
+
|
|
257
|
+
When a subagent completes, updates the parent event with:
|
|
258
|
+
1. Completion status
|
|
259
|
+
2. Count of spikes created during subagent execution
|
|
260
|
+
3. Completion timestamp
|
|
261
|
+
|
|
262
|
+
This closes the parent-child event trace and enables dashboard visualization
|
|
263
|
+
of the complete delegation hierarchy.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
hook_input: Hook input data from Claude Code
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Response: {"continue": True} with optional context
|
|
270
|
+
"""
|
|
271
|
+
# Try to get parent event ID from environment (set by PreToolUse hook)
|
|
272
|
+
parent_event_id = get_parent_event_id()
|
|
273
|
+
|
|
274
|
+
# If not available in environment, query database
|
|
275
|
+
# (environment variables may not be inherited across subagent process boundary)
|
|
276
|
+
# Get project directory and database path (reuse for both env and db lookup)
|
|
277
|
+
db_path = None
|
|
278
|
+
try:
|
|
279
|
+
from htmlgraph.config import get_database_path
|
|
280
|
+
|
|
281
|
+
cwd = hook_input.get("cwd", os.getcwd())
|
|
282
|
+
db_path = str(get_database_path(cwd))
|
|
283
|
+
|
|
284
|
+
if not Path(db_path).exists():
|
|
285
|
+
logger.warning(f"Database not found: {db_path}")
|
|
286
|
+
return {"continue": True}
|
|
287
|
+
|
|
288
|
+
except Exception as e:
|
|
289
|
+
logger.warning(f"Error resolving database path: {e}")
|
|
290
|
+
return {"continue": True}
|
|
291
|
+
|
|
292
|
+
# If parent event ID not in environment, query database
|
|
293
|
+
if not parent_event_id:
|
|
294
|
+
logger.debug("Parent event ID not in environment, querying database...")
|
|
295
|
+
try:
|
|
296
|
+
parent_event_id = get_parent_event_from_db(db_path)
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.debug(f"Could not query database for parent event: {e}")
|
|
299
|
+
|
|
300
|
+
if not parent_event_id:
|
|
301
|
+
logger.debug(
|
|
302
|
+
"No parent event ID found (env or db), skipping subagent stop tracking"
|
|
303
|
+
)
|
|
304
|
+
return {"continue": True}
|
|
305
|
+
|
|
306
|
+
# Get parent event start time
|
|
307
|
+
parent_start_time = get_parent_event_start_time(db_path, parent_event_id)
|
|
308
|
+
if not parent_start_time:
|
|
309
|
+
logger.warning(f"Could not find parent event: {parent_event_id}")
|
|
310
|
+
return {"continue": True}
|
|
311
|
+
|
|
312
|
+
# Count child spikes
|
|
313
|
+
child_spike_count = count_child_spikes(db_path, parent_event_id, parent_start_time)
|
|
314
|
+
|
|
315
|
+
# Update parent event with completion info
|
|
316
|
+
completion_time = datetime.now(timezone.utc).isoformat()
|
|
317
|
+
success = update_parent_event(
|
|
318
|
+
db_path,
|
|
319
|
+
parent_event_id,
|
|
320
|
+
child_spike_count,
|
|
321
|
+
completion_time,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if success:
|
|
325
|
+
# Clear parent event from environment
|
|
326
|
+
os.environ.pop("HTMLGRAPH_PARENT_EVENT", None)
|
|
327
|
+
os.environ.pop("HTMLGRAPH_SUBAGENT_TYPE", None)
|
|
328
|
+
|
|
329
|
+
logger.info(
|
|
330
|
+
f"Subagent stop recorded: parent_event={parent_event_id}, "
|
|
331
|
+
f"child_spikes={child_spike_count}"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
"continue": True,
|
|
336
|
+
"hookSpecificOutput": {
|
|
337
|
+
"hookEventName": "SubagentStop",
|
|
338
|
+
"additionalContext": (
|
|
339
|
+
f"Task delegation completed: {child_spike_count} spike(s) created"
|
|
340
|
+
),
|
|
341
|
+
},
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return {"continue": True}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def main() -> None:
|
|
348
|
+
"""Hook entry point for script wrapper."""
|
|
349
|
+
# Check if tracking is disabled
|
|
350
|
+
if os.environ.get("HTMLGRAPH_DISABLE_TRACKING") == "1":
|
|
351
|
+
print(json.dumps({"continue": True}))
|
|
352
|
+
sys.exit(0)
|
|
353
|
+
|
|
354
|
+
# Read hook input from stdin
|
|
355
|
+
try:
|
|
356
|
+
hook_input = json.load(sys.stdin)
|
|
357
|
+
except json.JSONDecodeError:
|
|
358
|
+
hook_input = {}
|
|
359
|
+
|
|
360
|
+
# Handle subagent stop
|
|
361
|
+
result = handle_subagent_stop(hook_input)
|
|
362
|
+
|
|
363
|
+
# Output response
|
|
364
|
+
print(json.dumps(result))
|
|
365
|
+
sys.exit(0)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
if __name__ == "__main__":
|
|
369
|
+
main()
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task Enforcer - Auto-inject HtmlGraph save instructions into Task prompts.
|
|
3
|
+
|
|
4
|
+
This module provides PreToolUse enforcement for the Task tool, ensuring that
|
|
5
|
+
subagent prompts include instructions to save their findings to HtmlGraph.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
- Detects Task tool calls in PreToolUse hook
|
|
9
|
+
- Checks if prompt already includes save instructions
|
|
10
|
+
- Auto-injects SDK save template if missing
|
|
11
|
+
- Returns updatedInput with modified prompt
|
|
12
|
+
- Tracks parent session context and nesting depth (Phase 2)
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
from htmlgraph.hooks.task_enforcer import enforce_task_saving
|
|
16
|
+
|
|
17
|
+
result = enforce_task_saving(tool_name, tool_params)
|
|
18
|
+
# Returns: {"continue": True, "hookSpecificOutput": {"updatedInput": {...}}}
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def has_save_instructions(prompt: str) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Check if prompt already includes HtmlGraph save instructions.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
prompt: Task prompt to check
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True if save instructions are present, False otherwise
|
|
34
|
+
"""
|
|
35
|
+
# Keywords that indicate save instructions are already present
|
|
36
|
+
save_keywords = [
|
|
37
|
+
"sdk",
|
|
38
|
+
"htmlgraph",
|
|
39
|
+
".save()",
|
|
40
|
+
"spike",
|
|
41
|
+
"report results",
|
|
42
|
+
"save findings",
|
|
43
|
+
"track results",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
prompt_lower = prompt.lower()
|
|
47
|
+
return any(keyword in prompt_lower for keyword in save_keywords)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def inject_save_instructions(prompt: str, subagent_type: str = "haiku") -> str:
|
|
51
|
+
"""
|
|
52
|
+
Inject HtmlGraph save instructions into Task prompt.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
prompt: Original task prompt
|
|
56
|
+
subagent_type: Type of subagent (default: "haiku")
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Modified prompt with save instructions appended
|
|
60
|
+
"""
|
|
61
|
+
# Template to inject
|
|
62
|
+
save_template = f"""
|
|
63
|
+
|
|
64
|
+
🔴 CRITICAL - Report Results to HtmlGraph:
|
|
65
|
+
After completing your research/analysis, you MUST save your findings using:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from htmlgraph import SDK
|
|
69
|
+
sdk = SDK(agent='{subagent_type}')
|
|
70
|
+
spike = sdk.spikes.create('Your Task Summary Here')
|
|
71
|
+
spike.set_findings(\'\'\'
|
|
72
|
+
# Your Comprehensive Findings
|
|
73
|
+
|
|
74
|
+
## Summary
|
|
75
|
+
[Brief overview of what you discovered]
|
|
76
|
+
|
|
77
|
+
## Key Findings
|
|
78
|
+
- [Finding 1]
|
|
79
|
+
- [Finding 2]
|
|
80
|
+
- [Finding 3]
|
|
81
|
+
|
|
82
|
+
## Details
|
|
83
|
+
[Detailed analysis, code examples, etc.]
|
|
84
|
+
|
|
85
|
+
## Recommendations
|
|
86
|
+
[Action items or next steps]
|
|
87
|
+
\'\'\')
|
|
88
|
+
spike.save()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
IMPORTANT:
|
|
92
|
+
- Replace 'Your Task Summary Here' with a concise description of your task
|
|
93
|
+
- Include ALL relevant findings in the set_findings() call
|
|
94
|
+
- This creates a permanent record that can be referenced later
|
|
95
|
+
- The spike will be saved to .htmlgraph/spikes/ directory
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
return prompt + save_template
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def enforce_task_saving(tool_name: str, tool_params: dict[str, Any]) -> dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Enforce HtmlGraph result saving for Task tool calls.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
tool_name: Name of the tool being called
|
|
107
|
+
tool_params: Tool parameters (includes "prompt" for Task)
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Hook response with updatedInput if modifications needed:
|
|
111
|
+
{
|
|
112
|
+
"continue": True,
|
|
113
|
+
"hookSpecificOutput": {
|
|
114
|
+
"hookEventName": "PreToolUse",
|
|
115
|
+
"updatedInput": {...}, # Modified tool_params
|
|
116
|
+
"additionalContext": "..." # Optional guidance
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
"""
|
|
120
|
+
# Only process Task tool calls
|
|
121
|
+
if tool_name != "Task":
|
|
122
|
+
return {"continue": True}
|
|
123
|
+
|
|
124
|
+
# Get prompt from tool params
|
|
125
|
+
prompt = tool_params.get("prompt", "")
|
|
126
|
+
if not prompt:
|
|
127
|
+
return {"continue": True}
|
|
128
|
+
|
|
129
|
+
# Phase 2: Track parent session context and increment nesting depth
|
|
130
|
+
parent_session = os.environ.get("HTMLGRAPH_PARENT_SESSION")
|
|
131
|
+
parent_agent = os.environ.get("HTMLGRAPH_PARENT_AGENT", "claude-code")
|
|
132
|
+
nesting_depth = int(os.environ.get("HTMLGRAPH_NESTING_DEPTH", "0"))
|
|
133
|
+
current_session = os.environ.get("HTMLGRAPH_SESSION_ID", "")
|
|
134
|
+
|
|
135
|
+
# Record delegation event in database
|
|
136
|
+
try:
|
|
137
|
+
import uuid
|
|
138
|
+
|
|
139
|
+
from htmlgraph.db.schema import HtmlGraphDB
|
|
140
|
+
|
|
141
|
+
db = HtmlGraphDB()
|
|
142
|
+
db.connect()
|
|
143
|
+
|
|
144
|
+
# Extract to_agent from subagent_type (e.g., "haiku" -> "haiku", "gemini-spawner" -> "gemini-spawner")
|
|
145
|
+
to_agent = tool_params.get("subagent_type", "unknown")
|
|
146
|
+
task_description = tool_params.get("description", "Unnamed task")
|
|
147
|
+
|
|
148
|
+
# Determine session ID, using current > parent > auto-generated
|
|
149
|
+
session_id = (
|
|
150
|
+
current_session or parent_session or f"session-{uuid.uuid4().hex[:8]}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Ensure session exists before recording delegation (handles FK constraints)
|
|
154
|
+
db._ensure_session_exists(session_id, parent_agent)
|
|
155
|
+
|
|
156
|
+
# Record the delegation event
|
|
157
|
+
db.record_delegation_event(
|
|
158
|
+
from_agent=parent_agent,
|
|
159
|
+
to_agent=to_agent,
|
|
160
|
+
task_description=task_description,
|
|
161
|
+
session_id=session_id,
|
|
162
|
+
context={
|
|
163
|
+
"nesting_depth": nesting_depth,
|
|
164
|
+
"prompt_preview": prompt[:200] if prompt else "",
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
db.close()
|
|
169
|
+
except Exception:
|
|
170
|
+
# Graceful degradation - continue even if delegation tracking fails
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
# Track Task invocation as activity (if parent session exists)
|
|
174
|
+
task_activity_id = None
|
|
175
|
+
if parent_session:
|
|
176
|
+
try:
|
|
177
|
+
from htmlgraph import SDK
|
|
178
|
+
|
|
179
|
+
sdk = SDK(agent=parent_agent, parent_session=parent_session)
|
|
180
|
+
|
|
181
|
+
# Track Task invocation
|
|
182
|
+
entry = sdk.track_activity(
|
|
183
|
+
tool="Task",
|
|
184
|
+
summary=f"Task invoked: {tool_params.get('description', 'Unnamed task')[:100]}",
|
|
185
|
+
payload={
|
|
186
|
+
"subagent_type": tool_params.get("subagent_type"),
|
|
187
|
+
"description": tool_params.get("description"),
|
|
188
|
+
"prompt_preview": prompt[:200] if prompt else "",
|
|
189
|
+
"nesting_depth": nesting_depth,
|
|
190
|
+
},
|
|
191
|
+
success=True,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if entry:
|
|
195
|
+
task_activity_id = entry.id
|
|
196
|
+
|
|
197
|
+
except Exception:
|
|
198
|
+
# Graceful degradation - continue even if tracking fails
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
# Increment nesting depth for child
|
|
202
|
+
new_depth = nesting_depth + 1
|
|
203
|
+
|
|
204
|
+
# Set parent activity and increment depth in environment
|
|
205
|
+
if task_activity_id:
|
|
206
|
+
os.environ["HTMLGRAPH_PARENT_ACTIVITY"] = task_activity_id
|
|
207
|
+
|
|
208
|
+
os.environ["HTMLGRAPH_NESTING_DEPTH"] = str(new_depth)
|
|
209
|
+
|
|
210
|
+
# Warn about runaway recursion
|
|
211
|
+
warning = ""
|
|
212
|
+
if new_depth > 3:
|
|
213
|
+
warning = f"\n⚠️ Warning: Nesting depth exceeds 3 levels (depth={new_depth}). Consider flattening task hierarchy."
|
|
214
|
+
|
|
215
|
+
# Check if save instructions already present
|
|
216
|
+
if has_save_instructions(prompt):
|
|
217
|
+
# Even if save instructions exist, we still need to update environment
|
|
218
|
+
return {"continue": True}
|
|
219
|
+
|
|
220
|
+
# Detect subagent type from prompt context
|
|
221
|
+
prompt_lower = prompt.lower()
|
|
222
|
+
if "haiku" in prompt_lower:
|
|
223
|
+
subagent_type = "haiku"
|
|
224
|
+
elif "sonnet" in prompt_lower:
|
|
225
|
+
subagent_type = "sonnet"
|
|
226
|
+
elif "opus" in prompt_lower:
|
|
227
|
+
subagent_type = "opus"
|
|
228
|
+
else:
|
|
229
|
+
subagent_type = "haiku" # Default to haiku for most subagents
|
|
230
|
+
|
|
231
|
+
# Inject save instructions
|
|
232
|
+
modified_prompt = inject_save_instructions(prompt, subagent_type)
|
|
233
|
+
|
|
234
|
+
# Create updated tool params
|
|
235
|
+
updated_params = tool_params.copy()
|
|
236
|
+
updated_params["prompt"] = modified_prompt
|
|
237
|
+
|
|
238
|
+
# Build context message
|
|
239
|
+
context_msg = (
|
|
240
|
+
f"📝 Auto-injected HtmlGraph save instructions into Task prompt. "
|
|
241
|
+
f"Subagent will be reminded to save findings using SDK.spikes. "
|
|
242
|
+
f"(depth={new_depth}, parent={parent_session[:12] if parent_session else 'none'})"
|
|
243
|
+
)
|
|
244
|
+
if warning:
|
|
245
|
+
context_msg += warning
|
|
246
|
+
|
|
247
|
+
# Return response with updatedInput
|
|
248
|
+
return {
|
|
249
|
+
"continue": True,
|
|
250
|
+
"hookSpecificOutput": {
|
|
251
|
+
"hookEventName": "PreToolUse",
|
|
252
|
+
"updatedInput": updated_params,
|
|
253
|
+
"additionalContext": context_msg,
|
|
254
|
+
},
|
|
255
|
+
}
|