@wipal/agent-team 1.2.1 → 1.2.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipal/agent-team",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "CLI tool to add AI agent teams to existing projects with specialized roles, skills, and workflows (v2.1 with OpenFang patterns)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,8 +33,32 @@
33
33
 
34
34
  <!-- Agents List -->
35
35
  <section class="section">
36
- <div id="agents-grid" hx-get="/api/agents" hx-trigger="load, refreshAgents from:body">
37
- <div class="loading">Loading agents...</div>
36
+ <div id="agents-grid">
37
+ <div x-show="loadingAgents" class="loading">Loading agents...</div>
38
+ <div x-show="!loadingAgents && agents.length === 0" class="empty-state">
39
+ <p>No agents yet. <a href="#" @click.prevent="openAddModal()">Add your first agent</a></p>
40
+ </div>
41
+ <template x-for="agent in agents" :key="agent.name">
42
+ <div class="agent-card">
43
+ <div class="agent-header">
44
+ <span class="agent-name" x-text="agent.name"></span>
45
+ <span class="agent-role" x-text="agent.variants?.base_role || 'unknown'"></span>
46
+ </div>
47
+ <div class="agent-variants">
48
+ <template x-for="(value, key) in agent.variants" :key="key">
49
+ <span class="variant-tag" x-text="key + ': ' + value"></span>
50
+ </template>
51
+ </div>
52
+ <div class="agent-skills" x-show="agent.skills?.length">
53
+ <template x-for="skill in agent.skills" :key="skill">
54
+ <span class="skill-tag" x-text="skill"></span>
55
+ </template>
56
+ </div>
57
+ <div class="agent-actions">
58
+ <button class="btn btn-danger" @click="deleteAgent(agent.name)">Delete</button>
59
+ </div>
60
+ </div>
61
+ </template>
38
62
  </div>
39
63
  </section>
40
64
 
@@ -118,9 +142,11 @@
118
142
  function agentApp() {
119
143
  return {
120
144
  showAddModal: false,
145
+ loadingAgents: true,
121
146
  loadingRoles: false,
122
147
  loadingSkills: false,
123
148
  creating: false,
149
+ agents: [],
124
150
  roles: [],
125
151
  variantCategories: {},
126
152
  previewSkills: [],
@@ -130,6 +156,38 @@
130
156
  variants: {}
131
157
  },
132
158
 
159
+ async init() {
160
+ await this.loadAgents();
161
+ },
162
+
163
+ async loadAgents() {
164
+ this.loadingAgents = true;
165
+ try {
166
+ const response = await fetch('/api/agents');
167
+ const data = await response.json();
168
+ this.agents = data.agents || [];
169
+ } catch (error) {
170
+ console.error('Failed to load agents:', error);
171
+ } finally {
172
+ this.loadingAgents = false;
173
+ }
174
+ },
175
+
176
+ async deleteAgent(name) {
177
+ if (!confirm(`Delete agent "${name}"?`)) return;
178
+ try {
179
+ const response = await fetch(`/api/agents/${name}`, { method: 'DELETE' });
180
+ if (response.ok) {
181
+ await this.loadAgents();
182
+ } else {
183
+ const error = await response.json();
184
+ alert('Failed to delete: ' + error.error);
185
+ }
186
+ } catch (error) {
187
+ alert('Failed to delete agent: ' + error.message);
188
+ }
189
+ },
190
+
133
191
  get selectedRoleDescription() {
134
192
  const role = this.roles.find(r => r.name === this.newAgent.role);
135
193
  return role ? role.description : '';
@@ -224,7 +282,7 @@
224
282
  if (response.ok) {
225
283
  this.showAddModal = false;
226
284
  this.resetForm();
227
- htmx.trigger(document.body, 'refreshAgents');
285
+ await this.loadAgents();
228
286
  alert('Agent created successfully!');
229
287
  } else {
230
288
  const error = await response.json();
@@ -497,6 +497,77 @@ body {
497
497
  /* Utility */
498
498
  [x-cloak] { display: none !important; }
499
499
 
500
+ /* Empty State */
501
+ .empty-state {
502
+ text-align: center;
503
+ padding: 2rem;
504
+ color: var(--text-muted);
505
+ }
506
+
507
+ .empty-state a {
508
+ color: var(--primary);
509
+ }
510
+
511
+ /* Compact agent card for dashboard */
512
+ .agent-card-compact {
513
+ display: flex;
514
+ justify-content: space-between;
515
+ align-items: center;
516
+ padding: 0.75rem 1rem;
517
+ background: var(--bg);
518
+ border-radius: 0.375rem;
519
+ margin-bottom: 0.5rem;
520
+ }
521
+
522
+ .agent-card-compact .agent-name {
523
+ font-weight: 500;
524
+ }
525
+
526
+ .agent-card-compact .agent-role {
527
+ font-size: 0.75rem;
528
+ color: var(--text-muted);
529
+ }
530
+
531
+ /* Roles preview grid */
532
+ .roles-grid-preview {
533
+ display: grid;
534
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
535
+ gap: 0.75rem;
536
+ }
537
+
538
+ .role-chip {
539
+ padding: 0.75rem 1rem;
540
+ background: var(--bg);
541
+ border-radius: 0.375rem;
542
+ border: 1px solid var(--border);
543
+ }
544
+
545
+ .role-chip strong {
546
+ display: block;
547
+ margin-bottom: 0.25rem;
548
+ }
549
+
550
+ .role-chip span {
551
+ font-size: 0.75rem;
552
+ color: var(--text-muted);
553
+ }
554
+
555
+ /* Agent variants in card */
556
+ .agent-variants {
557
+ display: flex;
558
+ flex-wrap: wrap;
559
+ gap: 0.25rem;
560
+ margin: 0.5rem 0;
561
+ }
562
+
563
+ .variant-tag {
564
+ background: var(--bg);
565
+ padding: 0.125rem 0.5rem;
566
+ border-radius: 0.25rem;
567
+ font-size: 0.7rem;
568
+ color: var(--text-muted);
569
+ }
570
+
500
571
  /* Responsive */
501
572
  @media (max-width: 768px) {
502
573
  .container {
package/src/ui/index.html CHANGED
@@ -41,9 +41,18 @@
41
41
  </nav>
42
42
 
43
43
  <!-- Stats Cards -->
44
- <div class="stats-grid" hx-get="/api/agents" hx-trigger="load, projectChanged from:body" hx-swap="innerHTML">
45
- <!-- Loaded via HTMX -->
46
- <div class="loading">Loading...</div>
44
+ <div class="stats-grid" x-data="statsCards()">
45
+ <template x-if="loading">
46
+ <div class="stat-card"><div class="loading">Loading...</div></div>
47
+ </template>
48
+ <template x-if="!loading">
49
+ <template x-for="stat in stats" :key="stat.label">
50
+ <div class="stat-card">
51
+ <div class="stat-value" x-text="stat.value"></div>
52
+ <div class="stat-label" x-text="stat.label"></div>
53
+ </div>
54
+ </template>
55
+ </template>
47
56
  </div>
48
57
 
49
58
  <!-- Quick Actions -->
@@ -65,16 +74,36 @@
65
74
  <!-- Recent Agents -->
66
75
  <section class="section">
67
76
  <h2>Your Agents</h2>
68
- <div id="agents-list" hx-get="/api/agents" hx-trigger="load, projectChanged from:body">
69
- <div class="loading">Loading agents...</div>
77
+ <div id="agents-list" x-data="dashboardAgents()">
78
+ <div x-show="loading" class="loading">Loading agents...</div>
79
+ <div x-show="!loading && agents.length === 0" class="empty-state">
80
+ <p>No agents configured yet. <a href="/ui/agents.html?action=add">Add your first agent</a></p>
81
+ </div>
82
+ <template x-for="agent in agents" :key="agent.name">
83
+ <div class="agent-card-compact">
84
+ <span class="agent-name" x-text="agent.name"></span>
85
+ <span class="agent-role" x-text="agent.variants?.base_role || 'unknown'"></span>
86
+ </div>
87
+ </template>
88
+ <div x-show="!loading && agents.length > 0" style="margin-top: 1rem;">
89
+ <a href="/ui/agents.html" class="btn btn-secondary">Manage Agents</a>
90
+ </div>
70
91
  </div>
71
92
  </section>
72
93
 
73
94
  <!-- Available Roles -->
74
95
  <section class="section">
75
96
  <h2>Available Roles</h2>
76
- <div id="roles-list" hx-get="/api/agents/meta/roles" hx-trigger="load">
77
- <div class="loading">Loading roles...</div>
97
+ <div id="roles-list" x-data="rolesPreview()">
98
+ <div x-show="loading" class="loading">Loading roles...</div>
99
+ <div class="roles-grid-preview">
100
+ <template x-for="role in roles" :key="role.name">
101
+ <div class="role-chip">
102
+ <strong x-text="role.name"></strong>
103
+ <span x-text="role.description"></span>
104
+ </div>
105
+ </template>
106
+ </div>
78
107
  </div>
79
108
  </section>
80
109
  </div>
@@ -141,44 +170,85 @@
141
170
  }
142
171
  }
143
172
 
144
- // Transform stats response
145
- document.body.addEventListener('htmx:beforeSwap', function(evt) {
146
- if (evt.detail.pathInfo.requestPath === '/api/agents') {
147
- // First load - render as stats
148
- if (evt.detail.target.matches('.stats-grid')) {
173
+ // Dashboard Agents Alpine Component
174
+ function dashboardAgents() {
175
+ return {
176
+ agents: [],
177
+ loading: true,
178
+
179
+ async init() {
180
+ await this.loadAgents();
181
+ // Listen for project changes
182
+ document.body.addEventListener('projectChanged', () => this.loadAgents());
183
+ },
184
+
185
+ async loadAgents() {
186
+ this.loading = true;
149
187
  try {
150
- const data = JSON.parse(evt.detail.xhr.response);
151
- const agents = data.agents || [];
152
- const statsHtml = renderStats(agents);
153
- evt.detail.swap = 'innerHTML';
154
- evt.detail.xhr.response = statsHtml;
155
- } catch (e) {}
188
+ const response = await fetch('/api/agents');
189
+ const data = await response.json();
190
+ this.agents = data.agents || [];
191
+ } catch (error) {
192
+ console.error('Failed to load agents:', error);
193
+ } finally {
194
+ this.loading = false;
195
+ }
156
196
  }
157
197
  }
158
- });
198
+ }
159
199
 
160
- function renderStats(agents) {
161
- const total = agents.length;
162
- const roles = [...new Set(agents.map(a => a.variants?.base_role).filter(Boolean))];
200
+ // Roles Preview Alpine Component
201
+ function rolesPreview() {
202
+ return {
203
+ roles: [],
204
+ loading: true,
163
205
 
164
- return `
165
- <div class="stat-card">
166
- <div class="stat-value">${total}</div>
167
- <div class="stat-label">Total Agents</div>
168
- </div>
169
- <div class="stat-card">
170
- <div class="stat-value">${roles.length}</div>
171
- <div class="stat-label">Roles Used</div>
172
- </div>
173
- <div class="stat-card">
174
- <div class="stat-value">50</div>
175
- <div class="stat-label">Skills Available</div>
176
- </div>
177
- <div class="stat-card">
178
- <div class="stat-value">9</div>
179
- <div class="stat-label">Roles Available</div>
180
- </div>
181
- `;
206
+ async init() {
207
+ try {
208
+ const response = await fetch('/api/agents/meta/roles');
209
+ this.roles = await response.json();
210
+ } catch (error) {
211
+ console.error('Failed to load roles:', error);
212
+ } finally {
213
+ this.loading = false;
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ // Stats Cards Alpine Component
220
+ function statsCards() {
221
+ return {
222
+ stats: [],
223
+ loading: true,
224
+
225
+ async init() {
226
+ await this.loadStats();
227
+ // Listen for project changes
228
+ document.body.addEventListener('projectChanged', () => this.loadStats());
229
+ },
230
+
231
+ async loadStats() {
232
+ this.loading = true;
233
+ try {
234
+ const response = await fetch('/api/agents');
235
+ const data = await response.json();
236
+ const agents = data.agents || [];
237
+ const roles = [...new Set(agents.map(a => a.variants?.base_role).filter(Boolean))];
238
+
239
+ this.stats = [
240
+ { value: agents.length, label: 'Total Agents' },
241
+ { value: roles.length, label: 'Roles Used' },
242
+ { value: 50, label: 'Skills Available' },
243
+ { value: 9, label: 'Roles Available' }
244
+ ];
245
+ } catch (error) {
246
+ console.error('Failed to load stats:', error);
247
+ } finally {
248
+ this.loading = false;
249
+ }
250
+ }
251
+ }
182
252
  }
183
253
  </script>
184
254
  </body>