htmlgraph 0.20.1__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 +51 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/__init__.py +8 -1
- htmlgraph/analytics/cli.py +86 -20
- 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 +10 -6
- 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 +67 -27
- htmlgraph/analytics_index.py +53 -20
- 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 +2 -1
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/base.py +57 -2
- htmlgraph/builders/bug.py +19 -3
- htmlgraph/builders/chore.py +19 -3
- htmlgraph/builders/epic.py +19 -3
- htmlgraph/builders/feature.py +27 -3
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +19 -3
- htmlgraph/builders/spike.py +29 -3
- htmlgraph/builders/track.py +42 -1
- 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 +2 -0
- htmlgraph/collections/base.py +197 -14
- htmlgraph/collections/bug.py +2 -1
- htmlgraph/collections/chore.py +2 -1
- htmlgraph/collections/epic.py +2 -1
- htmlgraph/collections/feature.py +2 -1
- htmlgraph/collections/insight.py +2 -1
- htmlgraph/collections/metric.py +2 -1
- htmlgraph/collections/pattern.py +2 -1
- htmlgraph/collections/phase.py +2 -1
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +13 -2
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +116 -7
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2246 -248
- 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 +2 -1
- htmlgraph/deploy.py +26 -27
- 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 +2 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +86 -37
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +67 -9
- 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 +8 -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 +790 -99
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +327 -76
- htmlgraph/hooks/orchestrator_reflector.py +31 -4
- htmlgraph/hooks/post_tool_use_failure.py +32 -7
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +92 -19
- htmlgraph/hooks/pretooluse.py +527 -7
- 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 +99 -4
- htmlgraph/hooks/validator.py +212 -91
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +125 -100
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +217 -18
- 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.py → orchestration/task_coordination.py} +16 -8
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +115 -4
- htmlgraph/parallel.py +2 -1
- htmlgraph/parser.py +86 -6
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +2 -1
- 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/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 +295 -107
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +285 -3
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +33 -1
- htmlgraph/track_manager.py +38 -0
- htmlgraph/transcript.py +18 -5
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -4839
- htmlgraph/sdk.py +0 -2359
- htmlgraph-0.20.1.dist-info/RECORD +0 -118
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
logger = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Session Summary Module - CIGS Integration
|
|
7
|
+
|
|
8
|
+
Generates comprehensive session summaries with CIGS analytics at session end.
|
|
9
|
+
This module is loaded by the Stop hook.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _resolve_project_dir(cwd: str | None = None) -> str:
|
|
21
|
+
"""Resolve project directory (git root or cwd)."""
|
|
22
|
+
env_dir = os.environ.get("CLAUDE_PROJECT_DIR")
|
|
23
|
+
if env_dir:
|
|
24
|
+
return env_dir
|
|
25
|
+
start_dir = cwd or os.getcwd()
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
cwd=start_dir,
|
|
32
|
+
timeout=5,
|
|
33
|
+
)
|
|
34
|
+
if result.returncode == 0:
|
|
35
|
+
return result.stdout.strip()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
return start_dir
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _bootstrap_pythonpath(project_dir: str) -> None:
|
|
42
|
+
"""Add local src/python to PYTHONPATH for CIGS imports."""
|
|
43
|
+
repo_src = Path(project_dir) / "src" / "python"
|
|
44
|
+
if repo_src.exists():
|
|
45
|
+
sys.path.insert(0, str(repo_src))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Try to import CIGS modules
|
|
49
|
+
try:
|
|
50
|
+
project_dir_for_import = _resolve_project_dir()
|
|
51
|
+
_bootstrap_pythonpath(project_dir_for_import)
|
|
52
|
+
|
|
53
|
+
from htmlgraph.cigs.autonomy import AutonomyRecommender
|
|
54
|
+
from htmlgraph.cigs.cost import CostCalculator
|
|
55
|
+
from htmlgraph.cigs.patterns import PatternDetector
|
|
56
|
+
from htmlgraph.cigs.tracker import ViolationTracker
|
|
57
|
+
|
|
58
|
+
CIGS_AVAILABLE = True
|
|
59
|
+
except Exception:
|
|
60
|
+
CIGS_AVAILABLE = False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CIGSSessionSummarizer:
|
|
64
|
+
"""
|
|
65
|
+
Generate comprehensive session summary with CIGS analytics.
|
|
66
|
+
|
|
67
|
+
Implements section 2.5 of CIGS design document:
|
|
68
|
+
- Load session violations from ViolationTracker
|
|
69
|
+
- Analyze session patterns using PatternDetector
|
|
70
|
+
- Calculate session costs
|
|
71
|
+
- Generate autonomy recommendation for next session
|
|
72
|
+
- Build comprehensive session summary
|
|
73
|
+
- Persist summary to .htmlgraph/cigs/session-summaries/{session_id}.json
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(self, graph_dir: Path):
|
|
77
|
+
"""Initialize session summarizer.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
graph_dir: Path to .htmlgraph directory
|
|
81
|
+
"""
|
|
82
|
+
if not CIGS_AVAILABLE:
|
|
83
|
+
raise RuntimeError("CIGS modules not available")
|
|
84
|
+
|
|
85
|
+
self.graph_dir = Path(graph_dir)
|
|
86
|
+
self.cigs_dir = self.graph_dir / "cigs"
|
|
87
|
+
self.summaries_dir = self.cigs_dir / "session-summaries"
|
|
88
|
+
|
|
89
|
+
# Ensure directories exist
|
|
90
|
+
self.summaries_dir.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
# Initialize CIGS components
|
|
93
|
+
self.tracker = ViolationTracker(graph_dir)
|
|
94
|
+
self.pattern_detector = PatternDetector()
|
|
95
|
+
self.cost_calculator = CostCalculator()
|
|
96
|
+
self.autonomy_recommender = AutonomyRecommender()
|
|
97
|
+
|
|
98
|
+
def summarize(self, session_id: str | None = None) -> dict:
|
|
99
|
+
"""
|
|
100
|
+
Generate comprehensive session summary.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
session_id: Session ID (defaults to current/active session)
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Hook response with session summary
|
|
107
|
+
"""
|
|
108
|
+
# Get session violations
|
|
109
|
+
violations = self.tracker.get_session_violations(session_id)
|
|
110
|
+
|
|
111
|
+
# Detect patterns from recent violations
|
|
112
|
+
patterns = self._detect_patterns(violations.violations)
|
|
113
|
+
|
|
114
|
+
# Calculate cost metrics
|
|
115
|
+
costs = self._calculate_costs(violations)
|
|
116
|
+
|
|
117
|
+
# Generate autonomy recommendation for next session
|
|
118
|
+
autonomy_rec = self.autonomy_recommender.recommend(
|
|
119
|
+
violations=violations,
|
|
120
|
+
patterns=patterns,
|
|
121
|
+
compliance_history=self._get_compliance_history(),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Build summary text
|
|
125
|
+
summary_text = self._build_summary_text(
|
|
126
|
+
violations, patterns, costs, autonomy_rec
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Persist summary for future reference
|
|
130
|
+
self._persist_summary(
|
|
131
|
+
violations.session_id,
|
|
132
|
+
{
|
|
133
|
+
"session_id": violations.session_id,
|
|
134
|
+
"violations": violations.to_dict(),
|
|
135
|
+
"patterns": [p.to_dict() for p in patterns],
|
|
136
|
+
"costs": costs,
|
|
137
|
+
"autonomy_recommendation": autonomy_rec.to_dict(),
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
"hookSpecificOutput": {
|
|
143
|
+
"hookEventName": "Stop",
|
|
144
|
+
"additionalContext": summary_text,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
def _detect_patterns(self, violation_records: list) -> list:
|
|
149
|
+
"""
|
|
150
|
+
Detect behavioral patterns from violation records.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
violation_records: List of ViolationRecord instances
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
List of detected PatternRecord instances
|
|
157
|
+
"""
|
|
158
|
+
if not violation_records:
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
# Convert violations to tool history format
|
|
162
|
+
history: list[dict[str, Any]] = []
|
|
163
|
+
for v in violation_records:
|
|
164
|
+
history.append(
|
|
165
|
+
{
|
|
166
|
+
"tool": v.tool,
|
|
167
|
+
"command": v.tool_params.get("command", ""),
|
|
168
|
+
"file_path": v.tool_params.get("file_path", ""),
|
|
169
|
+
"prompt": v.tool_params.get("prompt", ""),
|
|
170
|
+
"timestamp": v.timestamp,
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Detect all patterns
|
|
175
|
+
patterns = self.pattern_detector.detect_all_patterns(history)
|
|
176
|
+
return patterns # type: ignore[no-any-return]
|
|
177
|
+
|
|
178
|
+
def _calculate_costs(self, violations: Any) -> dict:
|
|
179
|
+
"""
|
|
180
|
+
Calculate cost metrics from violations.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
violations: SessionViolationSummary
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Dictionary with cost metrics
|
|
187
|
+
"""
|
|
188
|
+
total_tokens = sum(v.actual_cost_tokens for v in violations.violations)
|
|
189
|
+
optimal_tokens = sum(v.optimal_cost_tokens for v in violations.violations)
|
|
190
|
+
waste_tokens = violations.total_waste_tokens
|
|
191
|
+
|
|
192
|
+
if total_tokens > 0:
|
|
193
|
+
waste_percentage = (waste_tokens / total_tokens) * 100
|
|
194
|
+
efficiency_score = (optimal_tokens / total_tokens) * 100
|
|
195
|
+
else:
|
|
196
|
+
waste_percentage = 0.0
|
|
197
|
+
efficiency_score = 100.0
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
"total_tokens": total_tokens,
|
|
201
|
+
"optimal_tokens": optimal_tokens,
|
|
202
|
+
"waste_tokens": waste_tokens,
|
|
203
|
+
"waste_percentage": waste_percentage,
|
|
204
|
+
"efficiency_score": efficiency_score,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
def _get_compliance_history(self) -> list[float]:
|
|
208
|
+
"""
|
|
209
|
+
Get compliance history from last 5 sessions.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of compliance rates (0.0-1.0)
|
|
213
|
+
"""
|
|
214
|
+
# Get recent violations (last 5 sessions)
|
|
215
|
+
recent = self.tracker.get_recent_violations(sessions=5)
|
|
216
|
+
|
|
217
|
+
# Group by session and calculate compliance rates
|
|
218
|
+
sessions: dict[str, list] = {}
|
|
219
|
+
for v in recent:
|
|
220
|
+
if v.session_id not in sessions:
|
|
221
|
+
sessions[v.session_id] = []
|
|
222
|
+
sessions[v.session_id].append(v)
|
|
223
|
+
|
|
224
|
+
# Calculate compliance rate per session
|
|
225
|
+
compliance_rates = []
|
|
226
|
+
for session_id, session_violations in sessions.items():
|
|
227
|
+
total_violations = len(session_violations)
|
|
228
|
+
# Compliance rate: 1.0 = no violations, decreases with more violations
|
|
229
|
+
compliance_rate = max(0.0, 1.0 - (total_violations / 5.0))
|
|
230
|
+
compliance_rates.append(compliance_rate)
|
|
231
|
+
|
|
232
|
+
return compliance_rates[-5:] if compliance_rates else [1.0]
|
|
233
|
+
|
|
234
|
+
def _build_summary_text(
|
|
235
|
+
self, violations: Any, patterns: Any, costs: Any, autonomy_rec: Any
|
|
236
|
+
) -> str:
|
|
237
|
+
"""
|
|
238
|
+
Build human-readable session summary.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
violations: SessionViolationSummary
|
|
242
|
+
patterns: List of PatternRecord instances
|
|
243
|
+
costs: Cost metrics dictionary
|
|
244
|
+
autonomy_rec: AutonomyLevel recommendation
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Formatted markdown summary
|
|
248
|
+
"""
|
|
249
|
+
# Compliance rate
|
|
250
|
+
compliance_pct = violations.compliance_rate * 100
|
|
251
|
+
|
|
252
|
+
# Circuit breaker status
|
|
253
|
+
breaker_status = (
|
|
254
|
+
"🚨 TRIGGERED" if violations.circuit_breaker_triggered else "✅ OK"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Format violations by type
|
|
258
|
+
violations_detail = ""
|
|
259
|
+
if violations.violations_by_type:
|
|
260
|
+
for vtype, count in violations.violations_by_type.items():
|
|
261
|
+
violations_detail += f" - {vtype}: {count}\n"
|
|
262
|
+
else:
|
|
263
|
+
violations_detail = " - No violations detected\n"
|
|
264
|
+
|
|
265
|
+
# Format detected patterns
|
|
266
|
+
patterns_text = self._format_patterns(patterns)
|
|
267
|
+
|
|
268
|
+
# Format anti-patterns
|
|
269
|
+
anti_patterns_text = self._format_anti_patterns(patterns)
|
|
270
|
+
|
|
271
|
+
# Build summary
|
|
272
|
+
summary = f"""## 📊 CIGS Session Summary
|
|
273
|
+
|
|
274
|
+
### Delegation Metrics
|
|
275
|
+
- **Compliance Rate:** {compliance_pct:.0f}%
|
|
276
|
+
- **Violations:** {violations.total_violations} (circuit breaker threshold: 3)
|
|
277
|
+
- **Circuit Breaker:** {breaker_status}
|
|
278
|
+
|
|
279
|
+
### Violation Breakdown
|
|
280
|
+
{violations_detail}
|
|
281
|
+
|
|
282
|
+
### Cost Analysis
|
|
283
|
+
- **Total Context Used:** {costs["total_tokens"]} tokens
|
|
284
|
+
- **Estimated Waste:** {costs["waste_tokens"]} tokens ({costs["waste_percentage"]:.1f}%)
|
|
285
|
+
- **Optimal Path Cost:** {costs["optimal_tokens"]} tokens
|
|
286
|
+
- **Efficiency Score:** {costs["efficiency_score"]:.0f}/100
|
|
287
|
+
|
|
288
|
+
{patterns_text}
|
|
289
|
+
|
|
290
|
+
{anti_patterns_text}
|
|
291
|
+
|
|
292
|
+
### Autonomy Recommendation
|
|
293
|
+
**Next Session Level:** {autonomy_rec.level.upper()}
|
|
294
|
+
**Messaging Intensity:** {autonomy_rec.messaging_intensity}
|
|
295
|
+
**Enforcement Mode:** {autonomy_rec.enforcement_mode}
|
|
296
|
+
|
|
297
|
+
**Reason:** {autonomy_rec.reason}
|
|
298
|
+
|
|
299
|
+
### Learning Applied
|
|
300
|
+
- ✅ Violation patterns added to detection model
|
|
301
|
+
- ✅ Cost predictions updated with actual session data
|
|
302
|
+
- ✅ Messaging intensity adjusted for next session: {autonomy_rec.messaging_intensity}
|
|
303
|
+
- ✅ Session summary persisted to `.htmlgraph/cigs/session-summaries/`
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
**Next Steps:**
|
|
308
|
+
1. Review detected anti-patterns (if any) and adjust workflow
|
|
309
|
+
2. Your autonomy level for next session: **{autonomy_rec.level.upper()}**
|
|
310
|
+
3. Guidance intensity: **{autonomy_rec.messaging_intensity}**
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
return summary
|
|
314
|
+
|
|
315
|
+
def _format_patterns(self, patterns: list) -> str:
|
|
316
|
+
"""Format detected good patterns."""
|
|
317
|
+
good_patterns = [p for p in patterns if p.pattern_type == "good-pattern"]
|
|
318
|
+
|
|
319
|
+
if not good_patterns:
|
|
320
|
+
return "### Detected Patterns\n- No significant patterns detected"
|
|
321
|
+
|
|
322
|
+
text = "### Detected Patterns\n"
|
|
323
|
+
for p in good_patterns:
|
|
324
|
+
text += f"- ✅ **{p.name}**: {p.description}\n"
|
|
325
|
+
text += f" - Occurrences: {p.occurrence_count}\n"
|
|
326
|
+
|
|
327
|
+
return text
|
|
328
|
+
|
|
329
|
+
def _format_anti_patterns(self, patterns: list) -> str:
|
|
330
|
+
"""Format detected anti-patterns with remediation."""
|
|
331
|
+
anti_patterns = [p for p in patterns if p.pattern_type == "anti-pattern"]
|
|
332
|
+
|
|
333
|
+
if not anti_patterns:
|
|
334
|
+
return "### Anti-Patterns Identified\n- ✅ No anti-patterns detected"
|
|
335
|
+
|
|
336
|
+
text = "### Anti-Patterns Identified\n"
|
|
337
|
+
for p in anti_patterns:
|
|
338
|
+
text += f"- ⚠️ **{p.name}**: {p.description}\n"
|
|
339
|
+
text += f" - Occurrences: {p.occurrence_count}\n"
|
|
340
|
+
if p.correct_approach:
|
|
341
|
+
text += f" - **Correct Approach:** {p.correct_approach}\n"
|
|
342
|
+
if p.delegation_suggestion:
|
|
343
|
+
text += f" - **Suggested Delegation:** {p.delegation_suggestion}\n"
|
|
344
|
+
|
|
345
|
+
return text
|
|
346
|
+
|
|
347
|
+
def _persist_summary(self, session_id: str, summary_data: dict) -> None:
|
|
348
|
+
"""
|
|
349
|
+
Persist session summary to file for future reference.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
session_id: Session identifier
|
|
353
|
+
summary_data: Summary dictionary to persist
|
|
354
|
+
"""
|
|
355
|
+
try:
|
|
356
|
+
summary_file = self.summaries_dir / f"{session_id}.json"
|
|
357
|
+
with open(summary_file, "w") as f:
|
|
358
|
+
json.dump(summary_data, f, indent=2, default=str)
|
|
359
|
+
except Exception as e:
|
|
360
|
+
logger.warning(f"Warning: Failed to persist summary: {e}")
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def main() -> None:
|
|
364
|
+
"""Hook entry point for script wrapper."""
|
|
365
|
+
# Check if tracking is disabled
|
|
366
|
+
if os.environ.get("HTMLGRAPH_DISABLE_TRACKING") == "1":
|
|
367
|
+
print(json.dumps({"continue": True}))
|
|
368
|
+
sys.exit(0)
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
hook_input = json.load(sys.stdin)
|
|
372
|
+
except json.JSONDecodeError:
|
|
373
|
+
hook_input = {}
|
|
374
|
+
|
|
375
|
+
session_id = hook_input.get("session_id") or os.environ.get("CLAUDE_SESSION_ID")
|
|
376
|
+
cwd = hook_input.get("cwd")
|
|
377
|
+
project_dir = _resolve_project_dir(cwd if cwd else None)
|
|
378
|
+
graph_dir = Path(project_dir) / ".htmlgraph"
|
|
379
|
+
|
|
380
|
+
# Check if CIGS is enabled (disabled by default for now)
|
|
381
|
+
cigs_enabled = os.environ.get("HTMLGRAPH_CIGS_ENABLED") == "1"
|
|
382
|
+
|
|
383
|
+
if not cigs_enabled or not CIGS_AVAILABLE:
|
|
384
|
+
# CIGS not enabled or not available, just output empty response
|
|
385
|
+
print(json.dumps({"continue": True}))
|
|
386
|
+
return
|
|
387
|
+
|
|
388
|
+
# Generate CIGS session summary
|
|
389
|
+
try:
|
|
390
|
+
summarizer = CIGSSessionSummarizer(graph_dir)
|
|
391
|
+
result = summarizer.summarize(session_id)
|
|
392
|
+
print(json.dumps(result))
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.warning(f"Warning: Could not generate CIGS summary: {e}")
|
|
395
|
+
print(json.dumps({"continue": True}))
|