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.
Files changed (304) hide show
  1. htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
  2. htmlgraph/.htmlgraph/agents.json +72 -0
  3. htmlgraph/.htmlgraph/htmlgraph.db +0 -0
  4. htmlgraph/__init__.py +51 -1
  5. htmlgraph/__init__.pyi +123 -0
  6. htmlgraph/agent_detection.py +26 -10
  7. htmlgraph/agent_registry.py +2 -1
  8. htmlgraph/analytics/__init__.py +8 -1
  9. htmlgraph/analytics/cli.py +86 -20
  10. htmlgraph/analytics/cost_analyzer.py +391 -0
  11. htmlgraph/analytics/cost_monitor.py +664 -0
  12. htmlgraph/analytics/cost_reporter.py +675 -0
  13. htmlgraph/analytics/cross_session.py +617 -0
  14. htmlgraph/analytics/dependency.py +10 -6
  15. htmlgraph/analytics/pattern_learning.py +771 -0
  16. htmlgraph/analytics/session_graph.py +707 -0
  17. htmlgraph/analytics/strategic/__init__.py +80 -0
  18. htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
  19. htmlgraph/analytics/strategic/pattern_detector.py +876 -0
  20. htmlgraph/analytics/strategic/preference_manager.py +709 -0
  21. htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
  22. htmlgraph/analytics/work_type.py +67 -27
  23. htmlgraph/analytics_index.py +53 -20
  24. htmlgraph/api/__init__.py +3 -0
  25. htmlgraph/api/cost_alerts_websocket.py +416 -0
  26. htmlgraph/api/main.py +2498 -0
  27. htmlgraph/api/static/htmx.min.js +1 -0
  28. htmlgraph/api/static/style-redesign.css +1344 -0
  29. htmlgraph/api/static/style.css +1079 -0
  30. htmlgraph/api/templates/dashboard-redesign.html +1366 -0
  31. htmlgraph/api/templates/dashboard.html +794 -0
  32. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  33. htmlgraph/api/templates/partials/activity-feed.html +1100 -0
  34. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  35. htmlgraph/api/templates/partials/agents.html +317 -0
  36. htmlgraph/api/templates/partials/event-traces.html +373 -0
  37. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  38. htmlgraph/api/templates/partials/features.html +578 -0
  39. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  40. htmlgraph/api/templates/partials/metrics.html +346 -0
  41. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  42. htmlgraph/api/templates/partials/orchestration.html +198 -0
  43. htmlgraph/api/templates/partials/spawners.html +375 -0
  44. htmlgraph/api/templates/partials/work-items.html +613 -0
  45. htmlgraph/api/websocket.py +538 -0
  46. htmlgraph/archive/__init__.py +24 -0
  47. htmlgraph/archive/bloom.py +234 -0
  48. htmlgraph/archive/fts.py +297 -0
  49. htmlgraph/archive/manager.py +583 -0
  50. htmlgraph/archive/search.py +244 -0
  51. htmlgraph/atomic_ops.py +560 -0
  52. htmlgraph/attribute_index.py +2 -1
  53. htmlgraph/bounded_paths.py +539 -0
  54. htmlgraph/builders/base.py +57 -2
  55. htmlgraph/builders/bug.py +19 -3
  56. htmlgraph/builders/chore.py +19 -3
  57. htmlgraph/builders/epic.py +19 -3
  58. htmlgraph/builders/feature.py +27 -3
  59. htmlgraph/builders/insight.py +2 -1
  60. htmlgraph/builders/metric.py +2 -1
  61. htmlgraph/builders/pattern.py +2 -1
  62. htmlgraph/builders/phase.py +19 -3
  63. htmlgraph/builders/spike.py +29 -3
  64. htmlgraph/builders/track.py +42 -1
  65. htmlgraph/cigs/__init__.py +81 -0
  66. htmlgraph/cigs/autonomy.py +385 -0
  67. htmlgraph/cigs/cost.py +475 -0
  68. htmlgraph/cigs/messages_basic.py +472 -0
  69. htmlgraph/cigs/messaging.py +365 -0
  70. htmlgraph/cigs/models.py +771 -0
  71. htmlgraph/cigs/pattern_storage.py +427 -0
  72. htmlgraph/cigs/patterns.py +503 -0
  73. htmlgraph/cigs/posttool_analyzer.py +234 -0
  74. htmlgraph/cigs/reporter.py +818 -0
  75. htmlgraph/cigs/tracker.py +317 -0
  76. htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
  77. htmlgraph/cli/.htmlgraph/agents.json +72 -0
  78. htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
  79. htmlgraph/cli/__init__.py +42 -0
  80. htmlgraph/cli/__main__.py +6 -0
  81. htmlgraph/cli/analytics.py +1424 -0
  82. htmlgraph/cli/base.py +685 -0
  83. htmlgraph/cli/constants.py +206 -0
  84. htmlgraph/cli/core.py +954 -0
  85. htmlgraph/cli/main.py +147 -0
  86. htmlgraph/cli/models.py +475 -0
  87. htmlgraph/cli/templates/__init__.py +1 -0
  88. htmlgraph/cli/templates/cost_dashboard.py +399 -0
  89. htmlgraph/cli/work/__init__.py +239 -0
  90. htmlgraph/cli/work/browse.py +115 -0
  91. htmlgraph/cli/work/features.py +568 -0
  92. htmlgraph/cli/work/orchestration.py +676 -0
  93. htmlgraph/cli/work/report.py +728 -0
  94. htmlgraph/cli/work/sessions.py +466 -0
  95. htmlgraph/cli/work/snapshot.py +559 -0
  96. htmlgraph/cli/work/tracks.py +486 -0
  97. htmlgraph/cli_commands/__init__.py +1 -0
  98. htmlgraph/cli_commands/feature.py +195 -0
  99. htmlgraph/cli_framework.py +115 -0
  100. htmlgraph/collections/__init__.py +2 -0
  101. htmlgraph/collections/base.py +197 -14
  102. htmlgraph/collections/bug.py +2 -1
  103. htmlgraph/collections/chore.py +2 -1
  104. htmlgraph/collections/epic.py +2 -1
  105. htmlgraph/collections/feature.py +2 -1
  106. htmlgraph/collections/insight.py +2 -1
  107. htmlgraph/collections/metric.py +2 -1
  108. htmlgraph/collections/pattern.py +2 -1
  109. htmlgraph/collections/phase.py +2 -1
  110. htmlgraph/collections/session.py +194 -0
  111. htmlgraph/collections/spike.py +13 -2
  112. htmlgraph/collections/task_delegation.py +241 -0
  113. htmlgraph/collections/todo.py +14 -1
  114. htmlgraph/collections/traces.py +487 -0
  115. htmlgraph/config/cost_models.json +56 -0
  116. htmlgraph/config.py +190 -0
  117. htmlgraph/context_analytics.py +2 -1
  118. htmlgraph/converter.py +116 -7
  119. htmlgraph/cost_analysis/__init__.py +5 -0
  120. htmlgraph/cost_analysis/analyzer.py +438 -0
  121. htmlgraph/dashboard.html +2246 -248
  122. htmlgraph/dashboard.html.backup +6592 -0
  123. htmlgraph/dashboard.html.bak +7181 -0
  124. htmlgraph/dashboard.html.bak2 +7231 -0
  125. htmlgraph/dashboard.html.bak3 +7232 -0
  126. htmlgraph/db/__init__.py +38 -0
  127. htmlgraph/db/queries.py +790 -0
  128. htmlgraph/db/schema.py +1788 -0
  129. htmlgraph/decorators.py +317 -0
  130. htmlgraph/dependency_models.py +2 -1
  131. htmlgraph/deploy.py +26 -27
  132. htmlgraph/docs/API_REFERENCE.md +841 -0
  133. htmlgraph/docs/HTTP_API.md +750 -0
  134. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  135. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
  136. htmlgraph/docs/README.md +532 -0
  137. htmlgraph/docs/__init__.py +77 -0
  138. htmlgraph/docs/docs_version.py +55 -0
  139. htmlgraph/docs/metadata.py +93 -0
  140. htmlgraph/docs/migrations.py +232 -0
  141. htmlgraph/docs/template_engine.py +143 -0
  142. htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
  143. htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
  144. htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
  145. htmlgraph/docs/templates/base_agents.md.j2 +78 -0
  146. htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
  147. htmlgraph/docs/version_check.py +163 -0
  148. htmlgraph/edge_index.py +2 -1
  149. htmlgraph/error_handler.py +544 -0
  150. htmlgraph/event_log.py +86 -37
  151. htmlgraph/event_migration.py +2 -1
  152. htmlgraph/file_watcher.py +12 -8
  153. htmlgraph/find_api.py +2 -1
  154. htmlgraph/git_events.py +67 -9
  155. htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
  156. htmlgraph/hooks/.htmlgraph/agents.json +72 -0
  157. htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
  158. htmlgraph/hooks/__init__.py +8 -0
  159. htmlgraph/hooks/bootstrap.py +169 -0
  160. htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
  161. htmlgraph/hooks/concurrent_sessions.py +208 -0
  162. htmlgraph/hooks/context.py +350 -0
  163. htmlgraph/hooks/drift_handler.py +525 -0
  164. htmlgraph/hooks/event_tracker.py +790 -99
  165. htmlgraph/hooks/git_commands.py +175 -0
  166. htmlgraph/hooks/installer.py +5 -1
  167. htmlgraph/hooks/orchestrator.py +327 -76
  168. htmlgraph/hooks/orchestrator_reflector.py +31 -4
  169. htmlgraph/hooks/post_tool_use_failure.py +32 -7
  170. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  171. htmlgraph/hooks/posttooluse.py +92 -19
  172. htmlgraph/hooks/pretooluse.py +527 -7
  173. htmlgraph/hooks/prompt_analyzer.py +637 -0
  174. htmlgraph/hooks/session_handler.py +668 -0
  175. htmlgraph/hooks/session_summary.py +395 -0
  176. htmlgraph/hooks/state_manager.py +504 -0
  177. htmlgraph/hooks/subagent_detection.py +202 -0
  178. htmlgraph/hooks/subagent_stop.py +369 -0
  179. htmlgraph/hooks/task_enforcer.py +99 -4
  180. htmlgraph/hooks/validator.py +212 -91
  181. htmlgraph/ids.py +2 -1
  182. htmlgraph/learning.py +125 -100
  183. htmlgraph/mcp_server.py +2 -1
  184. htmlgraph/models.py +217 -18
  185. htmlgraph/operations/README.md +62 -0
  186. htmlgraph/operations/__init__.py +79 -0
  187. htmlgraph/operations/analytics.py +339 -0
  188. htmlgraph/operations/bootstrap.py +289 -0
  189. htmlgraph/operations/events.py +244 -0
  190. htmlgraph/operations/fastapi_server.py +231 -0
  191. htmlgraph/operations/hooks.py +350 -0
  192. htmlgraph/operations/initialization.py +597 -0
  193. htmlgraph/operations/initialization.py.backup +228 -0
  194. htmlgraph/operations/server.py +303 -0
  195. htmlgraph/orchestration/__init__.py +58 -0
  196. htmlgraph/orchestration/claude_launcher.py +179 -0
  197. htmlgraph/orchestration/command_builder.py +72 -0
  198. htmlgraph/orchestration/headless_spawner.py +281 -0
  199. htmlgraph/orchestration/live_events.py +377 -0
  200. htmlgraph/orchestration/model_selection.py +327 -0
  201. htmlgraph/orchestration/plugin_manager.py +140 -0
  202. htmlgraph/orchestration/prompts.py +137 -0
  203. htmlgraph/orchestration/spawner_event_tracker.py +383 -0
  204. htmlgraph/orchestration/spawners/__init__.py +16 -0
  205. htmlgraph/orchestration/spawners/base.py +194 -0
  206. htmlgraph/orchestration/spawners/claude.py +173 -0
  207. htmlgraph/orchestration/spawners/codex.py +435 -0
  208. htmlgraph/orchestration/spawners/copilot.py +294 -0
  209. htmlgraph/orchestration/spawners/gemini.py +471 -0
  210. htmlgraph/orchestration/subprocess_runner.py +36 -0
  211. htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
  212. htmlgraph/orchestration.md +563 -0
  213. htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
  214. htmlgraph/orchestrator.py +2 -1
  215. htmlgraph/orchestrator_config.py +357 -0
  216. htmlgraph/orchestrator_mode.py +115 -4
  217. htmlgraph/parallel.py +2 -1
  218. htmlgraph/parser.py +86 -6
  219. htmlgraph/path_query.py +608 -0
  220. htmlgraph/pattern_matcher.py +636 -0
  221. htmlgraph/pydantic_models.py +476 -0
  222. htmlgraph/quality_gates.py +350 -0
  223. htmlgraph/query_builder.py +2 -1
  224. htmlgraph/query_composer.py +509 -0
  225. htmlgraph/reflection.py +443 -0
  226. htmlgraph/refs.py +344 -0
  227. htmlgraph/repo_hash.py +512 -0
  228. htmlgraph/repositories/__init__.py +292 -0
  229. htmlgraph/repositories/analytics_repository.py +455 -0
  230. htmlgraph/repositories/analytics_repository_standard.py +628 -0
  231. htmlgraph/repositories/feature_repository.py +581 -0
  232. htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
  233. htmlgraph/repositories/feature_repository_memory.py +607 -0
  234. htmlgraph/repositories/feature_repository_sqlite.py +858 -0
  235. htmlgraph/repositories/filter_service.py +620 -0
  236. htmlgraph/repositories/filter_service_standard.py +445 -0
  237. htmlgraph/repositories/shared_cache.py +621 -0
  238. htmlgraph/repositories/shared_cache_memory.py +395 -0
  239. htmlgraph/repositories/track_repository.py +552 -0
  240. htmlgraph/repositories/track_repository_htmlfile.py +619 -0
  241. htmlgraph/repositories/track_repository_memory.py +508 -0
  242. htmlgraph/repositories/track_repository_sqlite.py +711 -0
  243. htmlgraph/sdk/__init__.py +398 -0
  244. htmlgraph/sdk/__init__.pyi +14 -0
  245. htmlgraph/sdk/analytics/__init__.py +19 -0
  246. htmlgraph/sdk/analytics/engine.py +155 -0
  247. htmlgraph/sdk/analytics/helpers.py +178 -0
  248. htmlgraph/sdk/analytics/registry.py +109 -0
  249. htmlgraph/sdk/base.py +484 -0
  250. htmlgraph/sdk/constants.py +216 -0
  251. htmlgraph/sdk/core.pyi +308 -0
  252. htmlgraph/sdk/discovery.py +120 -0
  253. htmlgraph/sdk/help/__init__.py +12 -0
  254. htmlgraph/sdk/help/mixin.py +699 -0
  255. htmlgraph/sdk/mixins/__init__.py +15 -0
  256. htmlgraph/sdk/mixins/attribution.py +113 -0
  257. htmlgraph/sdk/mixins/mixin.py +410 -0
  258. htmlgraph/sdk/operations/__init__.py +12 -0
  259. htmlgraph/sdk/operations/mixin.py +427 -0
  260. htmlgraph/sdk/orchestration/__init__.py +17 -0
  261. htmlgraph/sdk/orchestration/coordinator.py +203 -0
  262. htmlgraph/sdk/orchestration/spawner.py +204 -0
  263. htmlgraph/sdk/planning/__init__.py +19 -0
  264. htmlgraph/sdk/planning/bottlenecks.py +93 -0
  265. htmlgraph/sdk/planning/mixin.py +211 -0
  266. htmlgraph/sdk/planning/parallel.py +186 -0
  267. htmlgraph/sdk/planning/queue.py +210 -0
  268. htmlgraph/sdk/planning/recommendations.py +87 -0
  269. htmlgraph/sdk/planning/smart_planning.py +319 -0
  270. htmlgraph/sdk/session/__init__.py +19 -0
  271. htmlgraph/sdk/session/continuity.py +57 -0
  272. htmlgraph/sdk/session/handoff.py +110 -0
  273. htmlgraph/sdk/session/info.py +309 -0
  274. htmlgraph/sdk/session/manager.py +103 -0
  275. htmlgraph/sdk/strategic/__init__.py +26 -0
  276. htmlgraph/sdk/strategic/mixin.py +563 -0
  277. htmlgraph/server.py +295 -107
  278. htmlgraph/session_hooks.py +300 -0
  279. htmlgraph/session_manager.py +285 -3
  280. htmlgraph/session_registry.py +587 -0
  281. htmlgraph/session_state.py +436 -0
  282. htmlgraph/session_warning.py +2 -1
  283. htmlgraph/sessions/__init__.py +23 -0
  284. htmlgraph/sessions/handoff.py +756 -0
  285. htmlgraph/system_prompts.py +450 -0
  286. htmlgraph/templates/orchestration-view.html +350 -0
  287. htmlgraph/track_builder.py +33 -1
  288. htmlgraph/track_manager.py +38 -0
  289. htmlgraph/transcript.py +18 -5
  290. htmlgraph/validation.py +115 -0
  291. htmlgraph/watch.py +2 -1
  292. htmlgraph/work_type_utils.py +2 -1
  293. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
  294. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
  295. htmlgraph-0.27.5.dist-info/RECORD +337 -0
  296. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
  297. htmlgraph/cli.py +0 -4839
  298. htmlgraph/sdk.py +0 -2359
  299. htmlgraph-0.20.1.dist-info/RECORD +0 -118
  300. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
  301. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  302. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  303. {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  304. {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
@@ -0,0 +1,317 @@
1
+ """
2
+ ViolationTracker for CIGS (Computational Imperative Guidance System)
3
+
4
+ Tracks delegation violations in JSONL format, providing thread-safe access
5
+ to violation records and session metrics.
6
+
7
+ Reference: .htmlgraph/spikes/computational-imperative-guidance-system-design.md (Part 3)
8
+ """
9
+
10
+ import json
11
+ import os
12
+ import threading
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+ from uuid import uuid4
16
+
17
+ from htmlgraph.cigs.models import (
18
+ OperationClassification,
19
+ SessionViolationSummary,
20
+ TokenCost,
21
+ ViolationRecord,
22
+ ViolationType,
23
+ )
24
+
25
+
26
+ class ViolationTracker:
27
+ """
28
+ Thread-safe tracker for delegation violations.
29
+
30
+ Stores violations in JSONL format at `.htmlgraph/cigs/violations.jsonl`
31
+ """
32
+
33
+ def __init__(self, graph_dir: Path | None = None):
34
+ """
35
+ Initialize ViolationTracker.
36
+
37
+ Args:
38
+ graph_dir: Root directory for HtmlGraph (defaults to .htmlgraph)
39
+ """
40
+ if graph_dir is None:
41
+ graph_dir = Path.cwd() / ".htmlgraph"
42
+
43
+ self.graph_dir = Path(graph_dir)
44
+ self.cigs_dir = self.graph_dir / "cigs"
45
+ self.violations_file = self.cigs_dir / "violations.jsonl"
46
+
47
+ # Create directory if needed
48
+ self.cigs_dir.mkdir(parents=True, exist_ok=True)
49
+
50
+ # Thread-safe access
51
+ self._lock = threading.RLock()
52
+
53
+ # Current session ID (set externally or detect from environment)
54
+ self._session_id: str | None = self._detect_session_id()
55
+
56
+ def _detect_session_id(self) -> str | None:
57
+ """Detect current session ID from environment or HtmlGraph session manager."""
58
+ # First check environment variable
59
+ if "HTMLGRAPH_SESSION_ID" in os.environ:
60
+ return os.environ["HTMLGRAPH_SESSION_ID"]
61
+
62
+ # Try to get from session manager if available
63
+ try:
64
+ from htmlgraph.session_manager import SessionManager
65
+
66
+ sm = SessionManager(self.graph_dir)
67
+ current = sm.get_active_session()
68
+ if current:
69
+ return str(current.id)
70
+ except Exception:
71
+ pass
72
+
73
+ return None
74
+
75
+ def record_violation(
76
+ self,
77
+ tool: str,
78
+ params: dict,
79
+ classification: OperationClassification,
80
+ predicted_waste: int,
81
+ ) -> str:
82
+ """
83
+ Record a violation.
84
+
85
+ Args:
86
+ tool: Tool name (Read, Grep, Edit, etc.)
87
+ params: Tool parameters passed
88
+ classification: OperationClassification with context
89
+ predicted_waste: Predicted wasted tokens
90
+
91
+ Returns:
92
+ Violation ID
93
+ """
94
+ with self._lock:
95
+ violation_id = f"viol-{uuid4().hex[:12]}"
96
+ timestamp = datetime.utcnow()
97
+
98
+ # Get current session
99
+ session_id = self._session_id or "unknown"
100
+
101
+ record = ViolationRecord(
102
+ id=violation_id,
103
+ session_id=session_id,
104
+ timestamp=timestamp,
105
+ tool=tool,
106
+ tool_params=params,
107
+ violation_type=self._classify_violation(tool, classification),
108
+ context_before=None,
109
+ should_have_delegated_to=classification.suggested_delegation,
110
+ actual_cost_tokens=classification.predicted_cost,
111
+ optimal_cost_tokens=classification.optimal_cost,
112
+ waste_tokens=predicted_waste,
113
+ warning_level=1,
114
+ was_warned=False,
115
+ warning_ignored=False,
116
+ agent="claude-code",
117
+ feature_id=None,
118
+ )
119
+
120
+ # Append to JSONL file
121
+ self._append_violation(record)
122
+
123
+ return violation_id
124
+
125
+ def _classify_violation(
126
+ self, tool: str, classification: OperationClassification
127
+ ) -> ViolationType:
128
+ """Classify the violation type based on tool and context."""
129
+ if classification.is_exploration_sequence:
130
+ return ViolationType.EXPLORATION_SEQUENCE
131
+
132
+ # Map tools to violation types
133
+ if tool in ("Read", "Grep", "Glob"):
134
+ return ViolationType.DIRECT_EXPLORATION
135
+ elif tool in ("Edit", "Write", "NotebookEdit"):
136
+ return ViolationType.DIRECT_IMPLEMENTATION
137
+ elif tool == "Bash" and "git" in str(classification.reason).lower():
138
+ return ViolationType.DIRECT_GIT
139
+ elif tool == "Bash":
140
+ return ViolationType.DIRECT_TESTING
141
+
142
+ return ViolationType.DIRECT_EXPLORATION
143
+
144
+ def _append_violation(self, record: ViolationRecord) -> None:
145
+ """Append violation record to JSONL file (thread-safe)."""
146
+ with self._lock:
147
+ try:
148
+ with open(self.violations_file, "a") as f:
149
+ f.write(json.dumps(record.to_dict()) + "\n")
150
+ except Exception as e:
151
+ # Log but don't crash on storage errors
152
+ print(f"Warning: Failed to record violation: {e}")
153
+
154
+ def get_session_violations(
155
+ self, session_id: str | None = None
156
+ ) -> SessionViolationSummary:
157
+ """
158
+ Get violations for current or specific session.
159
+
160
+ Args:
161
+ session_id: Session ID (defaults to current session)
162
+
163
+ Returns:
164
+ SessionViolationSummary with aggregated metrics
165
+ """
166
+ if session_id is None:
167
+ session_id = self._session_id or "unknown"
168
+
169
+ with self._lock:
170
+ violations = self._load_violations()
171
+
172
+ # Filter to session
173
+ session_violations = [v for v in violations if v.session_id == session_id]
174
+
175
+ # Aggregate by type
176
+ violations_by_type = {}
177
+ for vtype in ViolationType:
178
+ count = sum(1 for v in session_violations if v.violation_type == vtype)
179
+ if count > 0:
180
+ violations_by_type[vtype] = count
181
+
182
+ # Calculate totals
183
+ total_violations = len(session_violations)
184
+ total_waste = sum(v.waste_tokens for v in session_violations)
185
+ circuit_breaker = total_violations >= 3
186
+
187
+ # Compliance rate: 1.0 = no violations, 0.0 = many violations
188
+ # For simplicity: (max_violations - actual) / max_violations
189
+ # where max_violations = 5 (violation rate saturates)
190
+ compliance_rate = max(0.0, 1.0 - (total_violations / 5.0))
191
+
192
+ return SessionViolationSummary(
193
+ session_id=session_id,
194
+ total_violations=total_violations,
195
+ violations_by_type=violations_by_type,
196
+ total_waste_tokens=total_waste,
197
+ circuit_breaker_triggered=circuit_breaker,
198
+ compliance_rate=compliance_rate,
199
+ violations=session_violations,
200
+ )
201
+
202
+ def get_recent_violations(self, sessions: int = 5) -> list[ViolationRecord]:
203
+ """
204
+ Get violations from last N sessions.
205
+
206
+ Args:
207
+ sessions: Number of sessions to include
208
+
209
+ Returns:
210
+ List of violation records from recent sessions
211
+ """
212
+ with self._lock:
213
+ violations = self._load_violations()
214
+
215
+ # Group by session and get N most recent
216
+ session_ids = set(v.session_id for v in violations)
217
+ recent_sessions = sorted(session_ids)[-sessions:]
218
+
219
+ return [v for v in violations if v.session_id in recent_sessions]
220
+
221
+ def record_actual_cost(self, tool: str, cost: TokenCost) -> None:
222
+ """
223
+ Update violation with actual cost after execution.
224
+
225
+ This updates the most recent violation for the given tool.
226
+
227
+ Args:
228
+ tool: Tool name
229
+ cost: TokenCost with actual metrics
230
+ """
231
+ with self._lock:
232
+ violations = self._load_violations()
233
+
234
+ if not violations:
235
+ return
236
+
237
+ # Find most recent violation for this tool (in current session)
238
+ session_id = self._session_id or "unknown"
239
+ matching = [
240
+ v for v in violations if v.tool == tool and v.session_id == session_id
241
+ ]
242
+
243
+ if not matching:
244
+ return
245
+
246
+ # Update most recent
247
+ latest = matching[-1]
248
+ latest.actual_cost_tokens = cost.total_tokens
249
+ latest.waste_tokens = cost.total_tokens - latest.optimal_cost_tokens
250
+
251
+ # Rewrite file (simple approach - replace entire file)
252
+ self._write_violations(violations)
253
+
254
+ def _write_violations(self, violations: list[ViolationRecord]) -> None:
255
+ """Write violations back to JSONL file."""
256
+ with self._lock:
257
+ try:
258
+ with open(self.violations_file, "w") as f:
259
+ for v in violations:
260
+ f.write(json.dumps(v.to_dict()) + "\n")
261
+ except Exception as e:
262
+ print(f"Warning: Failed to write violations: {e}")
263
+
264
+ def _load_violations(self) -> list[ViolationRecord]:
265
+ """Load all violations from JSONL file."""
266
+ violations: list[ViolationRecord] = []
267
+
268
+ if not self.violations_file.exists():
269
+ return violations
270
+
271
+ try:
272
+ with open(self.violations_file) as f:
273
+ for line in f:
274
+ if not line.strip():
275
+ continue
276
+ try:
277
+ data = json.loads(line)
278
+ violations.append(ViolationRecord.from_dict(data))
279
+ except (json.JSONDecodeError, ValueError) as e:
280
+ print(f"Warning: Failed to parse violation record: {e}")
281
+ continue
282
+ except Exception as e:
283
+ print(f"Warning: Failed to load violations: {e}")
284
+
285
+ return violations
286
+
287
+ def get_session_waste(self) -> int:
288
+ """
289
+ Get total wasted tokens for current session.
290
+
291
+ Returns:
292
+ Total waste tokens
293
+ """
294
+ summary = self.get_session_violations()
295
+ return summary.total_waste_tokens
296
+
297
+ def set_session_id(self, session_id: str) -> None:
298
+ """
299
+ Set the current session ID.
300
+
301
+ Args:
302
+ session_id: Session ID to use for subsequent violations
303
+ """
304
+ self._session_id = session_id
305
+
306
+ def clear_session_file(self) -> None:
307
+ """
308
+ Clear the violations file (useful for testing).
309
+
310
+ WARNING: This deletes all violation records!
311
+ """
312
+ with self._lock:
313
+ try:
314
+ if self.violations_file.exists():
315
+ self.violations_file.unlink()
316
+ except Exception as e:
317
+ print(f"Warning: Failed to clear violations file: {e}")
@@ -0,0 +1,6 @@
1
+ {
2
+ "dismissed_at": null,
3
+ "dismissed_by": null,
4
+ "session_id": null,
5
+ "show_count": 485
6
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "version": "1.0",
3
+ "updated": "2026-01-11T21:41:53.445581",
4
+ "agents": {
5
+ "claude": {
6
+ "id": "claude",
7
+ "name": "Claude",
8
+ "capabilities": [
9
+ "python",
10
+ "javascript",
11
+ "typescript",
12
+ "html",
13
+ "css",
14
+ "code-review",
15
+ "testing",
16
+ "documentation",
17
+ "debugging",
18
+ "refactoring",
19
+ "architecture",
20
+ "api-design"
21
+ ],
22
+ "max_parallel_tasks": 3,
23
+ "preferred_complexity": [
24
+ "low",
25
+ "medium",
26
+ "high",
27
+ "very-high"
28
+ ],
29
+ "active": true,
30
+ "metadata": {}
31
+ },
32
+ "gemini": {
33
+ "id": "gemini",
34
+ "name": "Gemini",
35
+ "capabilities": [
36
+ "python",
37
+ "data-analysis",
38
+ "documentation",
39
+ "testing",
40
+ "code-review",
41
+ "javascript"
42
+ ],
43
+ "max_parallel_tasks": 2,
44
+ "preferred_complexity": [
45
+ "low",
46
+ "medium",
47
+ "high"
48
+ ],
49
+ "active": true,
50
+ "metadata": {}
51
+ },
52
+ "codex": {
53
+ "id": "codex",
54
+ "name": "Codex",
55
+ "capabilities": [
56
+ "python",
57
+ "javascript",
58
+ "debugging",
59
+ "testing",
60
+ "code-generation",
61
+ "documentation"
62
+ ],
63
+ "max_parallel_tasks": 2,
64
+ "preferred_complexity": [
65
+ "low",
66
+ "medium"
67
+ ],
68
+ "active": true,
69
+ "metadata": {}
70
+ }
71
+ }
72
+ }
Binary file
@@ -0,0 +1,42 @@
1
+ """HtmlGraph CLI - Modular command-line interface.
2
+
3
+ Architecture:
4
+ - base.py: Base classes, formatters, error handling
5
+ - constants.py: Single source of truth for all configuration
6
+ - main.py: Entry point, argument parsing
7
+ - core.py: Infrastructure commands (serve, init, status, etc.)
8
+ - work.py: Work management (features, sessions, tracks, etc.)
9
+ - analytics.py: Reporting and analytics commands
10
+
11
+ Usage:
12
+ from htmlgraph.cli.main import main
13
+ main()
14
+ """
15
+
16
+ from htmlgraph.cli.base import (
17
+ BaseCommand,
18
+ CommandError,
19
+ CommandResult,
20
+ JsonFormatter,
21
+ TextFormatter,
22
+ get_formatter,
23
+ )
24
+ from htmlgraph.cli.main import main
25
+ from htmlgraph.cli.work import (
26
+ cmd_orchestrator_reset_violations,
27
+ cmd_orchestrator_set_level,
28
+ cmd_orchestrator_status,
29
+ )
30
+
31
+ __all__ = [
32
+ "main",
33
+ "BaseCommand",
34
+ "CommandError",
35
+ "CommandResult",
36
+ "JsonFormatter",
37
+ "TextFormatter",
38
+ "get_formatter",
39
+ "cmd_orchestrator_reset_violations",
40
+ "cmd_orchestrator_set_level",
41
+ "cmd_orchestrator_status",
42
+ ]
@@ -0,0 +1,6 @@
1
+ """CLI entry point for python -m htmlgraph.cli execution."""
2
+
3
+ from htmlgraph.cli.main import main
4
+
5
+ if __name__ == "__main__":
6
+ main()