htmlgraph 0.20.1__py3-none-any.whl → 0.27.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htmlgraph/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/.htmlgraph/agents.json +72 -0
- htmlgraph/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/__init__.py +51 -1
- htmlgraph/__init__.pyi +123 -0
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/agent_registry.py +2 -1
- htmlgraph/analytics/__init__.py +8 -1
- htmlgraph/analytics/cli.py +86 -20
- htmlgraph/analytics/cost_analyzer.py +391 -0
- htmlgraph/analytics/cost_monitor.py +664 -0
- htmlgraph/analytics/cost_reporter.py +675 -0
- htmlgraph/analytics/cross_session.py +617 -0
- htmlgraph/analytics/dependency.py +10 -6
- htmlgraph/analytics/pattern_learning.py +771 -0
- htmlgraph/analytics/session_graph.py +707 -0
- htmlgraph/analytics/strategic/__init__.py +80 -0
- htmlgraph/analytics/strategic/cost_optimizer.py +611 -0
- htmlgraph/analytics/strategic/pattern_detector.py +876 -0
- htmlgraph/analytics/strategic/preference_manager.py +709 -0
- htmlgraph/analytics/strategic/suggestion_engine.py +747 -0
- htmlgraph/analytics/work_type.py +67 -27
- htmlgraph/analytics_index.py +53 -20
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/cost_alerts_websocket.py +416 -0
- htmlgraph/api/main.py +2498 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +1366 -0
- htmlgraph/api/templates/dashboard.html +794 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +1100 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +578 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +198 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/api/templates/partials/work-items.html +613 -0
- htmlgraph/api/websocket.py +538 -0
- htmlgraph/archive/__init__.py +24 -0
- htmlgraph/archive/bloom.py +234 -0
- htmlgraph/archive/fts.py +297 -0
- htmlgraph/archive/manager.py +583 -0
- htmlgraph/archive/search.py +244 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/attribute_index.py +2 -1
- htmlgraph/bounded_paths.py +539 -0
- htmlgraph/builders/base.py +57 -2
- htmlgraph/builders/bug.py +19 -3
- htmlgraph/builders/chore.py +19 -3
- htmlgraph/builders/epic.py +19 -3
- htmlgraph/builders/feature.py +27 -3
- htmlgraph/builders/insight.py +2 -1
- htmlgraph/builders/metric.py +2 -1
- htmlgraph/builders/pattern.py +2 -1
- htmlgraph/builders/phase.py +19 -3
- htmlgraph/builders/spike.py +29 -3
- htmlgraph/builders/track.py +42 -1
- htmlgraph/cigs/__init__.py +81 -0
- htmlgraph/cigs/autonomy.py +385 -0
- htmlgraph/cigs/cost.py +475 -0
- htmlgraph/cigs/messages_basic.py +472 -0
- htmlgraph/cigs/messaging.py +365 -0
- htmlgraph/cigs/models.py +771 -0
- htmlgraph/cigs/pattern_storage.py +427 -0
- htmlgraph/cigs/patterns.py +503 -0
- htmlgraph/cigs/posttool_analyzer.py +234 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cigs/tracker.py +317 -0
- htmlgraph/cli/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/cli/.htmlgraph/agents.json +72 -0
- htmlgraph/cli/.htmlgraph/htmlgraph.db +0 -0
- htmlgraph/cli/__init__.py +42 -0
- htmlgraph/cli/__main__.py +6 -0
- htmlgraph/cli/analytics.py +1424 -0
- htmlgraph/cli/base.py +685 -0
- htmlgraph/cli/constants.py +206 -0
- htmlgraph/cli/core.py +954 -0
- htmlgraph/cli/main.py +147 -0
- htmlgraph/cli/models.py +475 -0
- htmlgraph/cli/templates/__init__.py +1 -0
- htmlgraph/cli/templates/cost_dashboard.py +399 -0
- htmlgraph/cli/work/__init__.py +239 -0
- htmlgraph/cli/work/browse.py +115 -0
- htmlgraph/cli/work/features.py +568 -0
- htmlgraph/cli/work/orchestration.py +676 -0
- htmlgraph/cli/work/report.py +728 -0
- htmlgraph/cli/work/sessions.py +466 -0
- htmlgraph/cli/work/snapshot.py +559 -0
- htmlgraph/cli/work/tracks.py +486 -0
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +2 -0
- htmlgraph/collections/base.py +197 -14
- htmlgraph/collections/bug.py +2 -1
- htmlgraph/collections/chore.py +2 -1
- htmlgraph/collections/epic.py +2 -1
- htmlgraph/collections/feature.py +2 -1
- htmlgraph/collections/insight.py +2 -1
- htmlgraph/collections/metric.py +2 -1
- htmlgraph/collections/pattern.py +2 -1
- htmlgraph/collections/phase.py +2 -1
- htmlgraph/collections/session.py +194 -0
- htmlgraph/collections/spike.py +13 -2
- htmlgraph/collections/task_delegation.py +241 -0
- htmlgraph/collections/todo.py +14 -1
- htmlgraph/collections/traces.py +487 -0
- htmlgraph/config/cost_models.json +56 -0
- htmlgraph/config.py +190 -0
- htmlgraph/context_analytics.py +2 -1
- htmlgraph/converter.py +116 -7
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +2246 -248
- htmlgraph/dashboard.html.backup +6592 -0
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1788 -0
- htmlgraph/decorators.py +317 -0
- htmlgraph/dependency_models.py +2 -1
- htmlgraph/deploy.py +26 -27
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +717 -0
- htmlgraph/docs/README.md +532 -0
- htmlgraph/docs/__init__.py +77 -0
- htmlgraph/docs/docs_version.py +55 -0
- htmlgraph/docs/metadata.py +93 -0
- htmlgraph/docs/migrations.py +232 -0
- htmlgraph/docs/template_engine.py +143 -0
- htmlgraph/docs/templates/_sections/cli_reference.md.j2 +52 -0
- htmlgraph/docs/templates/_sections/core_concepts.md.j2 +29 -0
- htmlgraph/docs/templates/_sections/sdk_basics.md.j2 +69 -0
- htmlgraph/docs/templates/base_agents.md.j2 +78 -0
- htmlgraph/docs/templates/example_user_override.md.j2 +47 -0
- htmlgraph/docs/version_check.py +163 -0
- htmlgraph/edge_index.py +2 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +86 -37
- htmlgraph/event_migration.py +2 -1
- htmlgraph/file_watcher.py +12 -8
- htmlgraph/find_api.py +2 -1
- htmlgraph/git_events.py +67 -9
- htmlgraph/hooks/.htmlgraph/.session-warning-state.json +6 -0
- htmlgraph/hooks/.htmlgraph/agents.json +72 -0
- htmlgraph/hooks/.htmlgraph/index.sqlite +0 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/cigs_pretool_enforcer.py +354 -0
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +350 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +790 -99
- htmlgraph/hooks/git_commands.py +175 -0
- htmlgraph/hooks/installer.py +5 -1
- htmlgraph/hooks/orchestrator.py +327 -76
- htmlgraph/hooks/orchestrator_reflector.py +31 -4
- htmlgraph/hooks/post_tool_use_failure.py +32 -7
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/posttooluse.py +92 -19
- htmlgraph/hooks/pretooluse.py +527 -7
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +668 -0
- htmlgraph/hooks/session_summary.py +395 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_detection.py +202 -0
- htmlgraph/hooks/subagent_stop.py +369 -0
- htmlgraph/hooks/task_enforcer.py +99 -4
- htmlgraph/hooks/validator.py +212 -91
- htmlgraph/ids.py +2 -1
- htmlgraph/learning.py +125 -100
- htmlgraph/mcp_server.py +2 -1
- htmlgraph/models.py +217 -18
- htmlgraph/operations/README.md +62 -0
- htmlgraph/operations/__init__.py +79 -0
- htmlgraph/operations/analytics.py +339 -0
- htmlgraph/operations/bootstrap.py +289 -0
- htmlgraph/operations/events.py +244 -0
- htmlgraph/operations/fastapi_server.py +231 -0
- htmlgraph/operations/hooks.py +350 -0
- htmlgraph/operations/initialization.py +597 -0
- htmlgraph/operations/initialization.py.backup +228 -0
- htmlgraph/operations/server.py +303 -0
- htmlgraph/orchestration/__init__.py +58 -0
- htmlgraph/orchestration/claude_launcher.py +179 -0
- htmlgraph/orchestration/command_builder.py +72 -0
- htmlgraph/orchestration/headless_spawner.py +281 -0
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/orchestration/model_selection.py +327 -0
- htmlgraph/orchestration/plugin_manager.py +140 -0
- htmlgraph/orchestration/prompts.py +137 -0
- htmlgraph/orchestration/spawner_event_tracker.py +383 -0
- htmlgraph/orchestration/spawners/__init__.py +16 -0
- htmlgraph/orchestration/spawners/base.py +194 -0
- htmlgraph/orchestration/spawners/claude.py +173 -0
- htmlgraph/orchestration/spawners/codex.py +435 -0
- htmlgraph/orchestration/spawners/copilot.py +294 -0
- htmlgraph/orchestration/spawners/gemini.py +471 -0
- htmlgraph/orchestration/subprocess_runner.py +36 -0
- htmlgraph/{orchestration.py → orchestration/task_coordination.py} +16 -8
- htmlgraph/orchestration.md +563 -0
- htmlgraph/orchestrator-system-prompt-optimized.txt +863 -0
- htmlgraph/orchestrator.py +2 -1
- htmlgraph/orchestrator_config.py +357 -0
- htmlgraph/orchestrator_mode.py +115 -4
- htmlgraph/parallel.py +2 -1
- htmlgraph/parser.py +86 -6
- htmlgraph/path_query.py +608 -0
- htmlgraph/pattern_matcher.py +636 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/query_builder.py +2 -1
- htmlgraph/query_composer.py +509 -0
- htmlgraph/reflection.py +443 -0
- htmlgraph/refs.py +344 -0
- htmlgraph/repo_hash.py +512 -0
- htmlgraph/repositories/__init__.py +292 -0
- htmlgraph/repositories/analytics_repository.py +455 -0
- htmlgraph/repositories/analytics_repository_standard.py +628 -0
- htmlgraph/repositories/feature_repository.py +581 -0
- htmlgraph/repositories/feature_repository_htmlfile.py +668 -0
- htmlgraph/repositories/feature_repository_memory.py +607 -0
- htmlgraph/repositories/feature_repository_sqlite.py +858 -0
- htmlgraph/repositories/filter_service.py +620 -0
- htmlgraph/repositories/filter_service_standard.py +445 -0
- htmlgraph/repositories/shared_cache.py +621 -0
- htmlgraph/repositories/shared_cache_memory.py +395 -0
- htmlgraph/repositories/track_repository.py +552 -0
- htmlgraph/repositories/track_repository_htmlfile.py +619 -0
- htmlgraph/repositories/track_repository_memory.py +508 -0
- htmlgraph/repositories/track_repository_sqlite.py +711 -0
- htmlgraph/sdk/__init__.py +398 -0
- htmlgraph/sdk/__init__.pyi +14 -0
- htmlgraph/sdk/analytics/__init__.py +19 -0
- htmlgraph/sdk/analytics/engine.py +155 -0
- htmlgraph/sdk/analytics/helpers.py +178 -0
- htmlgraph/sdk/analytics/registry.py +109 -0
- htmlgraph/sdk/base.py +484 -0
- htmlgraph/sdk/constants.py +216 -0
- htmlgraph/sdk/core.pyi +308 -0
- htmlgraph/sdk/discovery.py +120 -0
- htmlgraph/sdk/help/__init__.py +12 -0
- htmlgraph/sdk/help/mixin.py +699 -0
- htmlgraph/sdk/mixins/__init__.py +15 -0
- htmlgraph/sdk/mixins/attribution.py +113 -0
- htmlgraph/sdk/mixins/mixin.py +410 -0
- htmlgraph/sdk/operations/__init__.py +12 -0
- htmlgraph/sdk/operations/mixin.py +427 -0
- htmlgraph/sdk/orchestration/__init__.py +17 -0
- htmlgraph/sdk/orchestration/coordinator.py +203 -0
- htmlgraph/sdk/orchestration/spawner.py +204 -0
- htmlgraph/sdk/planning/__init__.py +19 -0
- htmlgraph/sdk/planning/bottlenecks.py +93 -0
- htmlgraph/sdk/planning/mixin.py +211 -0
- htmlgraph/sdk/planning/parallel.py +186 -0
- htmlgraph/sdk/planning/queue.py +210 -0
- htmlgraph/sdk/planning/recommendations.py +87 -0
- htmlgraph/sdk/planning/smart_planning.py +319 -0
- htmlgraph/sdk/session/__init__.py +19 -0
- htmlgraph/sdk/session/continuity.py +57 -0
- htmlgraph/sdk/session/handoff.py +110 -0
- htmlgraph/sdk/session/info.py +309 -0
- htmlgraph/sdk/session/manager.py +103 -0
- htmlgraph/sdk/strategic/__init__.py +26 -0
- htmlgraph/sdk/strategic/mixin.py +563 -0
- htmlgraph/server.py +295 -107
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +285 -3
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/session_warning.py +2 -1
- htmlgraph/sessions/__init__.py +23 -0
- htmlgraph/sessions/handoff.py +756 -0
- htmlgraph/system_prompts.py +450 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +33 -1
- htmlgraph/track_manager.py +38 -0
- htmlgraph/transcript.py +18 -5
- htmlgraph/validation.py +115 -0
- htmlgraph/watch.py +2 -1
- htmlgraph/work_type_utils.py +2 -1
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/dashboard.html +2246 -248
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/METADATA +95 -64
- htmlgraph-0.27.5.dist-info/RECORD +337 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/entry_points.txt +1 -1
- htmlgraph/cli.py +0 -4839
- htmlgraph/sdk.py +0 -2359
- htmlgraph-0.20.1.dist-info/RECORD +0 -118
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.20.1.data → htmlgraph-0.27.5.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.20.1.dist-info → htmlgraph-0.27.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
<!-- Orchestration View Component
|
|
2
|
+
Displays agent delegation chains and coordination topology.
|
|
3
|
+
Fetches from /api/orchestration endpoint.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<div id="orchestration-view" class="orchestration-container">
|
|
7
|
+
<div class="orchestration-header">
|
|
8
|
+
<h2>Agent Orchestration</h2>
|
|
9
|
+
<p class="subtitle">Delegation chains and agent coordination</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="orchestration-metrics">
|
|
13
|
+
<div class="metric-card">
|
|
14
|
+
<div class="metric-label">Total Delegations</div>
|
|
15
|
+
<div class="metric-value" id="delegation-count">0</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="metric-card">
|
|
18
|
+
<div class="metric-label">Unique Agents</div>
|
|
19
|
+
<div class="metric-value" id="unique-agents">0</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="metric-card">
|
|
22
|
+
<div class="metric-label">Active Agents</div>
|
|
23
|
+
<div class="metric-value" id="agent-list">—</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="orchestration-chains" id="delegation-chains">
|
|
28
|
+
<div class="empty-state">
|
|
29
|
+
<p>No delegation events recorded yet.</p>
|
|
30
|
+
<p class="hint">Delegation events will appear here when Task() calls are made.</p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="orchestration-legend">
|
|
35
|
+
<div class="legend-item">
|
|
36
|
+
<span class="status-badge pending"></span>
|
|
37
|
+
<span>Pending</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="legend-item">
|
|
40
|
+
<span class="status-badge accepted"></span>
|
|
41
|
+
<span>Accepted</span>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="legend-item">
|
|
44
|
+
<span class="status-badge completed"></span>
|
|
45
|
+
<span>Completed</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="legend-item">
|
|
48
|
+
<span class="status-badge failed"></span>
|
|
49
|
+
<span>Failed</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.orchestration-container {
|
|
56
|
+
padding: 2rem;
|
|
57
|
+
background: var(--bg-secondary);
|
|
58
|
+
border: 1px solid var(--border);
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.orchestration-header {
|
|
63
|
+
margin-bottom: 2rem;
|
|
64
|
+
border-bottom: 2px solid var(--border);
|
|
65
|
+
padding-bottom: 1rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.orchestration-header h2 {
|
|
69
|
+
font-size: 1.5rem;
|
|
70
|
+
color: var(--text-primary);
|
|
71
|
+
margin-bottom: 0.5rem;
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.orchestration-header .subtitle {
|
|
76
|
+
color: var(--text-secondary);
|
|
77
|
+
font-size: 0.9rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.orchestration-metrics {
|
|
81
|
+
display: grid;
|
|
82
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
83
|
+
gap: 1rem;
|
|
84
|
+
margin-bottom: 2rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.metric-card {
|
|
88
|
+
background: var(--bg-tertiary);
|
|
89
|
+
border: 1px solid var(--border);
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
padding: 1.5rem;
|
|
92
|
+
text-align: center;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.metric-label {
|
|
96
|
+
color: var(--text-secondary);
|
|
97
|
+
font-size: 0.9rem;
|
|
98
|
+
margin-bottom: 0.5rem;
|
|
99
|
+
text-transform: uppercase;
|
|
100
|
+
letter-spacing: 0.05em;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.metric-value {
|
|
104
|
+
font-size: 2rem;
|
|
105
|
+
font-weight: 700;
|
|
106
|
+
color: var(--accent);
|
|
107
|
+
font-family: 'JetBrains Mono', monospace;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.orchestration-chains {
|
|
111
|
+
margin-bottom: 2rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.empty-state {
|
|
115
|
+
text-align: center;
|
|
116
|
+
padding: 3rem 1rem;
|
|
117
|
+
color: var(--text-secondary);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.empty-state p {
|
|
121
|
+
margin-bottom: 0.5rem;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.empty-state .hint {
|
|
125
|
+
font-size: 0.85rem;
|
|
126
|
+
color: var(--text-muted);
|
|
127
|
+
font-style: italic;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.delegation-chain {
|
|
131
|
+
margin-bottom: 1.5rem;
|
|
132
|
+
border-left: 3px solid var(--status-active);
|
|
133
|
+
padding-left: 1rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.chain-from-agent {
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
color: var(--text-primary);
|
|
139
|
+
font-size: 1rem;
|
|
140
|
+
margin-bottom: 0.5rem;
|
|
141
|
+
padding: 0.75rem;
|
|
142
|
+
background: rgba(41, 121, 255, 0.1);
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
border-left: 3px solid var(--status-active);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.chain-delegations {
|
|
148
|
+
margin-top: 0.5rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.delegation-item {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: flex-start;
|
|
154
|
+
gap: 1rem;
|
|
155
|
+
padding: 0.75rem;
|
|
156
|
+
margin-bottom: 0.5rem;
|
|
157
|
+
background: var(--bg-tertiary);
|
|
158
|
+
border-radius: 3px;
|
|
159
|
+
border-left: 3px solid var(--status-active);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.delegation-arrow {
|
|
163
|
+
color: var(--text-secondary);
|
|
164
|
+
font-weight: bold;
|
|
165
|
+
min-width: 1rem;
|
|
166
|
+
text-align: center;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.delegation-details {
|
|
170
|
+
flex: 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.delegation-to-agent {
|
|
174
|
+
font-weight: 600;
|
|
175
|
+
color: var(--text-primary);
|
|
176
|
+
font-size: 0.95rem;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.delegation-task {
|
|
180
|
+
color: var(--text-secondary);
|
|
181
|
+
font-size: 0.85rem;
|
|
182
|
+
margin: 0.25rem 0;
|
|
183
|
+
font-family: 'JetBrains Mono', monospace;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.delegation-timestamp {
|
|
187
|
+
color: var(--text-muted);
|
|
188
|
+
font-size: 0.8rem;
|
|
189
|
+
margin-top: 0.25rem;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.status-badge {
|
|
193
|
+
display: inline-block;
|
|
194
|
+
width: 0.75rem;
|
|
195
|
+
height: 0.75rem;
|
|
196
|
+
border-radius: 50%;
|
|
197
|
+
margin-right: 0.5rem;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.status-badge.pending {
|
|
201
|
+
background: var(--status-todo);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.status-badge.accepted {
|
|
205
|
+
background: var(--status-active);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.status-badge.completed {
|
|
209
|
+
background: var(--status-done);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.status-badge.failed {
|
|
213
|
+
background: var(--status-blocked);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.delegation-status {
|
|
217
|
+
display: inline-flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
gap: 0.5rem;
|
|
220
|
+
font-size: 0.8rem;
|
|
221
|
+
padding: 0.25rem 0.5rem;
|
|
222
|
+
background: var(--bg-primary);
|
|
223
|
+
border-radius: 3px;
|
|
224
|
+
margin-left: auto;
|
|
225
|
+
text-transform: uppercase;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.orchestration-legend {
|
|
229
|
+
display: flex;
|
|
230
|
+
gap: 1.5rem;
|
|
231
|
+
padding: 1rem;
|
|
232
|
+
background: var(--bg-tertiary);
|
|
233
|
+
border-radius: 4px;
|
|
234
|
+
flex-wrap: wrap;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.legend-item {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 0.5rem;
|
|
241
|
+
font-size: 0.85rem;
|
|
242
|
+
color: var(--text-secondary);
|
|
243
|
+
}
|
|
244
|
+
</style>
|
|
245
|
+
|
|
246
|
+
<script>
|
|
247
|
+
function renderOrchestrationView(data) {
|
|
248
|
+
document.getElementById('delegation-count').textContent = data.delegation_count;
|
|
249
|
+
document.getElementById('unique-agents').textContent = data.unique_agents;
|
|
250
|
+
document.getElementById('agent-list').textContent = data.agents.join(', ') || '—';
|
|
251
|
+
|
|
252
|
+
const chainsContainer = document.getElementById('delegation-chains');
|
|
253
|
+
|
|
254
|
+
if (data.delegation_count === 0) {
|
|
255
|
+
chainsContainer.textContent = '';
|
|
256
|
+
const emptyDiv = document.createElement('div');
|
|
257
|
+
emptyDiv.className = 'empty-state';
|
|
258
|
+
emptyDiv.innerHTML = '<p>No delegation events recorded yet.</p><p class="hint">Delegation events will appear here when Task() calls are made.</p>';
|
|
259
|
+
chainsContainer.appendChild(emptyDiv);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
chainsContainer.textContent = '';
|
|
264
|
+
|
|
265
|
+
for (const fromAgent in data.delegation_chains) {
|
|
266
|
+
const delegations = data.delegation_chains[fromAgent];
|
|
267
|
+
const chainDiv = document.createElement('div');
|
|
268
|
+
chainDiv.className = 'delegation-chain';
|
|
269
|
+
|
|
270
|
+
const fromAgentDiv = document.createElement('div');
|
|
271
|
+
fromAgentDiv.className = 'chain-from-agent';
|
|
272
|
+
fromAgentDiv.textContent = fromAgent;
|
|
273
|
+
chainDiv.appendChild(fromAgentDiv);
|
|
274
|
+
|
|
275
|
+
const delegationsDiv = document.createElement('div');
|
|
276
|
+
delegationsDiv.className = 'chain-delegations';
|
|
277
|
+
|
|
278
|
+
for (const delegation of delegations) {
|
|
279
|
+
const itemDiv = document.createElement('div');
|
|
280
|
+
itemDiv.className = 'delegation-item';
|
|
281
|
+
|
|
282
|
+
const arrowDiv = document.createElement('div');
|
|
283
|
+
arrowDiv.className = 'delegation-arrow';
|
|
284
|
+
arrowDiv.textContent = '↓';
|
|
285
|
+
itemDiv.appendChild(arrowDiv);
|
|
286
|
+
|
|
287
|
+
const detailsDiv = document.createElement('div');
|
|
288
|
+
detailsDiv.className = 'delegation-details';
|
|
289
|
+
|
|
290
|
+
const toAgentDiv = document.createElement('div');
|
|
291
|
+
toAgentDiv.className = 'delegation-to-agent';
|
|
292
|
+
toAgentDiv.textContent = delegation.to_agent;
|
|
293
|
+
detailsDiv.appendChild(toAgentDiv);
|
|
294
|
+
|
|
295
|
+
const taskDiv = document.createElement('div');
|
|
296
|
+
taskDiv.className = 'delegation-task';
|
|
297
|
+
taskDiv.textContent = 'Task: ' + delegation.task;
|
|
298
|
+
detailsDiv.appendChild(taskDiv);
|
|
299
|
+
|
|
300
|
+
const timeDiv = document.createElement('div');
|
|
301
|
+
timeDiv.className = 'delegation-timestamp';
|
|
302
|
+
try {
|
|
303
|
+
const date = new Date(delegation.timestamp);
|
|
304
|
+
timeDiv.textContent = date.toLocaleString();
|
|
305
|
+
} catch {
|
|
306
|
+
timeDiv.textContent = delegation.timestamp || 'N/A';
|
|
307
|
+
}
|
|
308
|
+
detailsDiv.appendChild(timeDiv);
|
|
309
|
+
|
|
310
|
+
itemDiv.appendChild(detailsDiv);
|
|
311
|
+
|
|
312
|
+
const statusDiv = document.createElement('div');
|
|
313
|
+
statusDiv.className = 'delegation-status';
|
|
314
|
+
const badgeSpan = document.createElement('span');
|
|
315
|
+
badgeSpan.className = 'status-badge ' + (delegation.status || 'pending');
|
|
316
|
+
statusDiv.appendChild(badgeSpan);
|
|
317
|
+
const statusText = document.createTextNode((delegation.status || 'pending').charAt(0).toUpperCase() + (delegation.status || 'pending').slice(1));
|
|
318
|
+
statusDiv.appendChild(statusText);
|
|
319
|
+
itemDiv.appendChild(statusDiv);
|
|
320
|
+
|
|
321
|
+
delegationsDiv.appendChild(itemDiv);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
chainDiv.appendChild(delegationsDiv);
|
|
325
|
+
chainsContainer.appendChild(chainDiv);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function loadOrchestrationView() {
|
|
330
|
+
try {
|
|
331
|
+
const response = await fetch('/api/orchestration');
|
|
332
|
+
if (!response.ok) {
|
|
333
|
+
console.warn('Orchestration view not available');
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const data = await response.json();
|
|
337
|
+
renderOrchestrationView(data);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error('Failed to load orchestration view:', error);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (document.readyState === 'loading') {
|
|
344
|
+
document.addEventListener('DOMContentLoaded', loadOrchestrationView);
|
|
345
|
+
} else {
|
|
346
|
+
loadOrchestrationView();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
setInterval(loadOrchestrationView, 10000);
|
|
350
|
+
</script>
|
htmlgraph/track_builder.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Track Builder and Collection for agent-friendly track creation.
|
|
3
5
|
|
|
@@ -5,7 +7,6 @@ Note: TrackBuilder has been moved to builders/track.py for better organization.
|
|
|
5
7
|
This module now provides TrackCollection and re-exports TrackBuilder for backward compatibility.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
|
-
from __future__ import annotations
|
|
9
10
|
|
|
10
11
|
from collections.abc import Iterator
|
|
11
12
|
from contextlib import contextmanager
|
|
@@ -31,6 +32,18 @@ class TrackCollection:
|
|
|
31
32
|
self.collection_name = "tracks" # For backward compatibility
|
|
32
33
|
self.id_prefix = "track"
|
|
33
34
|
self._graph: HtmlGraph | None = None # Lazy-loaded
|
|
35
|
+
self._ref_manager: Any = None # Set by SDK during initialization
|
|
36
|
+
|
|
37
|
+
def set_ref_manager(self, ref_manager: Any) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Set the ref manager for this collection.
|
|
40
|
+
|
|
41
|
+
Called by SDK during initialization to enable short ref support.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
ref_manager: RefManager instance from SDK
|
|
45
|
+
"""
|
|
46
|
+
self._ref_manager = ref_manager
|
|
34
47
|
|
|
35
48
|
def _ensure_graph(self) -> HtmlGraph:
|
|
36
49
|
"""Lazy-load the graph for tracks with multi-pattern support."""
|
|
@@ -113,6 +126,25 @@ class TrackCollection:
|
|
|
113
126
|
# Auto-save on exit
|
|
114
127
|
graph.update(node)
|
|
115
128
|
|
|
129
|
+
def create(self, title: str) -> TrackBuilder:
|
|
130
|
+
"""
|
|
131
|
+
Create a new track with fluent interface.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
title: Track title
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
TrackBuilder for method chaining
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
track = sdk.tracks.create("Multi-Agent Collaboration") \\
|
|
141
|
+
.set_priority("high") \\
|
|
142
|
+
.save()
|
|
143
|
+
"""
|
|
144
|
+
builder = TrackBuilder(self._sdk)
|
|
145
|
+
builder._title = title
|
|
146
|
+
return builder
|
|
147
|
+
|
|
116
148
|
def builder(self) -> TrackBuilder:
|
|
117
149
|
"""
|
|
118
150
|
Create a new track builder with fluent interface.
|
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.
|
htmlgraph/transcript.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Claude Code Transcript Integration.
|
|
3
5
|
|
|
@@ -21,7 +23,6 @@ References:
|
|
|
21
23
|
- https://github.com/simonw/claude-code-transcripts
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
from __future__ import annotations
|
|
25
26
|
|
|
26
27
|
import json
|
|
27
28
|
from collections.abc import Iterator
|
|
@@ -588,14 +589,26 @@ class TranscriptReader:
|
|
|
588
589
|
Returns:
|
|
589
590
|
List of TranscriptSession objects, newest first
|
|
590
591
|
"""
|
|
592
|
+
from datetime import timezone
|
|
593
|
+
|
|
594
|
+
def normalize_dt(dt: datetime | None) -> datetime:
|
|
595
|
+
"""Normalize datetime to UTC for comparison."""
|
|
596
|
+
if dt is None:
|
|
597
|
+
return datetime.min.replace(tzinfo=timezone.utc)
|
|
598
|
+
if dt.tzinfo is None:
|
|
599
|
+
# Assume naive datetimes are UTC
|
|
600
|
+
return dt.replace(tzinfo=timezone.utc)
|
|
601
|
+
return dt.astimezone(timezone.utc)
|
|
602
|
+
|
|
591
603
|
sessions: list[TranscriptSession] = []
|
|
592
604
|
|
|
593
605
|
for path in self.list_transcript_files(project_path):
|
|
594
606
|
session = self.read_transcript(path)
|
|
595
607
|
|
|
596
608
|
# Filter by time
|
|
597
|
-
if since and session.started_at
|
|
598
|
-
|
|
609
|
+
if since and session.started_at:
|
|
610
|
+
if normalize_dt(session.started_at) < normalize_dt(since):
|
|
611
|
+
continue
|
|
599
612
|
|
|
600
613
|
sessions.append(session)
|
|
601
614
|
|
|
@@ -604,8 +617,8 @@ class TranscriptReader:
|
|
|
604
617
|
if deduplicate and sessions:
|
|
605
618
|
sessions = self._deduplicate_context_snapshots(sessions)
|
|
606
619
|
|
|
607
|
-
# Sort by start time, newest first
|
|
608
|
-
sessions.sort(key=lambda s: s.started_at
|
|
620
|
+
# Sort by start time, newest first (normalize for comparison)
|
|
621
|
+
sessions.sort(key=lambda s: normalize_dt(s.started_at), reverse=True)
|
|
609
622
|
|
|
610
623
|
if limit:
|
|
611
624
|
sessions = sessions[:limit]
|
htmlgraph/validation.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rich CLI validation error formatting and utilities.
|
|
3
|
+
|
|
4
|
+
Provides beautiful error display for Pydantic validation failures.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
|
|
10
|
+
from pydantic import ValidationError
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def display_validation_error(error: ValidationError) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Display a Pydantic validation error in Rich format.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
error: ValidationError from Pydantic model validation
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
try:
|
|
27
|
+
input_data = FeatureCreateInput(title="", priority="invalid")
|
|
28
|
+
except ValidationError as e:
|
|
29
|
+
display_validation_error(e)
|
|
30
|
+
"""
|
|
31
|
+
console.print("[red]✗ Validation Error[/red]")
|
|
32
|
+
console.print()
|
|
33
|
+
|
|
34
|
+
for err in error.errors():
|
|
35
|
+
field = ".".join(str(loc) for loc in err["loc"])
|
|
36
|
+
msg = err["msg"]
|
|
37
|
+
error_type = err["type"]
|
|
38
|
+
|
|
39
|
+
# Color code by error type for better UX
|
|
40
|
+
if "at least" in msg.lower() or "at most" in msg.lower():
|
|
41
|
+
# Length/range constraint violation
|
|
42
|
+
console.print(f" [yellow]{field}:[/yellow] {msg}")
|
|
43
|
+
elif "must be" in msg.lower() or "is not valid" in msg.lower():
|
|
44
|
+
# Type/value constraint violation
|
|
45
|
+
console.print(f" [cyan]{field}:[/cyan] {msg}")
|
|
46
|
+
elif error_type == "string_pattern":
|
|
47
|
+
# Pattern validation failed
|
|
48
|
+
console.print(f" [magenta]{field}:[/magenta] {msg}")
|
|
49
|
+
else:
|
|
50
|
+
# Generic validation error
|
|
51
|
+
console.print(f" [red]{field}:[/red] {msg}")
|
|
52
|
+
|
|
53
|
+
console.print()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def validate_input(
|
|
57
|
+
validator_class: type[T],
|
|
58
|
+
**kwargs: object,
|
|
59
|
+
) -> T | None:
|
|
60
|
+
"""
|
|
61
|
+
Validate input and display rich error messages on failure.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
validator_class: Pydantic model class to validate against
|
|
65
|
+
**kwargs: Input data to validate
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Validated model instance, or None if validation failed
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
input_data = validate_input(FeatureCreateInput, title="My Feature", priority="high")
|
|
72
|
+
if input_data is None:
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
# Use input_data.title, input_data.priority, etc.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
return validator_class(**kwargs)
|
|
78
|
+
except ValidationError as e:
|
|
79
|
+
display_validation_error(e)
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def wrap_command_validation(func: Callable[..., None]) -> Callable[..., None]:
|
|
84
|
+
"""
|
|
85
|
+
Decorator to wrap CLI command functions with validation error handling.
|
|
86
|
+
|
|
87
|
+
This decorator catches ValidationError and displays it in Rich format,
|
|
88
|
+
then exits gracefully.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
func: CLI command function
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Wrapped function with validation error handling
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
@wrap_command_validation
|
|
98
|
+
def cmd_feature_create(args: argparse.Namespace) -> None:
|
|
99
|
+
input_data = FeatureCreateInput(
|
|
100
|
+
title=args.title,
|
|
101
|
+
priority=args.priority
|
|
102
|
+
)
|
|
103
|
+
# Rest of command logic
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def wrapper(*args: object, **kwargs: object) -> None:
|
|
107
|
+
import sys
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
return func(*args, **kwargs)
|
|
111
|
+
except ValidationError as e:
|
|
112
|
+
display_validation_error(e)
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
return wrapper
|
htmlgraph/watch.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
File-change watcher for HtmlGraph.
|
|
3
5
|
|
|
@@ -7,7 +9,6 @@ native "PostToolUse" hooks (e.g. editors/agents that write files directly).
|
|
|
7
9
|
It batches filesystem changes and records them as activity events.
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
|
-
from __future__ import annotations
|
|
11
12
|
|
|
12
13
|
import os
|
|
13
14
|
import time
|
htmlgraph/work_type_utils.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
"""
|
|
2
4
|
Utility functions for work type inference and classification.
|
|
3
5
|
|
|
4
6
|
Provides automatic work type detection based on active work items.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
|
-
from __future__ import annotations
|
|
8
9
|
|
|
9
10
|
from typing import TYPE_CHECKING
|
|
10
11
|
|