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,637 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt Analysis Module for HtmlGraph Hooks.
|
|
3
|
+
|
|
4
|
+
Centralizes prompt classification and workflow guidance logic used across
|
|
5
|
+
multiple hooks (UserPromptSubmit, PreToolUse, etc.).
|
|
6
|
+
|
|
7
|
+
This module provides:
|
|
8
|
+
- Intent classification (implementation, testing, refactoring, etc.)
|
|
9
|
+
- CIGS (Computational Imperative Guidance System) violation detection
|
|
10
|
+
- Active work item tracking
|
|
11
|
+
- Workflow guidance generation
|
|
12
|
+
- UserQuery event creation for parent-child linking
|
|
13
|
+
|
|
14
|
+
The module is designed to be reusable across different hook implementations,
|
|
15
|
+
with graceful degradation if dependencies are unavailable.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
import re
|
|
20
|
+
import uuid
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from htmlgraph.hooks.context import HookContext
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Patterns that indicate implementation intent
|
|
30
|
+
IMPLEMENTATION_PATTERNS = [
|
|
31
|
+
r"\b(implement|add|create|build|write|develop|make)\b.*\b(feature|function|method|class|component|endpoint|api)\b",
|
|
32
|
+
r"\b(fix|resolve|patch|repair)\b.*\b(bug|issue|error|problem)\b",
|
|
33
|
+
r"\b(refactor|rewrite|restructure|reorganize)\b",
|
|
34
|
+
r"\b(update|modify|change|edit)\b.*\b(code|file|function|class)\b",
|
|
35
|
+
r"\bcan you\b.*\b(add|implement|create|fix|change)\b",
|
|
36
|
+
r"\bplease\b.*\b(add|implement|create|fix|change)\b",
|
|
37
|
+
r"\bI need\b.*\b(feature|function|fix|change)\b",
|
|
38
|
+
r"\blet'?s\b.*\b(implement|add|create|build|fix)\b",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# Patterns that indicate investigation/research
|
|
42
|
+
INVESTIGATION_PATTERNS = [
|
|
43
|
+
r"\b(investigate|research|explore|analyze|understand|find out|look into)\b",
|
|
44
|
+
r"\b(why|how come|what causes)\b.*\b(not working|broken|failing|error)\b",
|
|
45
|
+
r"\b(where|which|what)\b.*\b(file|code|function|class)\b.*\b(handle|process|do)\b",
|
|
46
|
+
r"\bcan you\b.*\b(find|search|look for|check)\b",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# Patterns that indicate bug/issue
|
|
50
|
+
BUG_PATTERNS = [
|
|
51
|
+
r"\b(bug|issue|error|problem|broken|not working|fails|crash)\b",
|
|
52
|
+
r"\b(something'?s? wrong|doesn'?t work|isn'?t working)\b",
|
|
53
|
+
r"\bCI\b.*\b(fail|error|broken)\b",
|
|
54
|
+
r"\btest.*\b(fail|error|broken)\b",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Patterns for continuation
|
|
58
|
+
CONTINUATION_PATTERNS = [
|
|
59
|
+
r"^(continue|resume|proceed|go on|keep going|next)\b",
|
|
60
|
+
r"\b(where we left off|from before|last time)\b",
|
|
61
|
+
r"^(ok|okay|yes|sure|do it|go ahead)\b",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# CIGS: Patterns for delegation-critical operations
|
|
65
|
+
EXPLORATION_KEYWORDS = [
|
|
66
|
+
"search",
|
|
67
|
+
"find",
|
|
68
|
+
"what files",
|
|
69
|
+
"which files",
|
|
70
|
+
"where is",
|
|
71
|
+
"locate",
|
|
72
|
+
"analyze",
|
|
73
|
+
"examine",
|
|
74
|
+
"inspect",
|
|
75
|
+
"review",
|
|
76
|
+
"check",
|
|
77
|
+
"look at",
|
|
78
|
+
"show me",
|
|
79
|
+
"list",
|
|
80
|
+
"grep",
|
|
81
|
+
"read",
|
|
82
|
+
"scan",
|
|
83
|
+
"explore",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
CODE_CHANGE_KEYWORDS = [
|
|
87
|
+
"implement",
|
|
88
|
+
"fix",
|
|
89
|
+
"update",
|
|
90
|
+
"refactor",
|
|
91
|
+
"change",
|
|
92
|
+
"modify",
|
|
93
|
+
"edit",
|
|
94
|
+
"write",
|
|
95
|
+
"create file",
|
|
96
|
+
"add code",
|
|
97
|
+
"remove code",
|
|
98
|
+
"replace",
|
|
99
|
+
"rewrite",
|
|
100
|
+
"patch",
|
|
101
|
+
"add",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
GIT_KEYWORDS = [
|
|
105
|
+
"commit",
|
|
106
|
+
"push",
|
|
107
|
+
"pull",
|
|
108
|
+
"merge",
|
|
109
|
+
"branch",
|
|
110
|
+
"checkout",
|
|
111
|
+
"git add",
|
|
112
|
+
"git commit",
|
|
113
|
+
"git push",
|
|
114
|
+
"git status",
|
|
115
|
+
"git diff",
|
|
116
|
+
"rebase",
|
|
117
|
+
"cherry-pick",
|
|
118
|
+
"stash",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def classify_prompt(prompt: str) -> dict[str, Any]:
|
|
123
|
+
"""
|
|
124
|
+
Classify the user's prompt intent.
|
|
125
|
+
|
|
126
|
+
Analyzes the prompt text to determine the primary intent using pattern matching.
|
|
127
|
+
Returns classification results with confidence scores.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
prompt: User's prompt text
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
dict with:
|
|
134
|
+
- is_implementation: bool - Implementation/coding request
|
|
135
|
+
- is_investigation: bool - Research/exploration request
|
|
136
|
+
- is_bug_report: bool - Bug/issue report
|
|
137
|
+
- is_continuation: bool - Continuation of previous work
|
|
138
|
+
- confidence: float - Overall confidence (0.0-1.0)
|
|
139
|
+
- matched_patterns: list - Patterns that matched
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> result = classify_prompt("Can you implement a new feature?")
|
|
143
|
+
>>> result['is_implementation']
|
|
144
|
+
True
|
|
145
|
+
"""
|
|
146
|
+
prompt_lower = prompt.lower().strip()
|
|
147
|
+
|
|
148
|
+
result: dict[str, Any] = {
|
|
149
|
+
"is_implementation": False,
|
|
150
|
+
"is_investigation": False,
|
|
151
|
+
"is_bug_report": False,
|
|
152
|
+
"is_continuation": False,
|
|
153
|
+
"confidence": 0.0,
|
|
154
|
+
"matched_patterns": [],
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Check for continuation first (short prompts like "ok", "continue")
|
|
158
|
+
for pattern in CONTINUATION_PATTERNS:
|
|
159
|
+
if re.search(pattern, prompt_lower):
|
|
160
|
+
result["is_continuation"] = True
|
|
161
|
+
result["confidence"] = 0.9
|
|
162
|
+
result["matched_patterns"].append(f"continuation: {pattern}")
|
|
163
|
+
return result
|
|
164
|
+
|
|
165
|
+
# Check for implementation patterns
|
|
166
|
+
for pattern in IMPLEMENTATION_PATTERNS:
|
|
167
|
+
if re.search(pattern, prompt_lower):
|
|
168
|
+
result["is_implementation"] = True
|
|
169
|
+
result["confidence"] = max(result["confidence"], 0.8)
|
|
170
|
+
result["matched_patterns"].append(f"implementation: {pattern}")
|
|
171
|
+
|
|
172
|
+
# Check for investigation patterns
|
|
173
|
+
for pattern in INVESTIGATION_PATTERNS:
|
|
174
|
+
if re.search(pattern, prompt_lower):
|
|
175
|
+
result["is_investigation"] = True
|
|
176
|
+
result["confidence"] = max(result["confidence"], 0.7)
|
|
177
|
+
result["matched_patterns"].append(f"investigation: {pattern}")
|
|
178
|
+
|
|
179
|
+
# Check for bug patterns
|
|
180
|
+
for pattern in BUG_PATTERNS:
|
|
181
|
+
if re.search(pattern, prompt_lower):
|
|
182
|
+
result["is_bug_report"] = True
|
|
183
|
+
result["confidence"] = max(result["confidence"], 0.75)
|
|
184
|
+
result["matched_patterns"].append(f"bug: {pattern}")
|
|
185
|
+
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def classify_cigs_intent(prompt: str) -> dict[str, Any]:
|
|
190
|
+
"""
|
|
191
|
+
Classify prompt for CIGS delegation guidance.
|
|
192
|
+
|
|
193
|
+
Analyzes the prompt to detect patterns that indicate exploration, code changes,
|
|
194
|
+
or git operations. Used to generate pre-response delegation imperatives.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
prompt: User's prompt text
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
dict with:
|
|
201
|
+
- involves_exploration: bool - Exploration/search activity
|
|
202
|
+
- involves_code_changes: bool - Code modification activity
|
|
203
|
+
- involves_git: bool - Git operation activity
|
|
204
|
+
- intent_confidence: float - Overall confidence (0.0-1.0)
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
>>> result = classify_cigs_intent("Search for all error handling code")
|
|
208
|
+
>>> result['involves_exploration']
|
|
209
|
+
True
|
|
210
|
+
"""
|
|
211
|
+
prompt_lower = prompt.lower().strip()
|
|
212
|
+
|
|
213
|
+
result: dict[str, Any] = {
|
|
214
|
+
"involves_exploration": False,
|
|
215
|
+
"involves_code_changes": False,
|
|
216
|
+
"involves_git": False,
|
|
217
|
+
"intent_confidence": 0.0,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Check for exploration keywords
|
|
221
|
+
exploration_matches = sum(1 for kw in EXPLORATION_KEYWORDS if kw in prompt_lower)
|
|
222
|
+
if exploration_matches > 0:
|
|
223
|
+
result["involves_exploration"] = True
|
|
224
|
+
result["intent_confidence"] = min(1.0, exploration_matches * 0.3)
|
|
225
|
+
|
|
226
|
+
# Check for code change keywords
|
|
227
|
+
code_matches = sum(1 for kw in CODE_CHANGE_KEYWORDS if kw in prompt_lower)
|
|
228
|
+
if code_matches > 0:
|
|
229
|
+
result["involves_code_changes"] = True
|
|
230
|
+
result["intent_confidence"] = max(
|
|
231
|
+
result["intent_confidence"], min(1.0, code_matches * 0.35)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Check for git keywords
|
|
235
|
+
git_matches = sum(1 for kw in GIT_KEYWORDS if kw in prompt_lower)
|
|
236
|
+
if git_matches > 0:
|
|
237
|
+
result["involves_git"] = True
|
|
238
|
+
result["intent_confidence"] = max(
|
|
239
|
+
result["intent_confidence"], min(1.0, git_matches * 0.4)
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
return result
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def get_session_violation_count(context: HookContext) -> tuple[int, int]:
|
|
246
|
+
"""
|
|
247
|
+
Get violation count for current session using CIGS ViolationTracker.
|
|
248
|
+
|
|
249
|
+
Queries the CIGS violation tracker to retrieve session violation metrics.
|
|
250
|
+
Gracefully degrades to (0, 0) if CIGS is unavailable.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
context: HookContext with session and graph directory info
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Tuple of (violation_count, total_waste_tokens)
|
|
257
|
+
Returns (0, 0) if CIGS is unavailable
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
>>> violation_count, waste_tokens = get_session_violation_count(context)
|
|
261
|
+
>>> if violation_count > 0:
|
|
262
|
+
... logger.info(f"Violations this session: {violation_count}")
|
|
263
|
+
"""
|
|
264
|
+
try:
|
|
265
|
+
from htmlgraph.cigs import ViolationTracker
|
|
266
|
+
|
|
267
|
+
tracker = ViolationTracker()
|
|
268
|
+
summary = tracker.get_session_violations()
|
|
269
|
+
return summary.total_violations, summary.total_waste_tokens
|
|
270
|
+
except Exception as e:
|
|
271
|
+
# Graceful degradation if CIGS not available
|
|
272
|
+
logger.debug(f"Could not get violation count: {e}")
|
|
273
|
+
return 0, 0
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_active_work_item(context: HookContext) -> dict[str, Any] | None:
|
|
277
|
+
"""
|
|
278
|
+
Query HtmlGraph for active feature/spike.
|
|
279
|
+
|
|
280
|
+
Attempts to load the active work item from the session manager.
|
|
281
|
+
Returns None if no active work item or if SDK is unavailable.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
context: HookContext with session and graph directory info
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
dict with work item details (id, title, type) or None if not found
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
>>> active = get_active_work_item(context)
|
|
291
|
+
>>> if active and active['type'] == 'feature':
|
|
292
|
+
... logger.info(f"Active feature: {active['title']}")
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
from htmlgraph import SDK
|
|
296
|
+
|
|
297
|
+
sdk = SDK()
|
|
298
|
+
work_item = sdk.get_active_work_item()
|
|
299
|
+
if work_item is None:
|
|
300
|
+
return None
|
|
301
|
+
# Convert ActiveWorkItem TypedDict to dict
|
|
302
|
+
return dict(work_item) if hasattr(work_item, "__iter__") else work_item # type: ignore
|
|
303
|
+
except Exception as e:
|
|
304
|
+
logger.debug(f"Could not get active work item: {e}")
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def generate_guidance(
|
|
309
|
+
classification: dict[str, Any], active_work: dict[str, Any] | None, prompt: str
|
|
310
|
+
) -> str | None:
|
|
311
|
+
"""
|
|
312
|
+
Generate workflow guidance based on classification and context.
|
|
313
|
+
|
|
314
|
+
Produces orchestrator directives and workflow suggestions based on
|
|
315
|
+
the prompt classification and current active work item.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
classification: Result from classify_prompt()
|
|
319
|
+
active_work: Result from get_active_work_item()
|
|
320
|
+
prompt: Original user prompt
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Guidance string to display to user, or None if no guidance needed
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> classification = classify_prompt("Implement new API endpoint")
|
|
327
|
+
>>> guidance = generate_guidance(classification, None, prompt)
|
|
328
|
+
>>> if guidance:
|
|
329
|
+
... logger.info("%s", guidance)
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
# If continuing and has active work, no guidance needed
|
|
333
|
+
if classification["is_continuation"] and active_work:
|
|
334
|
+
return None
|
|
335
|
+
|
|
336
|
+
# If has active work item, check if it matches intent
|
|
337
|
+
if active_work:
|
|
338
|
+
work_type = active_work.get("type", "")
|
|
339
|
+
work_id = active_work.get("id", "")
|
|
340
|
+
work_title = active_work.get("title", "")
|
|
341
|
+
|
|
342
|
+
# Implementation request with spike active - suggest creating feature
|
|
343
|
+
if classification["is_implementation"] and work_type == "spike":
|
|
344
|
+
return (
|
|
345
|
+
f"⚡ ORCHESTRATOR DIRECTIVE: Implementation requested during spike.\n\n"
|
|
346
|
+
f"Active work: {work_id} ({work_title}) - Type: spike\n\n"
|
|
347
|
+
f"Spikes are for investigation, NOT implementation.\n\n"
|
|
348
|
+
f"REQUIRED WORKFLOW:\n\n"
|
|
349
|
+
f"1. COMPLETE OR PAUSE the spike:\n"
|
|
350
|
+
f" sdk = SDK(agent='claude')\n"
|
|
351
|
+
f" sdk.spikes.complete('{work_id}') # or sdk.spikes.pause('{work_id}')\n\n"
|
|
352
|
+
f"2. CREATE A FEATURE for implementation:\n"
|
|
353
|
+
f" feature = sdk.features.create('Feature title').save()\n"
|
|
354
|
+
f" sdk.features.start(feature.id)\n\n"
|
|
355
|
+
f"3. DELEGATE TO SUBAGENT:\n"
|
|
356
|
+
f" from htmlgraph.tasks import Task\n"
|
|
357
|
+
f" Task(\n"
|
|
358
|
+
f" subagent_type='general-purpose',\n"
|
|
359
|
+
f" prompt='Implement: [details]'\n"
|
|
360
|
+
f" ).execute()\n\n"
|
|
361
|
+
f"Proceed with orchestration.\n"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Implementation request with feature active - remind to delegate
|
|
365
|
+
if classification["is_implementation"] and work_type == "feature":
|
|
366
|
+
return (
|
|
367
|
+
f"⚡ ORCHESTRATOR DIRECTIVE: Implementation work detected.\n\n"
|
|
368
|
+
f"Active work: {work_id} ({work_title}) - Type: feature\n\n"
|
|
369
|
+
f"REQUIRED: DELEGATE TO SUBAGENT:\n\n"
|
|
370
|
+
f" from htmlgraph.tasks import Task\n"
|
|
371
|
+
f" Task(\n"
|
|
372
|
+
f" subagent_type='general-purpose',\n"
|
|
373
|
+
f" prompt='Implement: [specific implementation details for {work_title}]'\n"
|
|
374
|
+
f" ).execute()\n\n"
|
|
375
|
+
f"DO NOT EXECUTE CODE DIRECTLY IN THIS CONTEXT.\n"
|
|
376
|
+
f"Orchestrators coordinate, subagents implement.\n\n"
|
|
377
|
+
f"Proceed with orchestration.\n"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Bug report with feature active - might want bug instead
|
|
381
|
+
if classification["is_bug_report"] and work_type == "feature":
|
|
382
|
+
return (
|
|
383
|
+
f"📋 WORKFLOW GUIDANCE:\n"
|
|
384
|
+
f"Active work: {work_id} ({work_title}) - Type: feature\n\n"
|
|
385
|
+
f"This looks like a bug report. Consider:\n"
|
|
386
|
+
f"1. If this bug is part of {work_title}, continue with current feature\n"
|
|
387
|
+
f"2. If this is a separate issue, create a bug:\n\n"
|
|
388
|
+
f" sdk = SDK(agent='claude')\n"
|
|
389
|
+
f" bug = sdk.bugs.create('Bug title').save()\n"
|
|
390
|
+
f" sdk.bugs.start(bug.id)\n"
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Has appropriate work item - no guidance needed
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
# No active work item - provide guidance based on intent
|
|
397
|
+
if classification["is_implementation"]:
|
|
398
|
+
return (
|
|
399
|
+
"⚡ ORCHESTRATOR DIRECTIVE: This is implementation work.\n\n"
|
|
400
|
+
"REQUIRED WORKFLOW (execute in order):\n\n"
|
|
401
|
+
"1. CREATE A WORK ITEM:\n"
|
|
402
|
+
" sdk = SDK(agent='claude')\n"
|
|
403
|
+
" feature = sdk.features.create('Your feature title').save()\n"
|
|
404
|
+
" sdk.features.start(feature.id)\n\n"
|
|
405
|
+
"2. DELEGATE TO SUBAGENT:\n"
|
|
406
|
+
" from htmlgraph.tasks import Task\n"
|
|
407
|
+
" Task(\n"
|
|
408
|
+
" subagent_type='general-purpose',\n"
|
|
409
|
+
" prompt='Implement: [specific implementation details]'\n"
|
|
410
|
+
" ).execute()\n\n"
|
|
411
|
+
"3. DO NOT EXECUTE CODE DIRECTLY IN THIS CONTEXT\n"
|
|
412
|
+
" - Orchestrators coordinate, subagents implement\n"
|
|
413
|
+
" - This ensures proper work tracking and session management\n\n"
|
|
414
|
+
"Proceed with orchestration.\n"
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
if classification["is_bug_report"]:
|
|
418
|
+
return (
|
|
419
|
+
"📋 WORKFLOW GUIDANCE - BUG REPORT DETECTED:\n\n"
|
|
420
|
+
"Create a bug work item to track this:\n\n"
|
|
421
|
+
" sdk = SDK(agent='claude')\n"
|
|
422
|
+
" bug = sdk.bugs.create('Bug title').save()\n"
|
|
423
|
+
" sdk.bugs.start(bug.id)\n\n"
|
|
424
|
+
"Then investigate and fix the issue.\n"
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
if classification["is_investigation"]:
|
|
428
|
+
return (
|
|
429
|
+
"📋 WORKFLOW GUIDANCE - INVESTIGATION REQUEST DETECTED:\n\n"
|
|
430
|
+
"Create a spike for time-boxed investigation:\n\n"
|
|
431
|
+
" sdk = SDK(agent='claude')\n"
|
|
432
|
+
" spike = sdk.spikes.create('Investigation title').save()\n"
|
|
433
|
+
" sdk.spikes.start(spike.id)\n\n"
|
|
434
|
+
"Spikes help track research and exploration work.\n"
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# Low confidence or unclear intent - provide gentle reminder
|
|
438
|
+
if classification["confidence"] < 0.5:
|
|
439
|
+
return (
|
|
440
|
+
"💡 REMINDER: Consider creating a work item if this is a task:\n"
|
|
441
|
+
"- Feature: sdk.features.create('Title').save()\n"
|
|
442
|
+
"- Bug: sdk.bugs.create('Title').save()\n"
|
|
443
|
+
"- Spike: sdk.spikes.create('Title').save()\n"
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return None
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def generate_cigs_guidance(
|
|
450
|
+
cigs_intent: dict[str, Any], violation_count: int, waste_tokens: int
|
|
451
|
+
) -> str:
|
|
452
|
+
"""
|
|
453
|
+
Generate CIGS-specific guidance for detected violations.
|
|
454
|
+
|
|
455
|
+
Produces pre-response imperative guidance based on CIGS intent
|
|
456
|
+
classification and current session violation metrics.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
cigs_intent: Result from classify_cigs_intent()
|
|
460
|
+
violation_count: Number of violations this session
|
|
461
|
+
waste_tokens: Total wasted tokens this session
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
Imperative guidance string (empty if no guidance needed)
|
|
465
|
+
|
|
466
|
+
Example:
|
|
467
|
+
>>> cigs = classify_cigs_intent("Search for all error handling")
|
|
468
|
+
>>> guidance = generate_cigs_guidance(cigs, 0, 0)
|
|
469
|
+
>>> if guidance:
|
|
470
|
+
... logger.info("%s", guidance)
|
|
471
|
+
"""
|
|
472
|
+
imperatives = []
|
|
473
|
+
|
|
474
|
+
# Exploration guidance
|
|
475
|
+
if cigs_intent["involves_exploration"]:
|
|
476
|
+
imperatives.append(
|
|
477
|
+
"🔴 IMPERATIVE: This request involves exploration.\n"
|
|
478
|
+
"YOU MUST use spawn_gemini() for exploration (FREE cost).\n"
|
|
479
|
+
"DO NOT use Read/Grep/Glob directly - delegate to Explorer subagent."
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
# Code changes guidance
|
|
483
|
+
if cigs_intent["involves_code_changes"]:
|
|
484
|
+
imperatives.append(
|
|
485
|
+
"🔴 IMPERATIVE: This request involves code changes.\n"
|
|
486
|
+
"YOU MUST use spawn_codex() or Task() for implementation.\n"
|
|
487
|
+
"DO NOT use Edit/Write directly - delegate to Coder subagent."
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# Git operations guidance
|
|
491
|
+
if cigs_intent["involves_git"]:
|
|
492
|
+
imperatives.append(
|
|
493
|
+
"🔴 IMPERATIVE: This request involves git operations.\n"
|
|
494
|
+
"YOU MUST use spawn_copilot() for git commands (60% cheaper).\n"
|
|
495
|
+
"DO NOT run git commands directly via Bash."
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
# Violation warning
|
|
499
|
+
if violation_count > 0:
|
|
500
|
+
warning_emoji = "⚠️" if violation_count < 3 else "🚨"
|
|
501
|
+
imperatives.append(
|
|
502
|
+
f"{warning_emoji} VIOLATION WARNING: You have {violation_count} delegation "
|
|
503
|
+
f"violations this session ({waste_tokens:,} tokens wasted).\n"
|
|
504
|
+
f"Circuit breaker triggers at 3 violations."
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
if not imperatives:
|
|
508
|
+
return ""
|
|
509
|
+
|
|
510
|
+
# Combine with header
|
|
511
|
+
guidance_parts = [
|
|
512
|
+
"═══════════════════════════════════════════════════════════",
|
|
513
|
+
"CIGS PRE-RESPONSE GUIDANCE (Computational Imperative Guidance System)",
|
|
514
|
+
"═══════════════════════════════════════════════════════════",
|
|
515
|
+
"",
|
|
516
|
+
]
|
|
517
|
+
guidance_parts.extend(imperatives)
|
|
518
|
+
guidance_parts.append("")
|
|
519
|
+
guidance_parts.append("═══════════════════════════════════════════════════════════")
|
|
520
|
+
|
|
521
|
+
return "\n".join(guidance_parts)
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def create_user_query_event(context: HookContext, prompt: str) -> str | None:
|
|
525
|
+
"""
|
|
526
|
+
Create UserQuery event in HtmlGraph database.
|
|
527
|
+
|
|
528
|
+
Records the user prompt as a UserQuery event that serves as the parent
|
|
529
|
+
for subsequent tool calls and delegations. Database is the single source
|
|
530
|
+
of truth - no file-based state is used.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
context: HookContext with session and database access
|
|
534
|
+
prompt: User's prompt text
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
UserQuery event_id if successful, None otherwise
|
|
538
|
+
|
|
539
|
+
Note:
|
|
540
|
+
Gracefully degrades if database is unavailable. Does not block
|
|
541
|
+
user interaction on failure.
|
|
542
|
+
|
|
543
|
+
Example:
|
|
544
|
+
>>> context = HookContext.from_input(hook_input)
|
|
545
|
+
>>> event_id = create_user_query_event(context, "Implement feature X")
|
|
546
|
+
>>> if event_id:
|
|
547
|
+
... logger.info(f"Created event: {event_id}")
|
|
548
|
+
"""
|
|
549
|
+
try:
|
|
550
|
+
session_id = context.session_id
|
|
551
|
+
|
|
552
|
+
if not session_id or session_id == "unknown":
|
|
553
|
+
logger.debug("No valid session ID for UserQuery event")
|
|
554
|
+
return None
|
|
555
|
+
|
|
556
|
+
# Create UserQuery event in database
|
|
557
|
+
try:
|
|
558
|
+
db = context.database
|
|
559
|
+
|
|
560
|
+
# Ensure session exists in database before creating event
|
|
561
|
+
# (sessions table has foreign key references, so we need to ensure it exists)
|
|
562
|
+
cursor = db.connection.cursor()
|
|
563
|
+
cursor.execute(
|
|
564
|
+
"SELECT COUNT(*) FROM sessions WHERE session_id = ?",
|
|
565
|
+
(session_id,),
|
|
566
|
+
)
|
|
567
|
+
session_exists = cursor.fetchone()[0] > 0
|
|
568
|
+
|
|
569
|
+
if not session_exists:
|
|
570
|
+
# Create session entry if it doesn't exist
|
|
571
|
+
cursor.execute(
|
|
572
|
+
"""
|
|
573
|
+
INSERT INTO sessions (session_id, created_at, status)
|
|
574
|
+
VALUES (?, ?, 'active')
|
|
575
|
+
""",
|
|
576
|
+
(session_id, datetime.now(timezone.utc).isoformat()),
|
|
577
|
+
)
|
|
578
|
+
db.connection.commit()
|
|
579
|
+
logger.debug(f"Created session entry: {session_id}")
|
|
580
|
+
|
|
581
|
+
# Generate event ID
|
|
582
|
+
user_query_event_id = f"uq-{uuid.uuid4().hex[:8]}"
|
|
583
|
+
|
|
584
|
+
# Prepare event details
|
|
585
|
+
input_summary = prompt[:200]
|
|
586
|
+
|
|
587
|
+
# Insert UserQuery event into agent_events
|
|
588
|
+
# Database is the single source of truth for parent-child linking
|
|
589
|
+
# Subsequent tool calls query database via get_parent_user_query()
|
|
590
|
+
success = db.insert_event(
|
|
591
|
+
event_id=user_query_event_id,
|
|
592
|
+
agent_id="user",
|
|
593
|
+
event_type="tool_call", # Valid event_type; find by tool_name='UserQuery'
|
|
594
|
+
session_id=session_id,
|
|
595
|
+
tool_name="UserQuery",
|
|
596
|
+
input_summary=input_summary,
|
|
597
|
+
context={
|
|
598
|
+
"prompt": prompt[:500],
|
|
599
|
+
"session": session_id,
|
|
600
|
+
},
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
if not success:
|
|
604
|
+
logger.warning("Failed to insert UserQuery event into database")
|
|
605
|
+
return None
|
|
606
|
+
|
|
607
|
+
logger.info(f"Created UserQuery event: {user_query_event_id}")
|
|
608
|
+
return user_query_event_id
|
|
609
|
+
|
|
610
|
+
except Exception as e:
|
|
611
|
+
# Database tracking is optional - graceful degradation
|
|
612
|
+
logger.error(f"UserQuery event creation failed: {e}", exc_info=True)
|
|
613
|
+
return None
|
|
614
|
+
|
|
615
|
+
except Exception as e:
|
|
616
|
+
# Silent failure - don't block user interaction
|
|
617
|
+
logger.error(f"Unexpected error in create_user_query_event: {e}", exc_info=True)
|
|
618
|
+
return None
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
__all__ = [
|
|
622
|
+
"classify_prompt",
|
|
623
|
+
"classify_cigs_intent",
|
|
624
|
+
"get_session_violation_count",
|
|
625
|
+
"get_active_work_item",
|
|
626
|
+
"generate_guidance",
|
|
627
|
+
"generate_cigs_guidance",
|
|
628
|
+
"create_user_query_event",
|
|
629
|
+
# Pattern constants for testing/extension
|
|
630
|
+
"IMPLEMENTATION_PATTERNS",
|
|
631
|
+
"INVESTIGATION_PATTERNS",
|
|
632
|
+
"BUG_PATTERNS",
|
|
633
|
+
"CONTINUATION_PATTERNS",
|
|
634
|
+
"EXPLORATION_KEYWORDS",
|
|
635
|
+
"CODE_CHANGE_KEYWORDS",
|
|
636
|
+
"GIT_KEYWORDS",
|
|
637
|
+
]
|