htmlgraph 0.26.24__py3-none-any.whl → 0.27.0__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 +23 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/cli.py +3 -3
- htmlgraph/analytics/cost_analyzer.py +5 -1
- htmlgraph/analytics/cross_session.py +13 -9
- htmlgraph/analytics/dependency.py +10 -6
- htmlgraph/analytics/work_type.py +15 -11
- htmlgraph/analytics_index.py +2 -1
- htmlgraph/api/main.py +114 -51
- htmlgraph/api/templates/dashboard-redesign.html +3 -3
- htmlgraph/api/templates/dashboard.html +3 -3
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/attribute_index.py +2 -1
- htmlgraph/builders/base.py +2 -1
- htmlgraph/builders/bug.py +2 -1
- htmlgraph/builders/chore.py +2 -1
- htmlgraph/builders/epic.py +2 -1
- htmlgraph/builders/feature.py +2 -1
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +2 -1
- htmlgraph/builders/spike.py +2 -1
- htmlgraph/builders/track.py +28 -1
- htmlgraph/cli/analytics.py +2 -1
- htmlgraph/cli/base.py +33 -8
- htmlgraph/cli/core.py +2 -1
- htmlgraph/cli/main.py +2 -1
- htmlgraph/cli/models.py +2 -1
- htmlgraph/cli/templates/cost_dashboard.py +2 -1
- htmlgraph/cli/work/__init__.py +76 -1
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +2 -1
- htmlgraph/cli/work/orchestration.py +2 -1
- htmlgraph/cli/work/report.py +2 -1
- htmlgraph/cli/work/sessions.py +2 -1
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +2 -1
- htmlgraph/collections/base.py +43 -4
- 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 +12 -7
- htmlgraph/collections/spike.py +6 -1
- htmlgraph/collections/task_delegation.py +7 -2
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +15 -10
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +11 -0
- htmlgraph/dependency_models.py +2 -1
- htmlgraph/edge_index.py +2 -1
- htmlgraph/event_log.py +81 -66
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +6 -2
- htmlgraph/hooks/cigs_pretool_enforcer.py +5 -1
- htmlgraph/hooks/drift_handler.py +3 -3
- htmlgraph/hooks/event_tracker.py +40 -61
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +92 -14
- htmlgraph/hooks/orchestrator_reflector.py +4 -0
- htmlgraph/hooks/post_tool_use_failure.py +7 -3
- htmlgraph/hooks/posttooluse.py +4 -0
- htmlgraph/hooks/prompt_analyzer.py +5 -5
- htmlgraph/hooks/session_handler.py +5 -2
- htmlgraph/hooks/session_summary.py +6 -2
- htmlgraph/hooks/validator.py +8 -4
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +2 -1
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +18 -1
- htmlgraph/operations/analytics.py +2 -1
- htmlgraph/operations/bootstrap.py +2 -1
- htmlgraph/operations/events.py +2 -1
- htmlgraph/operations/fastapi_server.py +2 -1
- htmlgraph/operations/hooks.py +2 -1
- htmlgraph/operations/initialization.py +2 -1
- htmlgraph/operations/server.py +2 -1
- htmlgraph/orchestration/__init__.py +4 -0
- htmlgraph/orchestration/claude_launcher.py +23 -20
- htmlgraph/orchestration/command_builder.py +2 -1
- htmlgraph/orchestration/headless_spawner.py +6 -2
- htmlgraph/orchestration/model_selection.py +7 -3
- htmlgraph/orchestration/plugin_manager.py +25 -21
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/claude.py +5 -2
- htmlgraph/orchestration/spawners/codex.py +12 -19
- htmlgraph/orchestration/spawners/copilot.py +13 -18
- htmlgraph/orchestration/spawners/gemini.py +12 -19
- htmlgraph/orchestration/subprocess_runner.py +6 -3
- htmlgraph/orchestration/task_coordination.py +16 -8
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/parallel.py +2 -1
- htmlgraph/query_builder.py +2 -1
- htmlgraph/reflection.py +2 -1
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +2 -1
- 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/server.py +21 -17
- htmlgraph/session_manager.py +1 -7
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/handoff.py +10 -3
- htmlgraph/system_prompts.py +2 -1
- htmlgraph/track_builder.py +14 -1
- htmlgraph/transcript.py +2 -1
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/METADATA +15 -1
- {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/RECORD +154 -117
- htmlgraph/sdk.py +0 -3430
- {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/dashboard.html +0 -0
- {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.26.24.data → htmlgraph-0.27.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/WHEEL +0 -0
- {htmlgraph-0.26.24.dist-info → htmlgraph-0.27.0.dist-info}/entry_points.txt +0 -0
htmlgraph/builders/insight.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Insight builder for creating session insight nodes.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Extends BaseBuilder with insight-specific methods for
|
|
|
5
7
|
session health scoring and pattern detection.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from typing import TYPE_CHECKING, Any
|
|
11
12
|
|
htmlgraph/builders/metric.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Metric builder for creating aggregated metric nodes.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Extends BaseBuilder with metric-specific methods for
|
|
|
5
7
|
time-series aggregation and trend analysis.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
from typing import TYPE_CHECKING, Any
|
htmlgraph/builders/pattern.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Pattern builder for creating workflow pattern nodes.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Extends BaseBuilder with pattern-specific methods for
|
|
|
5
7
|
tracking optimal and anti-pattern tool sequences.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
from typing import TYPE_CHECKING, Any
|
htmlgraph/builders/phase.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Phase builder for creating project phase nodes.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Extends BaseBuilder with phase-specific methods like
|
|
|
5
7
|
phase ordering and dependencies.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from datetime import date
|
|
11
12
|
from typing import TYPE_CHECKING, Any
|
htmlgraph/builders/spike.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Spike builder for creating spike investigation nodes.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Extends BaseBuilder with spike-specific methods like
|
|
|
5
7
|
spike_type and timebox_hours.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from typing import TYPE_CHECKING, Any
|
|
11
12
|
|
htmlgraph/builders/track.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Track Builder for agent-friendly track creation.
|
|
3
5
|
"""
|
|
4
6
|
|
|
5
|
-
from __future__ import annotations
|
|
6
7
|
|
|
7
8
|
import re
|
|
8
9
|
from datetime import datetime
|
|
@@ -617,6 +618,30 @@ class TrackBuilder:
|
|
|
617
618
|
Phase(id=f"phase-{i + 1}", name=phase_name, tasks=phase_tasks)
|
|
618
619
|
)
|
|
619
620
|
|
|
621
|
+
# Persist features to database from plan phases
|
|
622
|
+
features_created = 0
|
|
623
|
+
if phases:
|
|
624
|
+
for phase in phases:
|
|
625
|
+
for task in phase.tasks:
|
|
626
|
+
# Generate feature ID from task description
|
|
627
|
+
feature_id = generate_id(node_type="feat", title=task.description)
|
|
628
|
+
|
|
629
|
+
# Insert feature into database
|
|
630
|
+
success = self.sdk._db.insert_feature(
|
|
631
|
+
feature_id=feature_id,
|
|
632
|
+
feature_type="task", # Tasks from tracks are features of type "task"
|
|
633
|
+
title=task.description,
|
|
634
|
+
status="todo", # All new tasks start as "todo"
|
|
635
|
+
priority=self._priority, # Inherit priority from track
|
|
636
|
+
assigned_to=None, # No assignment initially
|
|
637
|
+
track_id=track_id,
|
|
638
|
+
description=f"Task from {phase.name}",
|
|
639
|
+
steps_total=0,
|
|
640
|
+
tags=None,
|
|
641
|
+
)
|
|
642
|
+
if success:
|
|
643
|
+
features_created += 1
|
|
644
|
+
|
|
620
645
|
if self._consolidated:
|
|
621
646
|
# Single-file format: everything in one index.html
|
|
622
647
|
track_file = self.sdk._directory / "tracks" / f"{track_id}.html"
|
|
@@ -677,6 +702,8 @@ class TrackBuilder:
|
|
|
677
702
|
if self._plan_phases:
|
|
678
703
|
total_tasks = sum(len(tasks) for _, tasks in self._plan_phases)
|
|
679
704
|
print(f" - Plan with {len(self._plan_phases)} phases, {total_tasks} tasks")
|
|
705
|
+
if features_created > 0:
|
|
706
|
+
print(f" - Persisted {features_created} features to database")
|
|
680
707
|
|
|
681
708
|
return track
|
|
682
709
|
|
htmlgraph/cli/analytics.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Analytics and reporting commands.
|
|
2
4
|
|
|
3
5
|
Commands for analytics and reporting:
|
|
@@ -7,7 +9,6 @@ Commands for analytics and reporting:
|
|
|
7
9
|
- sync-docs: Documentation synchronization
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
|
-
from __future__ import annotations
|
|
11
12
|
|
|
12
13
|
import argparse
|
|
13
14
|
import json
|
htmlgraph/cli/base.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""Base classes and utilities for CLI commands.
|
|
2
4
|
|
|
3
5
|
Provides:
|
|
@@ -10,7 +12,6 @@ Provides:
|
|
|
10
12
|
- save_traceback: Save full tracebacks to log files instead of console
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
|
-
from __future__ import annotations
|
|
14
15
|
|
|
15
16
|
import argparse
|
|
16
17
|
import json
|
|
@@ -464,11 +465,22 @@ class Formatter(Protocol):
|
|
|
464
465
|
|
|
465
466
|
|
|
466
467
|
def _serialize_json(value: Any) -> Any:
|
|
467
|
-
"""Recursively serialize value to JSON-compatible types.
|
|
468
|
+
"""Recursively serialize value to JSON-compatible types.
|
|
469
|
+
|
|
470
|
+
Sanitizes strings to remove control characters (newlines, tabs) that
|
|
471
|
+
would break JSON validity when using json.dumps().
|
|
472
|
+
"""
|
|
468
473
|
if value is None:
|
|
469
474
|
return None
|
|
470
475
|
if isinstance(value, (datetime, date)):
|
|
471
476
|
return value.isoformat()
|
|
477
|
+
if isinstance(value, str):
|
|
478
|
+
# Sanitize string: replace control characters with spaces
|
|
479
|
+
# This prevents newlines/tabs in JSON string values from breaking JSON validity
|
|
480
|
+
sanitized = value.replace("\n", " ").replace("\r", " ").replace("\t", " ")
|
|
481
|
+
# Collapse multiple spaces to single space
|
|
482
|
+
sanitized = " ".join(sanitized.split())
|
|
483
|
+
return sanitized
|
|
472
484
|
if hasattr(value, "model_dump") and callable(getattr(value, "model_dump")):
|
|
473
485
|
return _serialize_json(value.model_dump())
|
|
474
486
|
if hasattr(value, "to_dict") and callable(getattr(value, "to_dict")):
|
|
@@ -485,7 +497,9 @@ class JsonFormatter:
|
|
|
485
497
|
|
|
486
498
|
def output(self, result: CommandResult) -> None:
|
|
487
499
|
payload = result.json_data if result.json_data is not None else result.data
|
|
488
|
-
_console.print
|
|
500
|
+
# Use sys.stdout.write instead of _console.print to avoid Rich's line-wrapping
|
|
501
|
+
# which inserts literal newlines into JSON string values, breaking JSON validity
|
|
502
|
+
sys.stdout.write(json.dumps(_serialize_json(payload), indent=2) + "\n")
|
|
489
503
|
|
|
490
504
|
|
|
491
505
|
class TextFormatter:
|
|
@@ -507,16 +521,21 @@ class TextFormatter:
|
|
|
507
521
|
_console.print(result.data)
|
|
508
522
|
return
|
|
509
523
|
if isinstance(result.text, str):
|
|
510
|
-
|
|
524
|
+
# Use sys.stdout.write() for ANSI-formatted text to preserve colors when piped
|
|
525
|
+
# This bypasses Rich's reprocessing and ensures ANSI codes are preserved
|
|
526
|
+
sys.stdout.write(result.text)
|
|
527
|
+
if not result.text.endswith("\n"):
|
|
528
|
+
sys.stdout.write("\n")
|
|
511
529
|
return
|
|
512
|
-
|
|
530
|
+
# For text as list/iterable, write directly to preserve ANSI codes
|
|
531
|
+
sys.stdout.write("\n".join(str(line) for line in result.text) + "\n")
|
|
513
532
|
|
|
514
533
|
|
|
515
534
|
def get_formatter(format_name: str) -> Formatter:
|
|
516
|
-
"""Get formatter by name (json, text, plain)."""
|
|
535
|
+
"""Get formatter by name (json, text, plain, refs)."""
|
|
517
536
|
if format_name == "json":
|
|
518
537
|
return JsonFormatter()
|
|
519
|
-
if format_name in ("text", "plain", ""):
|
|
538
|
+
if format_name in ("text", "plain", "refs", ""):
|
|
520
539
|
return TextFormatter()
|
|
521
540
|
raise CommandError(f"Unknown output format '{format_name}'")
|
|
522
541
|
|
|
@@ -539,6 +558,9 @@ class BaseCommand(ABC):
|
|
|
539
558
|
self.graph_dir: str | None = None
|
|
540
559
|
self.agent: str | None = None
|
|
541
560
|
self._sdk: SDK | None = None
|
|
561
|
+
self.override_output_format: str | None = (
|
|
562
|
+
None # Allow commands to override formatter
|
|
563
|
+
)
|
|
542
564
|
|
|
543
565
|
@classmethod
|
|
544
566
|
@abstractmethod
|
|
@@ -648,7 +670,10 @@ class BaseCommand(ABC):
|
|
|
648
670
|
try:
|
|
649
671
|
self.validate()
|
|
650
672
|
result = self.execute()
|
|
651
|
-
|
|
673
|
+
# Allow commands to override output format
|
|
674
|
+
# (e.g., snapshot command's --output-format flag overrides global --format)
|
|
675
|
+
actual_format = self.override_output_format or output_format
|
|
676
|
+
formatter = get_formatter(actual_format)
|
|
652
677
|
formatter.output(result)
|
|
653
678
|
except CommandError as exc:
|
|
654
679
|
error_console = Console(file=sys.stderr)
|
htmlgraph/cli/core.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Infrastructure commands.
|
|
2
4
|
|
|
3
5
|
Commands for core infrastructure operations:
|
|
@@ -11,7 +13,6 @@ Commands for core infrastructure operations:
|
|
|
11
13
|
- Other utilities
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
|
-
from __future__ import annotations
|
|
15
16
|
|
|
16
17
|
import argparse
|
|
17
18
|
import sys
|
htmlgraph/cli/main.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Main entry point.
|
|
2
4
|
|
|
3
5
|
Entry point with argument parsing and command routing.
|
|
4
6
|
Keeps main() thin by delegating to command modules.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
|
-
from __future__ import annotations
|
|
8
9
|
|
|
9
10
|
import argparse
|
|
10
11
|
import sys
|
htmlgraph/cli/models.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Pydantic models for command filters and configuration.
|
|
2
4
|
|
|
3
5
|
This module provides type-safe models for validating command inputs:
|
|
@@ -5,7 +7,6 @@ This module provides type-safe models for validating command inputs:
|
|
|
5
7
|
- Configuration models for infrastructure commands (init, serve)
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
from typing import Literal
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HTML template for cost dashboard.
|
|
2
4
|
|
|
3
5
|
This module generates a beautiful, interactive HTML dashboard for cost analysis.
|
|
4
6
|
Separated from main CLI code for maintainability and testability.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
|
-
from __future__ import annotations
|
|
8
9
|
|
|
9
10
|
from datetime import datetime
|
|
10
11
|
from typing import TYPE_CHECKING, Any
|
htmlgraph/cli/work/__init__.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Work management commands.
|
|
2
4
|
|
|
3
5
|
Commands for managing work items:
|
|
@@ -9,7 +11,6 @@ Commands for managing work items:
|
|
|
9
11
|
- Other work-related operations
|
|
10
12
|
"""
|
|
11
13
|
|
|
12
|
-
from __future__ import annotations
|
|
13
14
|
|
|
14
15
|
from typing import TYPE_CHECKING
|
|
15
16
|
|
|
@@ -23,6 +24,7 @@ def register_commands(subparsers: _SubParsersAction) -> None:
|
|
|
23
24
|
Args:
|
|
24
25
|
subparsers: Subparser action from ArgumentParser.add_subparsers()
|
|
25
26
|
"""
|
|
27
|
+
from htmlgraph.cli.work.browse import BrowseCommand
|
|
26
28
|
from htmlgraph.cli.work.features import register_feature_commands
|
|
27
29
|
from htmlgraph.cli.work.orchestration import (
|
|
28
30
|
register_archive_commands,
|
|
@@ -31,6 +33,7 @@ def register_commands(subparsers: _SubParsersAction) -> None:
|
|
|
31
33
|
)
|
|
32
34
|
from htmlgraph.cli.work.report import register_report_commands
|
|
33
35
|
from htmlgraph.cli.work.sessions import register_session_commands
|
|
36
|
+
from htmlgraph.cli.work.snapshot import SnapshotCommand
|
|
34
37
|
from htmlgraph.cli.work.tracks import register_track_commands
|
|
35
38
|
|
|
36
39
|
# Register all command groups
|
|
@@ -42,8 +45,75 @@ def register_commands(subparsers: _SubParsersAction) -> None:
|
|
|
42
45
|
register_claude_commands(subparsers)
|
|
43
46
|
register_report_commands(subparsers)
|
|
44
47
|
|
|
48
|
+
# Snapshot command
|
|
49
|
+
snapshot_parser = subparsers.add_parser(
|
|
50
|
+
"snapshot",
|
|
51
|
+
help="Output current graph state with refs",
|
|
52
|
+
)
|
|
53
|
+
snapshot_parser.add_argument(
|
|
54
|
+
"--output-format",
|
|
55
|
+
choices=["refs", "json", "text"],
|
|
56
|
+
default="refs",
|
|
57
|
+
help="Output format (default: refs)",
|
|
58
|
+
)
|
|
59
|
+
snapshot_parser.add_argument(
|
|
60
|
+
"--type",
|
|
61
|
+
help="Filter by type (feature, track, bug, spike, chore, epic, all)",
|
|
62
|
+
)
|
|
63
|
+
snapshot_parser.add_argument(
|
|
64
|
+
"--status",
|
|
65
|
+
help="Filter by status (todo, in_progress, blocked, done, all)",
|
|
66
|
+
)
|
|
67
|
+
snapshot_parser.add_argument(
|
|
68
|
+
"--track",
|
|
69
|
+
help="Show only items in a specific track (by track ID or ref)",
|
|
70
|
+
)
|
|
71
|
+
snapshot_parser.add_argument(
|
|
72
|
+
"--active",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help="Show only TODO/IN_PROGRESS items (filters out metadata spikes)",
|
|
75
|
+
)
|
|
76
|
+
snapshot_parser.add_argument(
|
|
77
|
+
"--blockers",
|
|
78
|
+
action="store_true",
|
|
79
|
+
help="Show only critical/blocked items",
|
|
80
|
+
)
|
|
81
|
+
snapshot_parser.add_argument(
|
|
82
|
+
"--summary",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Show counts and progress summary instead of listing all items",
|
|
85
|
+
)
|
|
86
|
+
snapshot_parser.add_argument(
|
|
87
|
+
"--my-work",
|
|
88
|
+
action="store_true",
|
|
89
|
+
help="Show items assigned to current agent",
|
|
90
|
+
)
|
|
91
|
+
snapshot_parser.set_defaults(func=SnapshotCommand.from_args)
|
|
92
|
+
|
|
93
|
+
# Browse command
|
|
94
|
+
browse_parser = subparsers.add_parser(
|
|
95
|
+
"browse",
|
|
96
|
+
help="Open dashboard in browser",
|
|
97
|
+
)
|
|
98
|
+
browse_parser.add_argument(
|
|
99
|
+
"--port",
|
|
100
|
+
type=int,
|
|
101
|
+
default=8080,
|
|
102
|
+
help="Server port (default: 8080)",
|
|
103
|
+
)
|
|
104
|
+
browse_parser.add_argument(
|
|
105
|
+
"--query-type",
|
|
106
|
+
help="Filter by type (feature, track, bug, spike, chore, epic)",
|
|
107
|
+
)
|
|
108
|
+
browse_parser.add_argument(
|
|
109
|
+
"--query-status",
|
|
110
|
+
help="Filter by status (todo, in_progress, blocked, done)",
|
|
111
|
+
)
|
|
112
|
+
browse_parser.set_defaults(func=BrowseCommand.from_args)
|
|
113
|
+
|
|
45
114
|
|
|
46
115
|
# Re-export all command classes for backward compatibility
|
|
116
|
+
from htmlgraph.cli.work.browse import BrowseCommand
|
|
47
117
|
from htmlgraph.cli.work.features import (
|
|
48
118
|
FeatureClaimCommand,
|
|
49
119
|
FeatureCompleteCommand,
|
|
@@ -71,6 +141,7 @@ from htmlgraph.cli.work.sessions import (
|
|
|
71
141
|
SessionStartCommand,
|
|
72
142
|
SessionStartInfoCommand,
|
|
73
143
|
)
|
|
144
|
+
from htmlgraph.cli.work.snapshot import SnapshotCommand
|
|
74
145
|
from htmlgraph.cli.work.tracks import (
|
|
75
146
|
TrackDeleteCommand,
|
|
76
147
|
TrackListCommand,
|
|
@@ -89,6 +160,10 @@ __all__ = [
|
|
|
89
160
|
"SessionStartInfoCommand",
|
|
90
161
|
# Report commands
|
|
91
162
|
"SessionReportCommand",
|
|
163
|
+
# Snapshot commands
|
|
164
|
+
"SnapshotCommand",
|
|
165
|
+
# Browse commands
|
|
166
|
+
"BrowseCommand",
|
|
92
167
|
# Feature commands
|
|
93
168
|
"FeatureListCommand",
|
|
94
169
|
"FeatureCreateCommand",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""HtmlGraph CLI - Browse command for opening dashboard in browser."""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import webbrowser
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from htmlgraph.cli.base import BaseCommand, CommandResult
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BrowseCommand(BaseCommand):
|
|
17
|
+
"""Open the HtmlGraph dashboard in your default browser.
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
htmlgraph browse # Open dashboard
|
|
21
|
+
htmlgraph browse --port 8080 # Custom port
|
|
22
|
+
htmlgraph browse --query-type feature # Show only features
|
|
23
|
+
htmlgraph browse --query-status todo # Show only todo items
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
*,
|
|
29
|
+
port: int = 8080,
|
|
30
|
+
query_type: str | None = None,
|
|
31
|
+
query_status: str | None = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Initialize BrowseCommand.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
port: Server port (default: 8080)
|
|
37
|
+
query_type: Filter by type (feature, track, bug, spike, chore, epic)
|
|
38
|
+
query_status: Filter by status (todo, in_progress, blocked, done)
|
|
39
|
+
"""
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.port = port
|
|
42
|
+
self.query_type = query_type
|
|
43
|
+
self.query_status = query_status
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def from_args(cls, args: argparse.Namespace) -> BrowseCommand:
|
|
47
|
+
"""Create BrowseCommand from argparse arguments.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
args: Argparse namespace with command arguments
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
BrowseCommand instance
|
|
54
|
+
"""
|
|
55
|
+
return cls(
|
|
56
|
+
port=args.port,
|
|
57
|
+
query_type=args.query_type,
|
|
58
|
+
query_status=args.query_status,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def execute(self) -> CommandResult:
|
|
62
|
+
"""Execute the browse command.
|
|
63
|
+
|
|
64
|
+
Opens the dashboard in the default browser with optional query parameters.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
CommandResult with success status and URL
|
|
68
|
+
"""
|
|
69
|
+
# Build URL with query params
|
|
70
|
+
url = f"http://localhost:{self.port}"
|
|
71
|
+
|
|
72
|
+
params = []
|
|
73
|
+
if self.query_type:
|
|
74
|
+
params.append(f"type={self.query_type}")
|
|
75
|
+
if self.query_status:
|
|
76
|
+
params.append(f"status={self.query_status}")
|
|
77
|
+
|
|
78
|
+
if params:
|
|
79
|
+
url += "?" + "&".join(params)
|
|
80
|
+
|
|
81
|
+
# Check if server is running
|
|
82
|
+
try:
|
|
83
|
+
import requests # type: ignore[import-untyped]
|
|
84
|
+
|
|
85
|
+
response = requests.head(f"http://localhost:{self.port}", timeout=1)
|
|
86
|
+
response.raise_for_status()
|
|
87
|
+
except ImportError:
|
|
88
|
+
# requests module not available - try to open anyway with a warning
|
|
89
|
+
webbrowser.open(url)
|
|
90
|
+
return CommandResult(
|
|
91
|
+
data={"url": url},
|
|
92
|
+
text=f"Opening dashboard at {url}\n(Note: Could not verify server is running - install 'requests' for server checks)",
|
|
93
|
+
exit_code=0,
|
|
94
|
+
)
|
|
95
|
+
except Exception:
|
|
96
|
+
# Server not running or not responding
|
|
97
|
+
return CommandResult(
|
|
98
|
+
text=f"Dashboard server not running on port {self.port}.\nStart with: htmlgraph serve --port {self.port}",
|
|
99
|
+
exit_code=1,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Open browser
|
|
103
|
+
try:
|
|
104
|
+
webbrowser.open(url)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
return CommandResult(
|
|
107
|
+
text=f"Failed to open browser: {e}\nYou can manually visit: {url}",
|
|
108
|
+
exit_code=1,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return CommandResult(
|
|
112
|
+
data={"url": url},
|
|
113
|
+
text=f"Opening dashboard at {url}",
|
|
114
|
+
exit_code=0,
|
|
115
|
+
)
|
htmlgraph/cli/work/features.py
CHANGED
htmlgraph/cli/work/report.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""HtmlGraph CLI - Session report commands.
|
|
2
4
|
|
|
3
5
|
Commands for generating "What Did Claude Do?" reports:
|
|
@@ -7,7 +9,6 @@ THE killer feature that differentiates HtmlGraph - complete observability
|
|
|
7
9
|
of AI agent activities with cost attribution and tool usage analysis.
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
|
-
from __future__ import annotations
|
|
11
12
|
|
|
12
13
|
import argparse
|
|
13
14
|
import sqlite3
|