htmlgraph 0.24.2__py3-none-any.whl → 0.26.1__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 +20 -1
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/analytics/cross_session.py +4 -3
- htmlgraph/analytics/work_type.py +52 -16
- htmlgraph/analytics_index.py +51 -19
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/main.py +2263 -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 +812 -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 +1020 -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 +509 -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 +163 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/builders/base.py +55 -1
- htmlgraph/builders/bug.py +17 -2
- htmlgraph/builders/chore.py +17 -2
- htmlgraph/builders/epic.py +17 -2
- htmlgraph/builders/feature.py +25 -2
- htmlgraph/builders/phase.py +17 -2
- htmlgraph/builders/spike.py +27 -2
- htmlgraph/builders/track.py +14 -0
- htmlgraph/cigs/__init__.py +4 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cli.py +1427 -401
- 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 +21 -0
- htmlgraph/collections/session.py +189 -0
- htmlgraph/collections/spike.py +7 -1
- htmlgraph/collections/task_delegation.py +236 -0
- htmlgraph/collections/traces.py +482 -0
- htmlgraph/config.py +113 -0
- htmlgraph/converter.py +41 -0
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +3356 -492
- htmlgraph-0.24.2.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
- 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 +1584 -0
- 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 +710 -0
- htmlgraph/docs/README.md +533 -0
- htmlgraph/docs/version_check.py +3 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +2 -0
- 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 +2 -2
- htmlgraph/hooks/concurrent_sessions.py +208 -0
- htmlgraph/hooks/context.py +318 -0
- htmlgraph/hooks/drift_handler.py +525 -0
- htmlgraph/hooks/event_tracker.py +496 -79
- htmlgraph/hooks/orchestrator.py +6 -4
- htmlgraph/hooks/orchestrator_reflector.py +4 -4
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/pretooluse.py +473 -6
- htmlgraph/hooks/prompt_analyzer.py +637 -0
- htmlgraph/hooks/session_handler.py +637 -0
- htmlgraph/hooks/state_manager.py +504 -0
- htmlgraph/hooks/subagent_stop.py +309 -0
- htmlgraph/hooks/task_enforcer.py +39 -0
- htmlgraph/hooks/validator.py +15 -11
- htmlgraph/models.py +111 -15
- htmlgraph/operations/fastapi_server.py +230 -0
- htmlgraph/orchestration/headless_spawner.py +344 -29
- htmlgraph/orchestration/live_events.py +377 -0
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/repo_hash.py +511 -0
- htmlgraph/sdk.py +348 -10
- htmlgraph/server.py +194 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +131 -1
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/system_prompts.py +449 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +19 -0
- htmlgraph/validation.py +115 -0
- htmlgraph-0.26.1.data/data/htmlgraph/dashboard.html +7458 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.26.1.dist-info}/METADATA +91 -64
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.26.1.dist-info}/RECORD +112 -46
- {htmlgraph-0.24.2.data → htmlgraph-0.26.1.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.24.2.data → htmlgraph-0.26.1.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.26.1.dist-info}/WHEEL +0 -0
- {htmlgraph-0.24.2.dist-info → htmlgraph-0.26.1.dist-info}/entry_points.txt +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
|
@@ -113,6 +113,25 @@ class TrackCollection:
|
|
|
113
113
|
# Auto-save on exit
|
|
114
114
|
graph.update(node)
|
|
115
115
|
|
|
116
|
+
def create(self, title: str) -> TrackBuilder:
|
|
117
|
+
"""
|
|
118
|
+
Create a new track with fluent interface.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
title: Track title
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
TrackBuilder for method chaining
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
track = sdk.tracks.create("Multi-Agent Collaboration") \\
|
|
128
|
+
.set_priority("high") \\
|
|
129
|
+
.save()
|
|
130
|
+
"""
|
|
131
|
+
builder = TrackBuilder(self._sdk)
|
|
132
|
+
builder._title = title
|
|
133
|
+
return builder
|
|
134
|
+
|
|
116
135
|
def builder(self) -> TrackBuilder:
|
|
117
136
|
"""
|
|
118
137
|
Create a new track builder with fluent interface.
|
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
|