htmlgraph 0.20.1__py3-none-any.whl → 0.20.3__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/__init__.py +1 -1
- htmlgraph/cli.py +66 -0
- htmlgraph/hooks/orchestrator.py +47 -6
- htmlgraph/hooks/post_tool_use_failure.py +25 -4
- htmlgraph/hooks/posttooluse.py +38 -17
- htmlgraph/orchestrator_mode.py +78 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.20.3.dist-info}/METADATA +1 -1
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.20.3.dist-info}/RECORD +15 -15
- {htmlgraph-0.20.1.data → htmlgraph-0.20.3.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.20.3.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.20.3.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.20.3.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.20.3.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.20.3.dist-info}/WHEEL +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.20.3.dist-info}/entry_points.txt +0 -0
htmlgraph/__init__.py
CHANGED
htmlgraph/cli.py
CHANGED
|
@@ -2724,12 +2724,53 @@ def cmd_orchestrator_status(args: argparse.Namespace) -> None:
|
|
|
2724
2724
|
print(f"Activated at: {status['activated_at']}")
|
|
2725
2725
|
if status["auto_activated"]:
|
|
2726
2726
|
print("Auto-activated: yes")
|
|
2727
|
+
|
|
2728
|
+
# Show violation tracking info
|
|
2729
|
+
violations = status.get("violations", 0)
|
|
2730
|
+
circuit_breaker = status.get("circuit_breaker_triggered", False)
|
|
2731
|
+
if violations > 0:
|
|
2732
|
+
print(f"Violations: {violations}/3")
|
|
2733
|
+
if circuit_breaker:
|
|
2734
|
+
print("⚠️ Circuit breaker: TRIGGERED")
|
|
2727
2735
|
else:
|
|
2728
2736
|
print("Orchestrator mode: disabled")
|
|
2729
2737
|
if status["disabled_by_user"]:
|
|
2730
2738
|
print("Disabled by user (auto-activation prevented)")
|
|
2731
2739
|
|
|
2732
2740
|
|
|
2741
|
+
def cmd_orchestrator_set_level(args: argparse.Namespace) -> None:
|
|
2742
|
+
"""Set orchestrator mode enforcement level."""
|
|
2743
|
+
from typing import Literal
|
|
2744
|
+
|
|
2745
|
+
from htmlgraph.orchestrator_mode import OrchestratorModeManager
|
|
2746
|
+
|
|
2747
|
+
manager = OrchestratorModeManager(args.graph_dir)
|
|
2748
|
+
level: Literal["strict", "guidance"] = args.level
|
|
2749
|
+
manager.set_level(level)
|
|
2750
|
+
|
|
2751
|
+
level_text = "strict enforcement" if level == "strict" else "guidance mode"
|
|
2752
|
+
print(f"✓ Orchestrator enforcement level set to: {level_text}")
|
|
2753
|
+
|
|
2754
|
+
|
|
2755
|
+
def cmd_orchestrator_reset_violations(args: argparse.Namespace) -> None:
|
|
2756
|
+
"""Reset orchestrator mode violation counter."""
|
|
2757
|
+
from htmlgraph.orchestrator_mode import OrchestratorModeManager
|
|
2758
|
+
|
|
2759
|
+
manager = OrchestratorModeManager(args.graph_dir)
|
|
2760
|
+
|
|
2761
|
+
# Check if mode is enabled
|
|
2762
|
+
if not manager.is_enabled():
|
|
2763
|
+
print("⚠️ Orchestrator mode is not enabled")
|
|
2764
|
+
return
|
|
2765
|
+
|
|
2766
|
+
# Reset violations
|
|
2767
|
+
manager.reset_violations()
|
|
2768
|
+
|
|
2769
|
+
print("✓ Violation counter reset")
|
|
2770
|
+
print("Circuit breaker: cleared")
|
|
2771
|
+
print("You can now continue with delegation workflow")
|
|
2772
|
+
|
|
2773
|
+
|
|
2733
2774
|
def cmd_publish(args: argparse.Namespace) -> None:
|
|
2734
2775
|
"""Build and publish the package to PyPI (Interoperable)."""
|
|
2735
2776
|
import shutil
|
|
@@ -4475,6 +4516,27 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4475
4516
|
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4476
4517
|
)
|
|
4477
4518
|
|
|
4519
|
+
# orchestrator set-level
|
|
4520
|
+
orchestrator_set_level = orchestrator_subparsers.add_parser(
|
|
4521
|
+
"set-level", help="Set enforcement level"
|
|
4522
|
+
)
|
|
4523
|
+
orchestrator_set_level.add_argument(
|
|
4524
|
+
"level",
|
|
4525
|
+
choices=["strict", "guidance"],
|
|
4526
|
+
help="Enforcement level to set",
|
|
4527
|
+
)
|
|
4528
|
+
orchestrator_set_level.add_argument(
|
|
4529
|
+
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4530
|
+
)
|
|
4531
|
+
|
|
4532
|
+
# orchestrator reset-violations
|
|
4533
|
+
orchestrator_reset_violations = orchestrator_subparsers.add_parser(
|
|
4534
|
+
"reset-violations", help="Reset violation counter and circuit breaker"
|
|
4535
|
+
)
|
|
4536
|
+
orchestrator_reset_violations.add_argument(
|
|
4537
|
+
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4538
|
+
)
|
|
4539
|
+
|
|
4478
4540
|
# install-gemini-extension
|
|
4479
4541
|
subparsers.add_parser(
|
|
4480
4542
|
"install-gemini-extension",
|
|
@@ -4678,6 +4740,10 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4678
4740
|
cmd_orchestrator_disable(args)
|
|
4679
4741
|
elif args.orchestrator_command == "status":
|
|
4680
4742
|
cmd_orchestrator_status(args)
|
|
4743
|
+
elif args.orchestrator_command == "set-level":
|
|
4744
|
+
cmd_orchestrator_set_level(args)
|
|
4745
|
+
elif args.orchestrator_command == "reset-violations":
|
|
4746
|
+
cmd_orchestrator_reset_violations(args)
|
|
4681
4747
|
else:
|
|
4682
4748
|
orchestrator_parser.print_help()
|
|
4683
4749
|
sys.exit(1)
|
htmlgraph/hooks/orchestrator.py
CHANGED
|
@@ -358,6 +358,31 @@ def enforce_orchestrator_mode(tool: str, params: dict) -> dict:
|
|
|
358
358
|
},
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
# Check if circuit breaker is triggered in strict mode
|
|
362
|
+
if enforcement_level == "strict" and manager.is_circuit_breaker_triggered():
|
|
363
|
+
# Circuit breaker triggered - block all non-core operations
|
|
364
|
+
if tool not in ["Task", "AskUserQuestion", "TodoWrite"]:
|
|
365
|
+
circuit_breaker_message = (
|
|
366
|
+
"🚨 ORCHESTRATOR CIRCUIT BREAKER TRIGGERED\n\n"
|
|
367
|
+
f"You have violated delegation rules {manager.get_violation_count()} times this session.\n\n"
|
|
368
|
+
"Violations detected:\n"
|
|
369
|
+
"- Direct execution instead of delegation\n"
|
|
370
|
+
"- Context waste on tactical operations\n\n"
|
|
371
|
+
"Options:\n"
|
|
372
|
+
"1. Disable orchestrator mode: uv run htmlgraph orchestrator disable\n"
|
|
373
|
+
"2. Change to guidance mode: uv run htmlgraph orchestrator set-level guidance\n"
|
|
374
|
+
"3. Reset counter (acknowledge violations): uv run htmlgraph orchestrator reset-violations\n\n"
|
|
375
|
+
"To proceed, choose an option above."
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
"hookSpecificOutput": {
|
|
380
|
+
"hookEventName": "PreToolUse",
|
|
381
|
+
"permissionDecision": "deny",
|
|
382
|
+
"permissionDecisionReason": circuit_breaker_message,
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
|
|
361
386
|
# Check if operation is allowed
|
|
362
387
|
is_allowed, reason, category = is_allowed_orchestrator_operation(tool, params)
|
|
363
388
|
|
|
@@ -386,19 +411,35 @@ def enforce_orchestrator_mode(tool: str, params: dict) -> dict:
|
|
|
386
411
|
},
|
|
387
412
|
}
|
|
388
413
|
|
|
389
|
-
# Operation not allowed - provide
|
|
390
|
-
|
|
414
|
+
# Operation not allowed - track violation and provide warnings
|
|
415
|
+
if enforcement_level == "strict":
|
|
416
|
+
# Increment violation counter
|
|
417
|
+
mode = manager.increment_violation()
|
|
418
|
+
violations = mode.violations
|
|
419
|
+
|
|
391
420
|
suggestion = create_task_suggestion(tool, params)
|
|
392
421
|
|
|
393
422
|
if enforcement_level == "strict":
|
|
394
|
-
# STRICT mode - loud warning
|
|
423
|
+
# STRICT mode - loud warning with violation count
|
|
395
424
|
error_message = (
|
|
396
|
-
f"🚫 ORCHESTRATOR MODE VIOLATION: {reason}\n\n"
|
|
425
|
+
f"🚫 ORCHESTRATOR MODE VIOLATION ({violations}/3): {reason}\n\n"
|
|
397
426
|
f"⚠️ WARNING: Direct operations waste context and break delegation pattern!\n\n"
|
|
398
427
|
f"Suggested delegation:\n"
|
|
399
428
|
f"{suggestion}\n\n"
|
|
400
|
-
|
|
401
|
-
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# Add circuit breaker warning if approaching threshold
|
|
432
|
+
if violations >= 3:
|
|
433
|
+
error_message += (
|
|
434
|
+
"🚨 CIRCUIT BREAKER TRIGGERED - Further violations will be blocked!\n\n"
|
|
435
|
+
"Reset with: uv run htmlgraph orchestrator reset-violations\n"
|
|
436
|
+
)
|
|
437
|
+
elif violations == 2:
|
|
438
|
+
error_message += "⚠️ Next violation will trigger circuit breaker!\n\n"
|
|
439
|
+
|
|
440
|
+
error_message += (
|
|
441
|
+
"See ORCHESTRATOR_DIRECTIVES in session context for HtmlGraph delegation pattern.\n"
|
|
442
|
+
"To disable orchestrator mode: uv run htmlgraph orchestrator disable"
|
|
402
443
|
)
|
|
403
444
|
|
|
404
445
|
return {
|
|
@@ -37,27 +37,48 @@ def run(hook_input: dict[str, Any]) -> dict[str, Any]:
|
|
|
37
37
|
Standard hook response: {"continue": True}
|
|
38
38
|
"""
|
|
39
39
|
try:
|
|
40
|
+
# DEBUG: Log raw hook input to understand structure
|
|
41
|
+
debug_log = Path(".htmlgraph/hook-debug.jsonl")
|
|
42
|
+
debug_log.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
with open(debug_log, "a") as f:
|
|
44
|
+
f.write(
|
|
45
|
+
json.dumps(
|
|
46
|
+
{
|
|
47
|
+
"raw_input": hook_input,
|
|
48
|
+
"keys": list(hook_input.keys()),
|
|
49
|
+
"ts": datetime.now().isoformat(),
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
+ "\n"
|
|
53
|
+
)
|
|
54
|
+
|
|
40
55
|
# Extract error information from PostToolUse hook format
|
|
41
|
-
|
|
56
|
+
# Official PostToolUse uses: tool_name, tool_response
|
|
57
|
+
# Custom hooks may use: name, result
|
|
58
|
+
tool_name = hook_input.get("tool_name") or hook_input.get("name", "unknown")
|
|
42
59
|
session_id = hook_input.get("session_id", "unknown")
|
|
43
60
|
|
|
44
61
|
# Error message can be in different places depending on tool
|
|
45
62
|
error_msg = "No error message"
|
|
46
63
|
|
|
47
|
-
# Check
|
|
48
|
-
result
|
|
64
|
+
# Check tool_response field first (official PostToolUse format)
|
|
65
|
+
# Then check result field (custom hook format)
|
|
66
|
+
result = hook_input.get("tool_response") or hook_input.get("result", {})
|
|
49
67
|
if isinstance(result, dict):
|
|
50
68
|
if "error" in result:
|
|
51
69
|
error_msg = result["error"]
|
|
52
70
|
elif "message" in result:
|
|
53
71
|
error_msg = result["message"]
|
|
72
|
+
elif isinstance(result, str):
|
|
73
|
+
# Sometimes the error is directly in the result as a string
|
|
74
|
+
error_msg = result
|
|
54
75
|
|
|
55
76
|
# Fallback: check top-level error field
|
|
56
77
|
if error_msg == "No error message" and "error" in hook_input:
|
|
57
78
|
error_msg = hook_input["error"]
|
|
58
79
|
|
|
59
80
|
# Last resort: stringify the result if it contains error indicators
|
|
60
|
-
if error_msg == "No error message":
|
|
81
|
+
if error_msg == "No error message" and result:
|
|
61
82
|
result_str = str(result).lower()
|
|
62
83
|
if any(
|
|
63
84
|
indicator in result_str
|
htmlgraph/hooks/posttooluse.py
CHANGED
|
@@ -119,6 +119,8 @@ async def run_error_tracking(hook_input: dict[str, Any]) -> dict[str, Any]:
|
|
|
119
119
|
"""
|
|
120
120
|
Track errors to .htmlgraph/errors.jsonl and auto-create debug spikes.
|
|
121
121
|
|
|
122
|
+
Only tracks ACTUAL errors, not responses containing the word "error".
|
|
123
|
+
|
|
122
124
|
Args:
|
|
123
125
|
hook_input: Hook input with tool execution details
|
|
124
126
|
|
|
@@ -128,23 +130,26 @@ async def run_error_tracking(hook_input: dict[str, Any]) -> dict[str, Any]:
|
|
|
128
130
|
try:
|
|
129
131
|
loop = asyncio.get_event_loop()
|
|
130
132
|
|
|
131
|
-
# Check if this is an
|
|
133
|
+
# Check if this is an ACTUAL error
|
|
132
134
|
has_error = False
|
|
133
|
-
tool_response = hook_input.get("
|
|
134
|
-
|
|
135
|
-
)
|
|
135
|
+
tool_response = hook_input.get("tool_response") or hook_input.get("result", {})
|
|
136
|
+
|
|
137
|
+
if isinstance(tool_response, dict):
|
|
138
|
+
# Bash: non-empty stderr indicates error
|
|
139
|
+
stderr = tool_response.get("stderr", "")
|
|
140
|
+
if stderr and isinstance(stderr, str) and stderr.strip():
|
|
141
|
+
has_error = True
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
# Explicit error field with content
|
|
144
|
+
error_field = tool_response.get("error")
|
|
145
|
+
if error_field and str(error_field).strip():
|
|
146
|
+
has_error = True
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if any(indicator in response_text for indicator in error_indicators):
|
|
145
|
-
has_error = True
|
|
148
|
+
# success=false flag
|
|
149
|
+
if tool_response.get("success") is False:
|
|
150
|
+
has_error = True
|
|
146
151
|
|
|
147
|
-
# Only track if there's an error
|
|
152
|
+
# Only track if there's an actual error
|
|
148
153
|
if has_error:
|
|
149
154
|
return await loop.run_in_executor(
|
|
150
155
|
None,
|
|
@@ -162,6 +167,9 @@ async def suggest_debugging_resources(hook_input: dict[str, Any]) -> dict[str, A
|
|
|
162
167
|
"""
|
|
163
168
|
Suggest debugging resources based on tool results.
|
|
164
169
|
|
|
170
|
+
Only triggers on ACTUAL errors, not on responses that happen to contain
|
|
171
|
+
the word "error" in their content.
|
|
172
|
+
|
|
165
173
|
Args:
|
|
166
174
|
hook_input: Hook input with tool execution details
|
|
167
175
|
|
|
@@ -176,11 +184,24 @@ async def suggest_debugging_resources(hook_input: dict[str, Any]) -> dict[str, A
|
|
|
176
184
|
|
|
177
185
|
suggestions = []
|
|
178
186
|
|
|
179
|
-
# Check for
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
# Check for ACTUAL errors (not just text containing "error")
|
|
188
|
+
has_actual_error = False
|
|
189
|
+
|
|
190
|
+
if isinstance(tool_response, dict):
|
|
191
|
+
# Bash: non-empty stderr indicates error
|
|
192
|
+
stderr = tool_response.get("stderr", "")
|
|
193
|
+
if stderr and isinstance(stderr, str) and stderr.strip():
|
|
194
|
+
has_actual_error = True
|
|
195
|
+
|
|
196
|
+
# Explicit error field
|
|
197
|
+
if tool_response.get("error"):
|
|
198
|
+
has_actual_error = True
|
|
199
|
+
|
|
200
|
+
# success=false flag
|
|
201
|
+
if tool_response.get("success") is False:
|
|
202
|
+
has_actual_error = True
|
|
182
203
|
|
|
183
|
-
if
|
|
204
|
+
if has_actual_error:
|
|
184
205
|
suggestions.append("⚠️ Error detected in tool response")
|
|
185
206
|
suggestions.append("Debugging resources:")
|
|
186
207
|
suggestions.append(" 📚 DEBUGGING.md - Systematic debugging guide")
|
htmlgraph/orchestrator_mode.py
CHANGED
|
@@ -34,6 +34,15 @@ class OrchestratorMode(BaseModel):
|
|
|
34
34
|
disabled_by_user: bool = False
|
|
35
35
|
"""Whether user explicitly disabled mode (prevents auto-reactivation)."""
|
|
36
36
|
|
|
37
|
+
violations: int = 0
|
|
38
|
+
"""Count of delegation violations in current session."""
|
|
39
|
+
|
|
40
|
+
last_violation_at: datetime | None = None
|
|
41
|
+
"""Timestamp of most recent violation."""
|
|
42
|
+
|
|
43
|
+
circuit_breaker_triggered: bool = False
|
|
44
|
+
"""Whether circuit breaker has been triggered (3+ violations)."""
|
|
45
|
+
|
|
37
46
|
def to_dict(self) -> dict:
|
|
38
47
|
"""Convert to dict for JSON serialization."""
|
|
39
48
|
return {
|
|
@@ -45,6 +54,11 @@ class OrchestratorMode(BaseModel):
|
|
|
45
54
|
"enforcement_level": self.enforcement_level,
|
|
46
55
|
"auto_activated": self.auto_activated,
|
|
47
56
|
"disabled_by_user": self.disabled_by_user,
|
|
57
|
+
"violations": self.violations,
|
|
58
|
+
"last_violation_at": (
|
|
59
|
+
self.last_violation_at.isoformat() if self.last_violation_at else None
|
|
60
|
+
),
|
|
61
|
+
"circuit_breaker_triggered": self.circuit_breaker_triggered,
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
@classmethod
|
|
@@ -57,6 +71,13 @@ class OrchestratorMode(BaseModel):
|
|
|
57
71
|
activated_at = activated_at[:-1] + "+00:00"
|
|
58
72
|
activated_at = datetime.fromisoformat(activated_at)
|
|
59
73
|
|
|
74
|
+
last_violation_at = data.get("last_violation_at")
|
|
75
|
+
if last_violation_at:
|
|
76
|
+
# Handle both 'Z' suffix and '+00:00' timezone format
|
|
77
|
+
if last_violation_at.endswith("Z"):
|
|
78
|
+
last_violation_at = last_violation_at[:-1] + "+00:00"
|
|
79
|
+
last_violation_at = datetime.fromisoformat(last_violation_at)
|
|
80
|
+
|
|
60
81
|
return cls(
|
|
61
82
|
enabled=data.get("enabled", False),
|
|
62
83
|
activated_at=activated_at,
|
|
@@ -64,6 +85,9 @@ class OrchestratorMode(BaseModel):
|
|
|
64
85
|
enforcement_level=data.get("enforcement_level", "strict"),
|
|
65
86
|
auto_activated=data.get("auto_activated", False),
|
|
66
87
|
disabled_by_user=data.get("disabled_by_user", False),
|
|
88
|
+
violations=data.get("violations", 0),
|
|
89
|
+
last_violation_at=last_violation_at,
|
|
90
|
+
circuit_breaker_triggered=data.get("circuit_breaker_triggered", False),
|
|
67
91
|
)
|
|
68
92
|
|
|
69
93
|
|
|
@@ -214,4 +238,58 @@ class OrchestratorModeManager:
|
|
|
214
238
|
),
|
|
215
239
|
"auto_activated": mode.auto_activated,
|
|
216
240
|
"disabled_by_user": mode.disabled_by_user,
|
|
241
|
+
"violations": mode.violations,
|
|
242
|
+
"circuit_breaker_triggered": mode.circuit_breaker_triggered,
|
|
217
243
|
}
|
|
244
|
+
|
|
245
|
+
def increment_violation(self) -> OrchestratorMode:
|
|
246
|
+
"""
|
|
247
|
+
Increment violation counter and update timestamp.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Updated OrchestratorMode with incremented violations
|
|
251
|
+
"""
|
|
252
|
+
mode = self.load()
|
|
253
|
+
mode.violations += 1
|
|
254
|
+
mode.last_violation_at = datetime.now(timezone.utc)
|
|
255
|
+
|
|
256
|
+
# Trigger circuit breaker if threshold reached
|
|
257
|
+
if mode.violations >= 3:
|
|
258
|
+
mode.circuit_breaker_triggered = True
|
|
259
|
+
|
|
260
|
+
self.save(mode)
|
|
261
|
+
return mode
|
|
262
|
+
|
|
263
|
+
def reset_violations(self) -> OrchestratorMode:
|
|
264
|
+
"""
|
|
265
|
+
Reset violation counter and circuit breaker.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Updated OrchestratorMode with reset violations
|
|
269
|
+
"""
|
|
270
|
+
mode = self.load()
|
|
271
|
+
mode.violations = 0
|
|
272
|
+
mode.last_violation_at = None
|
|
273
|
+
mode.circuit_breaker_triggered = False
|
|
274
|
+
self.save(mode)
|
|
275
|
+
return mode
|
|
276
|
+
|
|
277
|
+
def is_circuit_breaker_triggered(self) -> bool:
|
|
278
|
+
"""
|
|
279
|
+
Check if circuit breaker is currently triggered.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
True if circuit breaker is active
|
|
283
|
+
"""
|
|
284
|
+
mode = self.load()
|
|
285
|
+
return mode.circuit_breaker_triggered
|
|
286
|
+
|
|
287
|
+
def get_violation_count(self) -> int:
|
|
288
|
+
"""
|
|
289
|
+
Get current violation count.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Number of violations in current session
|
|
293
|
+
"""
|
|
294
|
+
mode = self.load()
|
|
295
|
+
return mode.violations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlgraph
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.3
|
|
4
4
|
Summary: HTML is All You Need - Graph database on web standards
|
|
5
5
|
Project-URL: Homepage, https://github.com/Shakes-tzd/htmlgraph
|
|
6
6
|
Project-URL: Documentation, https://github.com/Shakes-tzd/htmlgraph#readme
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
htmlgraph/__init__.py,sha256=
|
|
1
|
+
htmlgraph/__init__.py,sha256=q7f-IQyiqw8sLurh9hC8rSIoomoMTcHd95SVh-lFpNE,4979
|
|
2
2
|
htmlgraph/agent_detection.py,sha256=PAYo7rU3N_y1cGRd7Dwjh5Wgu-QZ7ENblX_yOzU-gJ0,2749
|
|
3
3
|
htmlgraph/agent_registry.py,sha256=Usa_35by7p5gtpvHO7K3AcGimnorw-FzgPVa3cWTQ58,9448
|
|
4
4
|
htmlgraph/agents.py,sha256=Yvu6x1nOfrW2WhRTAHiCuSpvqoVJXx1Mkzd59kwEczw,33466
|
|
5
5
|
htmlgraph/analytics_index.py,sha256=ba6Y4H_NNOCxI_Z4U7wSgBFFairf4IJT74WcM1PoZuI,30594
|
|
6
6
|
htmlgraph/attribute_index.py,sha256=cBZUV4YfGnhh6lF59aYPCdNrRr1hK__BzSKCueSDUhQ,6593
|
|
7
|
-
htmlgraph/cli.py,sha256=
|
|
7
|
+
htmlgraph/cli.py,sha256=RvcJFIhJZSC1mgEALeyfBB6EzMhJ3MUZU7EIal76i30,170549
|
|
8
8
|
htmlgraph/context_analytics.py,sha256=CaLu0o2uSr6rlBM5YeaFZe7grgsy7_Hx10qdXuNcdao,11344
|
|
9
9
|
htmlgraph/converter.py,sha256=SHS_7F6DHCPhmpZcLl3wN2LgGGP4XWApZ9TkW-u5m_c,20556
|
|
10
10
|
htmlgraph/dashboard.html,sha256=rkZYjSnPbUuAm35QMpCNWemenYqQTdkkumCX2hhe8Dc,173537
|
|
@@ -26,7 +26,7 @@ htmlgraph/mcp_server.py,sha256=AeJeGJEtX5Dqu5rfhKfT5kwF2Oe8V8xCaP8BgMEh86s,24033
|
|
|
26
26
|
htmlgraph/models.py,sha256=yz5GrSRvQCC2Qy2ozOOfNm5Tw6mXaXZaACkajSjJnqg,79911
|
|
27
27
|
htmlgraph/orchestration.py,sha256=7_oQ4AlHOv14hs6RvLsatJzF-F5gkIbv1EOrmeGPhiw,9699
|
|
28
28
|
htmlgraph/orchestrator.py,sha256=6mj70vroWjmNmdvQ7jqqRSA9O1rFUNMUYDWPzqkizLk,19697
|
|
29
|
-
htmlgraph/orchestrator_mode.py,sha256=
|
|
29
|
+
htmlgraph/orchestrator_mode.py,sha256=F6LNZARqieQXUri3CRSq_lsqFbnVeGXJQPno1ZP47O4,9187
|
|
30
30
|
htmlgraph/orchestrator_validator.py,sha256=gd_KbHsRsNEIF7EElwcxbMYqOMlyeuYIZwClASp-L-E,4699
|
|
31
31
|
htmlgraph/parallel.py,sha256=BsyqGKWY_DkSRElBdvvAkWlL6stC9BPkyxjdPdggx_w,22418
|
|
32
32
|
htmlgraph/parser.py,sha256=JM2cSxEK_2shf_cW7otLToD-83jtEakX2_B4VUfqLGU,13567
|
|
@@ -87,13 +87,13 @@ htmlgraph/hooks/__init__.py,sha256=jL2HyCoFWQQ8l-4-EAlypDxPalNE3JBfDyELYWAg-g0,8
|
|
|
87
87
|
htmlgraph/hooks/event_tracker.py,sha256=KQcIWbhNJser6Tip87oUAPQJgUAAKESKE5ARQasLtCM,23301
|
|
88
88
|
htmlgraph/hooks/hooks-config.example.json,sha256=tXpk-U-FZzGOoNJK2uiDMbIHCYEHA794J-El0fBwkqg,197
|
|
89
89
|
htmlgraph/hooks/installer.py,sha256=nOctCFDEV7BEh7ZzxNY-apu1KZG0SHPMq74UPIOChqY,11756
|
|
90
|
-
htmlgraph/hooks/orchestrator.py,sha256=
|
|
90
|
+
htmlgraph/hooks/orchestrator.py,sha256=55xjmfg680e9PKcMkBmpbAmSXkZa33EQcZXlwPjDn50,17105
|
|
91
91
|
htmlgraph/hooks/orchestrator_reflector.py,sha256=j3kZge33m42CEUVYiufiz7mf7Qm4DimnsRZKjbpZStA,5154
|
|
92
92
|
htmlgraph/hooks/post-checkout.sh,sha256=Hsr5hqD54jisGbtqf7-Z-G_b6XNGcee_CZRYaKYzWzU,615
|
|
93
93
|
htmlgraph/hooks/post-commit.sh,sha256=if65jNGZnEWsZPq_iYDNYunrZ1cmjPUEUbh6_4vfpOE,511
|
|
94
94
|
htmlgraph/hooks/post-merge.sh,sha256=gq-EeFLhDUVp-J2jyWMBVFcB0pdmH54Wu1SW_Gn-s2I,541
|
|
95
|
-
htmlgraph/hooks/post_tool_use_failure.py,sha256=
|
|
96
|
-
htmlgraph/hooks/posttooluse.py,sha256=
|
|
95
|
+
htmlgraph/hooks/post_tool_use_failure.py,sha256=DHkJtuAOg5KSLfFZ1O-kePwaqmtNkbGQSEn4NplzvD8,8381
|
|
96
|
+
htmlgraph/hooks/posttooluse.py,sha256=imrRl29qsywRQW0-ruBbJq-FGYKCkvgbjSvis3U5j50,11261
|
|
97
97
|
htmlgraph/hooks/pre-commit.sh,sha256=gTpbnHIBFxpAl7-REhXoS0NI4Pmlqo9pQEMEngTAU_A,3865
|
|
98
98
|
htmlgraph/hooks/pre-push.sh,sha256=rNnkG8YmDtyk7OuJHOcbOYQR3MYFneaG6_w2X-Hl8Hs,660
|
|
99
99
|
htmlgraph/hooks/pretooluse.py,sha256=Q6wtU_IBIjCd22j7MvZrdd959TdAHx8OovIOnqkwCm0,9606
|
|
@@ -107,12 +107,12 @@ htmlgraph/services/claiming.py,sha256=HcrltEJKN72mxuD7fGuXWeh1U0vwhjMvhZcFc02Eiy
|
|
|
107
107
|
htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4bet1H4ZsDpI,7642
|
|
108
108
|
htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
|
|
109
109
|
htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
|
|
110
|
-
htmlgraph-0.20.
|
|
111
|
-
htmlgraph-0.20.
|
|
112
|
-
htmlgraph-0.20.
|
|
113
|
-
htmlgraph-0.20.
|
|
114
|
-
htmlgraph-0.20.
|
|
115
|
-
htmlgraph-0.20.
|
|
116
|
-
htmlgraph-0.20.
|
|
117
|
-
htmlgraph-0.20.
|
|
118
|
-
htmlgraph-0.20.
|
|
110
|
+
htmlgraph-0.20.3.data/data/htmlgraph/dashboard.html,sha256=rkZYjSnPbUuAm35QMpCNWemenYqQTdkkumCX2hhe8Dc,173537
|
|
111
|
+
htmlgraph-0.20.3.data/data/htmlgraph/styles.css,sha256=oDUSC8jG-V-hKojOBO9J88hxAeY2wJrBYTq0uCwX_Y4,7135
|
|
112
|
+
htmlgraph-0.20.3.data/data/htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4bet1H4ZsDpI,7642
|
|
113
|
+
htmlgraph-0.20.3.data/data/htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
|
|
114
|
+
htmlgraph-0.20.3.data/data/htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
|
|
115
|
+
htmlgraph-0.20.3.dist-info/METADATA,sha256=O9sf78ar2DpOXVtjErl-0bqyfsxPVjJLiRac-uDDKH0,7645
|
|
116
|
+
htmlgraph-0.20.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
117
|
+
htmlgraph-0.20.3.dist-info/entry_points.txt,sha256=EaUbjA_bbDwEO_XDLEGMeK8aQP-ZnHiUTkLshyKDyB8,98
|
|
118
|
+
htmlgraph-0.20.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|