htmlgraph 0.20.2__py3-none-any.whl → 0.20.4__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 +137 -1
- htmlgraph/hooks/orchestrator.py +47 -6
- htmlgraph/orchestrator_mode.py +78 -0
- htmlgraph/track_manager.py +38 -0
- {htmlgraph-0.20.2.dist-info → htmlgraph-0.20.4.dist-info}/METADATA +1 -1
- {htmlgraph-0.20.2.dist-info → htmlgraph-0.20.4.dist-info}/RECORD +14 -14
- {htmlgraph-0.20.2.data → htmlgraph-0.20.4.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.20.2.data → htmlgraph-0.20.4.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.2.data → htmlgraph-0.20.4.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.2.data → htmlgraph-0.20.4.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.2.data → htmlgraph-0.20.4.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.2.dist-info → htmlgraph-0.20.4.dist-info}/WHEEL +0 -0
- {htmlgraph-0.20.2.dist-info → htmlgraph-0.20.4.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
|
|
@@ -3200,6 +3241,64 @@ def cmd_track_plan(args: argparse.Namespace) -> None:
|
|
|
3200
3241
|
print(f"\nView plan: open {args.graph_dir}/tracks/{args.track_id}/plan.html")
|
|
3201
3242
|
|
|
3202
3243
|
|
|
3244
|
+
def cmd_track_show(args: argparse.Namespace) -> None:
|
|
3245
|
+
"""Show details of a track."""
|
|
3246
|
+
import json
|
|
3247
|
+
from pathlib import Path
|
|
3248
|
+
|
|
3249
|
+
from htmlgraph.track_manager import TrackManager
|
|
3250
|
+
|
|
3251
|
+
manager = TrackManager(args.graph_dir)
|
|
3252
|
+
|
|
3253
|
+
# Load the track
|
|
3254
|
+
track = manager.load_track(args.track_id)
|
|
3255
|
+
if not track:
|
|
3256
|
+
print(f"Error: Track '{args.track_id}' not found", file=sys.stderr)
|
|
3257
|
+
sys.exit(1)
|
|
3258
|
+
|
|
3259
|
+
if args.format == "json":
|
|
3260
|
+
data = {
|
|
3261
|
+
"id": track.id,
|
|
3262
|
+
"title": track.title,
|
|
3263
|
+
"description": track.description,
|
|
3264
|
+
"status": track.status,
|
|
3265
|
+
"priority": track.priority,
|
|
3266
|
+
"has_spec": track.has_spec,
|
|
3267
|
+
"has_plan": track.has_plan,
|
|
3268
|
+
"created": track.created.isoformat(),
|
|
3269
|
+
"updated": track.updated.isoformat(),
|
|
3270
|
+
"features": track.features,
|
|
3271
|
+
"sessions": track.sessions,
|
|
3272
|
+
}
|
|
3273
|
+
print(json.dumps(data, indent=2))
|
|
3274
|
+
else:
|
|
3275
|
+
# Determine if consolidated or directory-based
|
|
3276
|
+
is_consolidated = manager.is_consolidated(args.track_id)
|
|
3277
|
+
if is_consolidated:
|
|
3278
|
+
track_file = Path(args.graph_dir) / "tracks" / f"{args.track_id}.html"
|
|
3279
|
+
file_type = "single file (consolidated)"
|
|
3280
|
+
else:
|
|
3281
|
+
track_file = Path(args.graph_dir) / "tracks" / args.track_id / "index.html"
|
|
3282
|
+
file_type = "directory-based"
|
|
3283
|
+
|
|
3284
|
+
print(f"Track: {track.id}")
|
|
3285
|
+
print(f" Title: {track.title}")
|
|
3286
|
+
print(f" Description: {track.description}")
|
|
3287
|
+
print(f" Status: {track.status}")
|
|
3288
|
+
print(f" Priority: {track.priority}")
|
|
3289
|
+
print(f" Format: {file_type}")
|
|
3290
|
+
print(f" Has Spec: {'Yes' if track.has_spec else 'No'}")
|
|
3291
|
+
print(f" Has Plan: {'Yes' if track.has_plan else 'No'}")
|
|
3292
|
+
print(f" Created: {track.created.strftime('%Y-%m-%d %H:%M')}")
|
|
3293
|
+
print(f" Updated: {track.updated.strftime('%Y-%m-%d %H:%M')}")
|
|
3294
|
+
if track.features:
|
|
3295
|
+
print(f" Features: {', '.join(track.features)}")
|
|
3296
|
+
if track.sessions:
|
|
3297
|
+
print(f" Sessions: {', '.join(track.sessions)}")
|
|
3298
|
+
print(f"\nPath: {track_file}")
|
|
3299
|
+
print(f"View: open {track_file}")
|
|
3300
|
+
|
|
3301
|
+
|
|
3203
3302
|
def cmd_track_delete(args: argparse.Namespace) -> None:
|
|
3204
3303
|
"""Delete a track."""
|
|
3205
3304
|
import json
|
|
@@ -4207,6 +4306,16 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4207
4306
|
"--format", "-f", choices=["text", "json"], default="text", help="Output format"
|
|
4208
4307
|
)
|
|
4209
4308
|
|
|
4309
|
+
# track show
|
|
4310
|
+
track_show = track_subparsers.add_parser("show", help="Show track details")
|
|
4311
|
+
track_show.add_argument("track_id", help="Track ID to display")
|
|
4312
|
+
track_show.add_argument(
|
|
4313
|
+
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4314
|
+
)
|
|
4315
|
+
track_show.add_argument(
|
|
4316
|
+
"--format", "-f", choices=["text", "json"], default="text", help="Output format"
|
|
4317
|
+
)
|
|
4318
|
+
|
|
4210
4319
|
# track delete
|
|
4211
4320
|
track_delete = track_subparsers.add_parser("delete", help="Delete a track")
|
|
4212
4321
|
track_delete.add_argument("track_id", help="Track ID to delete")
|
|
@@ -4475,6 +4584,27 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4475
4584
|
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4476
4585
|
)
|
|
4477
4586
|
|
|
4587
|
+
# orchestrator set-level
|
|
4588
|
+
orchestrator_set_level = orchestrator_subparsers.add_parser(
|
|
4589
|
+
"set-level", help="Set enforcement level"
|
|
4590
|
+
)
|
|
4591
|
+
orchestrator_set_level.add_argument(
|
|
4592
|
+
"level",
|
|
4593
|
+
choices=["strict", "guidance"],
|
|
4594
|
+
help="Enforcement level to set",
|
|
4595
|
+
)
|
|
4596
|
+
orchestrator_set_level.add_argument(
|
|
4597
|
+
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4598
|
+
)
|
|
4599
|
+
|
|
4600
|
+
# orchestrator reset-violations
|
|
4601
|
+
orchestrator_reset_violations = orchestrator_subparsers.add_parser(
|
|
4602
|
+
"reset-violations", help="Reset violation counter and circuit breaker"
|
|
4603
|
+
)
|
|
4604
|
+
orchestrator_reset_violations.add_argument(
|
|
4605
|
+
"--graph-dir", "-g", default=".htmlgraph", help="Graph directory"
|
|
4606
|
+
)
|
|
4607
|
+
|
|
4478
4608
|
# install-gemini-extension
|
|
4479
4609
|
subparsers.add_parser(
|
|
4480
4610
|
"install-gemini-extension",
|
|
@@ -4563,6 +4693,8 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4563
4693
|
cmd_track_spec(args)
|
|
4564
4694
|
elif args.track_command == "plan":
|
|
4565
4695
|
cmd_track_plan(args)
|
|
4696
|
+
elif args.track_command == "show":
|
|
4697
|
+
cmd_track_show(args)
|
|
4566
4698
|
elif args.track_command == "delete":
|
|
4567
4699
|
cmd_track_delete(args)
|
|
4568
4700
|
else:
|
|
@@ -4609,7 +4741,7 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4609
4741
|
feature_parser.print_help()
|
|
4610
4742
|
sys.exit(1)
|
|
4611
4743
|
elif args.command == "analytics":
|
|
4612
|
-
from htmlgraph.
|
|
4744
|
+
from htmlgraph.analytics.cli import cmd_analytics
|
|
4613
4745
|
|
|
4614
4746
|
cmd_analytics(args)
|
|
4615
4747
|
elif args.command == "events":
|
|
@@ -4678,6 +4810,10 @@ For more help: https://github.com/Shakes-tzd/htmlgraph
|
|
|
4678
4810
|
cmd_orchestrator_disable(args)
|
|
4679
4811
|
elif args.orchestrator_command == "status":
|
|
4680
4812
|
cmd_orchestrator_status(args)
|
|
4813
|
+
elif args.orchestrator_command == "set-level":
|
|
4814
|
+
cmd_orchestrator_set_level(args)
|
|
4815
|
+
elif args.orchestrator_command == "reset-violations":
|
|
4816
|
+
cmd_orchestrator_reset_violations(args)
|
|
4681
4817
|
else:
|
|
4682
4818
|
orchestrator_parser.print_help()
|
|
4683
4819
|
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 {
|
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
|
htmlgraph/track_manager.py
CHANGED
|
@@ -338,6 +338,44 @@ class TrackManager:
|
|
|
338
338
|
track_id=track_id,
|
|
339
339
|
)
|
|
340
340
|
|
|
341
|
+
def load_track(self, track_id: str) -> Track | None:
|
|
342
|
+
"""
|
|
343
|
+
Load a track from disk.
|
|
344
|
+
|
|
345
|
+
Supports both consolidated (single .html file) and directory-based tracks.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
track_id: Track ID
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Track instance or None if not found
|
|
352
|
+
"""
|
|
353
|
+
track_path = self.get_track_path(track_id)
|
|
354
|
+
if track_path is None:
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
# For now, return a basic Track instance
|
|
358
|
+
# TODO: Parse HTML back to Track using justhtml
|
|
359
|
+
if track_path.is_file():
|
|
360
|
+
# Consolidated format - single file
|
|
361
|
+
return Track(
|
|
362
|
+
id=track_id,
|
|
363
|
+
title=f"Track {track_id}",
|
|
364
|
+
description="Loaded from consolidated format",
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
# Directory format - check for spec and plan
|
|
368
|
+
spec_exists = (track_path / "spec.html").exists()
|
|
369
|
+
plan_exists = (track_path / "plan.html").exists()
|
|
370
|
+
|
|
371
|
+
return Track(
|
|
372
|
+
id=track_id,
|
|
373
|
+
title=f"Track {track_id}",
|
|
374
|
+
description="Loaded from directory format",
|
|
375
|
+
has_spec=spec_exists,
|
|
376
|
+
has_plan=plan_exists,
|
|
377
|
+
)
|
|
378
|
+
|
|
341
379
|
def list_tracks(self) -> list[str]:
|
|
342
380
|
"""
|
|
343
381
|
List all track IDs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: htmlgraph
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.4
|
|
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=7HleUwg9jG5AvknycHtlXJl8D2JnyJGDTugHrdW95XU,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=wYoCjciu1cHqwZe84u2TgO7lS_YyTs4yX5ipLLUqUfo,173218
|
|
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
|
|
@@ -42,7 +42,7 @@ htmlgraph/spike_index.py,sha256=-qsJc92MtxJN1whHqV1SGU6HjXkKeG2Vsg7l3q2ReRg,4264
|
|
|
42
42
|
htmlgraph/styles.css,sha256=oDUSC8jG-V-hKojOBO9J88hxAeY2wJrBYTq0uCwX_Y4,7135
|
|
43
43
|
htmlgraph/sync_docs.py,sha256=CH1xB3OeOFG6Wmo_D_r1aiGWS1P1gE7hfjJySCEz7bc,9114
|
|
44
44
|
htmlgraph/track_builder.py,sha256=3Ldtv6OsBfrCqpXiUB13TOjuchJdgo4IuOYnFCaxc1k,6009
|
|
45
|
-
htmlgraph/track_manager.py,sha256=
|
|
45
|
+
htmlgraph/track_manager.py,sha256=HhCg0nFOy_dx2R0_Bb2IOx9dA2lPfg7dM-_O3vBmlRU,22445
|
|
46
46
|
htmlgraph/transcript.py,sha256=uk_I9tirypUdMRfKxJmoHuaySxlu1OWt7xy774KYZNw,31808
|
|
47
47
|
htmlgraph/transcript_analytics.py,sha256=O-T7SfM3gJIbGFNPlF3gmKjcy--NzPiJt03lvTC209w,24922
|
|
48
48
|
htmlgraph/types.py,sha256=EBxCbke3PhORsmIhHrD-f1XG6YPRX-1D5R1OXCp6xt8,8945
|
|
@@ -87,7 +87,7 @@ 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
|
|
@@ -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.4.data/data/htmlgraph/dashboard.html,sha256=rkZYjSnPbUuAm35QMpCNWemenYqQTdkkumCX2hhe8Dc,173537
|
|
111
|
+
htmlgraph-0.20.4.data/data/htmlgraph/styles.css,sha256=oDUSC8jG-V-hKojOBO9J88hxAeY2wJrBYTq0uCwX_Y4,7135
|
|
112
|
+
htmlgraph-0.20.4.data/data/htmlgraph/templates/AGENTS.md.template,sha256=f96h7V6ygwj-v-fanVI48eYMxR6t_se4bet1H4ZsDpI,7642
|
|
113
|
+
htmlgraph-0.20.4.data/data/htmlgraph/templates/CLAUDE.md.template,sha256=h1kG2hTX2XYig2KszsHBfzrwa_4Cfcq2Pj4SwqzeDlM,1984
|
|
114
|
+
htmlgraph-0.20.4.data/data/htmlgraph/templates/GEMINI.md.template,sha256=gAGzE53Avki87BM_otqy5HdcYCoLsHgqaKjVzNzPMX8,1622
|
|
115
|
+
htmlgraph-0.20.4.dist-info/METADATA,sha256=b_kFOPIXxYM38kXPcp30icc3idKjo9_CcnQh2QOjnOM,7645
|
|
116
|
+
htmlgraph-0.20.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
117
|
+
htmlgraph-0.20.4.dist-info/entry_points.txt,sha256=EaUbjA_bbDwEO_XDLEGMeK8aQP-ZnHiUTkLshyKDyB8,98
|
|
118
|
+
htmlgraph-0.20.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|