arkaos 2.2.2 → 2.3.1

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.
Files changed (66) hide show
  1. package/VERSION +1 -1
  2. package/arka/skills/conclave/SKILL.md +194 -0
  3. package/arka/skills/human-writing/SKILL.md +143 -0
  4. package/config/agent-memory-template.md +28 -0
  5. package/config/disc-profiles.json +108 -0
  6. package/config/disc-team-validator.sh +94 -0
  7. package/config/gotchas-fixes.json +148 -0
  8. package/config/profile-template.json +12 -0
  9. package/config/providers-registry.json +56 -0
  10. package/config/settings-template.json +42 -0
  11. package/config/standards/communication.md +64 -0
  12. package/config/standards/orchestration.md +91 -0
  13. package/config/statusline-v2.sh +101 -0
  14. package/config/statusline.sh +139 -0
  15. package/config/system-prompt.sh +190 -0
  16. package/dashboard/LICENSE +21 -0
  17. package/dashboard/README.md +64 -0
  18. package/dashboard/app/app.config.ts +8 -0
  19. package/dashboard/app/app.vue +42 -0
  20. package/dashboard/app/assets/css/main.css +18 -0
  21. package/dashboard/app/composables/useApi.ts +8 -0
  22. package/dashboard/app/composables/useDashboard.ts +19 -0
  23. package/dashboard/app/error.vue +24 -0
  24. package/dashboard/app/layouts/default.vue +114 -0
  25. package/dashboard/app/pages/agents/[id].vue +506 -0
  26. package/dashboard/app/pages/agents/index.vue +225 -0
  27. package/dashboard/app/pages/budget.vue +132 -0
  28. package/dashboard/app/pages/commands.vue +180 -0
  29. package/dashboard/app/pages/health.vue +98 -0
  30. package/dashboard/app/pages/index.vue +126 -0
  31. package/dashboard/app/pages/knowledge.vue +729 -0
  32. package/dashboard/app/pages/personas.vue +597 -0
  33. package/dashboard/app/pages/settings.vue +146 -0
  34. package/dashboard/app/pages/tasks.vue +203 -0
  35. package/dashboard/app/types/index.d.ts +181 -0
  36. package/dashboard/app/utils/index.ts +7 -0
  37. package/dashboard/nuxt.config.ts +39 -0
  38. package/dashboard/package.json +37 -0
  39. package/dashboard/pnpm-workspace.yaml +7 -0
  40. package/dashboard/tsconfig.json +10 -0
  41. package/installer/cli.js +0 -0
  42. package/installer/index.js +262 -62
  43. package/knowledge/INDEX.md +34 -0
  44. package/knowledge/agents-registry.json +254 -0
  45. package/knowledge/channels-config.json +6 -0
  46. package/knowledge/commands-keywords.json +466 -0
  47. package/knowledge/commands-registry.json +2791 -0
  48. package/knowledge/commands-registry.json.bak +2791 -0
  49. package/knowledge/ecosystems.json +7 -0
  50. package/knowledge/obsidian-config.json +112 -0
  51. package/package.json +10 -6
  52. package/pyproject.toml +1 -1
  53. package/scripts/check-version.js +13 -0
  54. package/scripts/dashboard-api.py +636 -0
  55. package/scripts/knowledge-index.py +113 -0
  56. package/scripts/skill_validator.py +217 -0
  57. package/scripts/start-dashboard.sh +54 -0
  58. package/scripts/synapse-bridge.py +199 -0
  59. package/scripts/tools/brand_voice_analyzer.py +192 -0
  60. package/scripts/tools/dcf_calculator.py +168 -0
  61. package/scripts/tools/headline_scorer.py +215 -0
  62. package/scripts/tools/okr_cascade.py +207 -0
  63. package/scripts/tools/rice_prioritizer.py +230 -0
  64. package/scripts/tools/saas_metrics.py +234 -0
  65. package/scripts/tools/seo_checker.py +197 -0
  66. package/scripts/tools/tech_debt_analyzer.py +206 -0
@@ -0,0 +1,506 @@
1
+ <script setup lang="ts">
2
+ const route = useRoute()
3
+ const agentId = route.params.id as string
4
+
5
+ const { fetchApi } = useApi()
6
+ const { data: agent, status, error } = fetchApi<any>(`/api/agents/${agentId}`)
7
+
8
+ // --- Labels & mappings ---
9
+
10
+ const tierLabel: Record<number, string> = {
11
+ 0: 'C-Suite',
12
+ 1: 'Squad Lead',
13
+ 2: 'Specialist',
14
+ 3: 'Support',
15
+ }
16
+
17
+ const tierColor: Record<number, string> = {
18
+ 0: 'error',
19
+ 1: 'warning',
20
+ 2: 'primary',
21
+ 3: 'neutral',
22
+ }
23
+
24
+ const depthColor: Record<string, string> = {
25
+ master: 'error',
26
+ expert: 'warning',
27
+ advanced: 'primary',
28
+ intermediate: 'neutral',
29
+ }
30
+
31
+ const bigFiveLabels: Record<string, string> = {
32
+ O: 'Openness',
33
+ C: 'Conscientiousness',
34
+ E: 'Extraversion',
35
+ A: 'Agreeableness',
36
+ N: 'Neuroticism',
37
+ }
38
+
39
+ const bigFiveKeys = ['O', 'C', 'E', 'A', 'N'] as const
40
+
41
+ const mbtiDescriptions: Record<string, string> = {
42
+ INTJ: 'Ni-Te-Fi-Se — The Architect',
43
+ INTP: 'Ti-Ne-Si-Fe — The Logician',
44
+ ENTJ: 'Te-Ni-Se-Fi — The Commander',
45
+ ENTP: 'Ne-Ti-Fe-Si — The Debater',
46
+ INFJ: 'Ni-Fe-Ti-Se — The Advocate',
47
+ INFP: 'Fi-Ne-Si-Te — The Mediator',
48
+ ENFJ: 'Fe-Ni-Se-Ti — The Protagonist',
49
+ ENFP: 'Ne-Fi-Te-Si — The Campaigner',
50
+ ISTJ: 'Si-Te-Fi-Ne — The Inspector',
51
+ ISFJ: 'Si-Fe-Ti-Ne — The Defender',
52
+ ESTJ: 'Te-Si-Ne-Fi — The Executive',
53
+ ESFJ: 'Fe-Si-Ne-Ti — The Consul',
54
+ ISTP: 'Ti-Se-Ni-Fe — The Virtuoso',
55
+ ISFP: 'Fi-Se-Ni-Te — The Adventurer',
56
+ ESTP: 'Se-Ti-Fe-Ni — The Entrepreneur',
57
+ ESFP: 'Se-Fi-Te-Ni — The Entertainer',
58
+ }
59
+
60
+ // --- DISC bar values ---
61
+
62
+ const discLetters = ['D', 'I', 'S', 'C'] as const
63
+
64
+ function discBarValue(letter: string): number {
65
+ if (!agent.value?.disc) return 20
66
+ if (agent.value.disc.primary === letter) return 90
67
+ if (agent.value.disc.secondary === letter) return 70
68
+ return 20
69
+ }
70
+
71
+ function discBarColor(letter: string): string {
72
+ const colors: Record<string, string> = {
73
+ D: 'bg-red-500',
74
+ I: 'bg-yellow-500',
75
+ S: 'bg-green-500',
76
+ C: 'bg-blue-500',
77
+ }
78
+ return colors[letter] ?? 'bg-primary'
79
+ }
80
+
81
+ function bigFiveBarColor(value: number): string {
82
+ if (value >= 75) return 'bg-primary'
83
+ if (value >= 50) return 'bg-blue-400'
84
+ if (value >= 30) return 'bg-yellow-500'
85
+ return 'bg-neutral-500'
86
+ }
87
+
88
+ // --- Tabs ---
89
+
90
+ const tabs = [
91
+ { label: 'DNA', value: 'dna', icon: 'i-lucide-dna' },
92
+ { label: 'Communication', value: 'communication', icon: 'i-lucide-message-square' },
93
+ { label: 'Mental Models', value: 'models', icon: 'i-lucide-brain' },
94
+ { label: 'Authority', value: 'authority', icon: 'i-lucide-shield' },
95
+ { label: 'Expertise', value: 'expertise', icon: 'i-lucide-award' },
96
+ ]
97
+ </script>
98
+
99
+ <template>
100
+ <UDashboardPanel id="agent-detail">
101
+ <template #header>
102
+ <UDashboardNavbar :title="agent?.name ?? 'Agent'">
103
+ <template #leading>
104
+ <UDashboardSidebarCollapse />
105
+ </template>
106
+ <template #trailing>
107
+ <UButton
108
+ label="Back"
109
+ variant="ghost"
110
+ icon="i-lucide-arrow-left"
111
+ to="/agents"
112
+ aria-label="Back to agents list"
113
+ />
114
+ </template>
115
+ </UDashboardNavbar>
116
+ </template>
117
+
118
+ <template #body>
119
+ <!-- Loading -->
120
+ <div v-if="status === 'pending'" class="flex items-center justify-center py-24">
121
+ <UIcon name="i-lucide-loader-2" class="size-8 animate-spin text-muted" />
122
+ </div>
123
+
124
+ <!-- Error -->
125
+ <div v-else-if="error" class="flex flex-col items-center justify-center gap-4 py-24" role="alert">
126
+ <UIcon name="i-lucide-alert-triangle" class="size-12 text-red-500" />
127
+ <p class="text-sm text-muted">Failed to load agent data.</p>
128
+ <UButton label="Back to Agents" variant="outline" icon="i-lucide-arrow-left" to="/agents" />
129
+ </div>
130
+
131
+ <!-- Not found -->
132
+ <div v-else-if="!agent" class="flex flex-col items-center justify-center gap-4 py-24">
133
+ <UIcon name="i-lucide-user-x" class="size-12 text-muted" />
134
+ <p class="text-sm text-muted">Agent not found.</p>
135
+ <UButton label="Back to Agents" variant="outline" icon="i-lucide-arrow-left" to="/agents" />
136
+ </div>
137
+
138
+ <!-- Content -->
139
+ <div v-else class="space-y-8 pb-12">
140
+ <!-- ===== HEADER SECTION ===== -->
141
+ <section class="space-y-3">
142
+ <h1 class="text-3xl font-bold tracking-tight">{{ agent.name }}</h1>
143
+ <p class="text-lg text-muted">{{ agent.role }}</p>
144
+
145
+ <div class="flex flex-wrap items-center gap-2">
146
+ <UBadge :label="agent.department" variant="subtle" />
147
+ <UBadge
148
+ :label="`Tier ${agent.tier} — ${tierLabel[agent.tier] ?? ''}`"
149
+ variant="subtle"
150
+ :color="(tierColor[agent.tier] ?? 'neutral') as any"
151
+ />
152
+ <UBadge
153
+ v-if="agent.expertise_depth"
154
+ :label="agent.expertise_depth"
155
+ variant="subtle"
156
+ :color="(depthColor[agent.expertise_depth] ?? 'neutral') as any"
157
+ />
158
+ <UBadge
159
+ v-if="agent.expertise_years"
160
+ :label="`${agent.expertise_years}y experience`"
161
+ variant="outline"
162
+ />
163
+ </div>
164
+
165
+ <p class="text-xs text-muted/60 font-mono select-all">{{ agent.id }}</p>
166
+ </section>
167
+
168
+ <!-- ===== TABS ===== -->
169
+ <UTabs :items="tabs" class="w-full">
170
+ <template #content="{ item }">
171
+ <!-- ===== TAB: DNA ===== -->
172
+ <div v-if="item.value === 'dna'" class="space-y-6 mt-6">
173
+ <!-- DNA Summary — 3 cards -->
174
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
175
+ <!-- Card 1: MBTI -->
176
+ <UCard>
177
+ <div class="space-y-2">
178
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">MBTI</p>
179
+ <p class="text-4xl font-bold font-mono tracking-widest">
180
+ {{ agent.mbti || '----' }}
181
+ </p>
182
+ <p v-if="agent.mbti && mbtiDescriptions[agent.mbti]" class="text-sm text-muted">
183
+ {{ mbtiDescriptions[agent.mbti] }}
184
+ </p>
185
+ </div>
186
+ </UCard>
187
+
188
+ <!-- Card 2: Enneagram -->
189
+ <UCard>
190
+ <div class="space-y-3">
191
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">Enneagram</p>
192
+ <div>
193
+ <p class="text-3xl font-bold">
194
+ Type {{ agent.enneagram?.type ?? '-' }}
195
+ <span v-if="agent.enneagram?.wing" class="text-xl font-normal text-muted">
196
+ w{{ agent.enneagram.wing }}
197
+ </span>
198
+ </p>
199
+ <p v-if="agent.enneagram?.label" class="text-sm text-muted mt-1">
200
+ {{ agent.enneagram.label }}
201
+ </p>
202
+ </div>
203
+
204
+ <div class="grid grid-cols-2 gap-2 pt-1">
205
+ <div class="rounded-lg bg-red-500/10 border border-red-500/20 p-2.5">
206
+ <p class="text-[10px] font-bold text-red-400 uppercase tracking-wider mb-1">Fear</p>
207
+ <p class="text-xs text-muted leading-snug">
208
+ {{ agent.enneagram?.core_fear ?? 'Unknown' }}
209
+ </p>
210
+ </div>
211
+ <div class="rounded-lg bg-emerald-500/10 border border-emerald-500/20 p-2.5">
212
+ <p class="text-[10px] font-bold text-emerald-400 uppercase tracking-wider mb-1">Drive</p>
213
+ <p class="text-xs text-muted leading-snug">
214
+ {{ agent.enneagram?.core_motivation ?? 'Unknown' }}
215
+ </p>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </UCard>
220
+
221
+ <!-- Card 3: DISC -->
222
+ <UCard>
223
+ <div class="space-y-3">
224
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">DISC</p>
225
+ <div>
226
+ <p class="text-3xl font-bold font-mono">
227
+ {{ agent.disc?.primary ?? '' }}{{ agent.disc?.secondary ?? '' }}
228
+ </p>
229
+ <p v-if="agent.disc?.label" class="text-sm text-muted mt-1">
230
+ {{ agent.disc.label }}
231
+ </p>
232
+ </div>
233
+
234
+ <div class="space-y-2 pt-1">
235
+ <div v-for="letter in discLetters" :key="letter" class="flex items-center gap-2">
236
+ <span class="w-4 text-xs font-mono font-bold text-muted">{{ letter }}</span>
237
+ <div class="flex-1 h-2 rounded-full bg-muted/20">
238
+ <div
239
+ class="h-2 rounded-full transition-none"
240
+ :class="discBarColor(letter)"
241
+ :style="{ width: `${discBarValue(letter)}%` }"
242
+ />
243
+ </div>
244
+ <span class="w-6 text-right text-xs font-mono text-muted">
245
+ {{ discBarValue(letter) }}
246
+ </span>
247
+ </div>
248
+ </div>
249
+ </div>
250
+ </UCard>
251
+ </div>
252
+
253
+ <!-- Big Five -->
254
+ <UCard>
255
+ <div class="space-y-1">
256
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-4">
257
+ Big Five (OCEAN)
258
+ </p>
259
+ <div v-if="agent.big_five" class="space-y-3">
260
+ <div
261
+ v-for="key in bigFiveKeys"
262
+ :key="key"
263
+ class="flex items-center gap-3"
264
+ >
265
+ <span class="w-36 text-sm text-muted">{{ bigFiveLabels[key] }}</span>
266
+ <div class="flex-1 h-2 rounded-full bg-muted/20">
267
+ <div
268
+ class="h-2 rounded-full transition-none"
269
+ :class="bigFiveBarColor(agent.big_five[key] ?? 0)"
270
+ :style="{ width: `${agent.big_five[key] ?? 0}%` }"
271
+ />
272
+ </div>
273
+ <span class="w-8 text-right text-sm font-mono">
274
+ {{ agent.big_five[key] ?? 0 }}
275
+ </span>
276
+ </div>
277
+ </div>
278
+ <p v-else class="text-sm text-muted">No Big Five data available.</p>
279
+ </div>
280
+ </UCard>
281
+ </div>
282
+
283
+ <!-- ===== TAB: COMMUNICATION ===== -->
284
+ <div v-else-if="item.value === 'communication'" class="space-y-4 mt-6">
285
+ <div v-if="agent.communication" class="grid grid-cols-1 md:grid-cols-2 gap-4">
286
+ <UCard>
287
+ <div class="space-y-4">
288
+ <div>
289
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Tone</p>
290
+ <p class="text-sm">{{ agent.communication.tone ?? '-' }}</p>
291
+ </div>
292
+ <div>
293
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Vocabulary Level</p>
294
+ <UBadge :label="agent.communication.vocabulary_level ?? '-'" variant="subtle" />
295
+ </div>
296
+ <div>
297
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Language</p>
298
+ <p class="text-sm font-mono">{{ agent.communication.language ?? '-' }}</p>
299
+ </div>
300
+ </div>
301
+ </UCard>
302
+
303
+ <UCard>
304
+ <div class="space-y-4">
305
+ <div>
306
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Preferred Format</p>
307
+ <p class="text-sm">{{ agent.communication.preferred_format ?? '-' }}</p>
308
+ </div>
309
+ <div v-if="agent.communication.avoid?.length">
310
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-2">Avoids</p>
311
+ <ul class="space-y-1.5">
312
+ <li
313
+ v-for="item in agent.communication.avoid"
314
+ :key="item"
315
+ class="flex items-start gap-2 text-sm text-muted"
316
+ >
317
+ <UIcon name="i-lucide-x" class="size-4 text-red-400 mt-0.5 shrink-0" />
318
+ {{ item }}
319
+ </li>
320
+ </ul>
321
+ </div>
322
+ </div>
323
+ </UCard>
324
+ </div>
325
+
326
+ <!-- DISC Communication Details -->
327
+ <UCard v-if="agent.disc?.communication_style || agent.disc?.under_pressure || agent.disc?.motivator">
328
+ <div class="space-y-4">
329
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">DISC Communication Profile</p>
330
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
331
+ <div v-if="agent.disc.communication_style">
332
+ <p class="text-xs text-muted mb-1">Communication Style</p>
333
+ <p class="text-sm">{{ agent.disc.communication_style }}</p>
334
+ </div>
335
+ <div v-if="agent.disc.under_pressure">
336
+ <p class="text-xs text-muted mb-1">Under Pressure</p>
337
+ <p class="text-sm">{{ agent.disc.under_pressure }}</p>
338
+ </div>
339
+ <div v-if="agent.disc.motivator">
340
+ <p class="text-xs text-muted mb-1">Motivator</p>
341
+ <p class="text-sm">{{ agent.disc.motivator }}</p>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </UCard>
346
+
347
+ <div v-if="!agent.communication && !agent.disc?.communication_style" class="py-8 text-center">
348
+ <p class="text-sm text-muted">No communication data available.</p>
349
+ </div>
350
+ </div>
351
+
352
+ <!-- ===== TAB: MENTAL MODELS ===== -->
353
+ <div v-else-if="item.value === 'models'" class="space-y-4 mt-6">
354
+ <div v-if="agent.mental_models" class="grid grid-cols-1 md:grid-cols-2 gap-4">
355
+ <UCard>
356
+ <div class="space-y-3">
357
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">Primary Models</p>
358
+ <ul v-if="agent.mental_models.primary?.length" class="space-y-2">
359
+ <li
360
+ v-for="model in agent.mental_models.primary"
361
+ :key="model"
362
+ class="flex items-center gap-2"
363
+ >
364
+ <div class="size-1.5 rounded-full bg-primary shrink-0" />
365
+ <span class="text-sm font-medium">{{ model }}</span>
366
+ </li>
367
+ </ul>
368
+ <p v-else class="text-sm text-muted">None listed.</p>
369
+ </div>
370
+ </UCard>
371
+
372
+ <UCard>
373
+ <div class="space-y-3">
374
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">Secondary Models</p>
375
+ <ul v-if="agent.mental_models.secondary?.length" class="space-y-2">
376
+ <li
377
+ v-for="model in agent.mental_models.secondary"
378
+ :key="model"
379
+ class="flex items-center gap-2"
380
+ >
381
+ <div class="size-1.5 rounded-full bg-muted/40 shrink-0" />
382
+ <span class="text-sm text-muted">{{ model }}</span>
383
+ </li>
384
+ </ul>
385
+ <p v-else class="text-sm text-muted">None listed.</p>
386
+ </div>
387
+ </UCard>
388
+ </div>
389
+
390
+ <div v-else class="py-8 text-center">
391
+ <p class="text-sm text-muted">No mental model data available.</p>
392
+ </div>
393
+ </div>
394
+
395
+ <!-- ===== TAB: AUTHORITY ===== -->
396
+ <div v-else-if="item.value === 'authority'" class="space-y-4 mt-6">
397
+ <div v-if="agent.authority">
398
+ <UCard>
399
+ <div class="space-y-5">
400
+ <div>
401
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-3">Permissions</p>
402
+ <div class="flex flex-wrap gap-2">
403
+ <UBadge v-if="agent.authority.veto" label="Veto" color="error" variant="subtle" />
404
+ <UBadge v-if="agent.authority.approve_architecture" label="Approve Architecture" color="success" variant="subtle" />
405
+ <UBadge v-if="agent.authority.approve_budget" label="Approve Budget" color="success" variant="subtle" />
406
+ <UBadge v-if="agent.authority.approve_quality" label="Approve Quality" color="success" variant="subtle" />
407
+ <UBadge v-if="agent.authority.block_release" label="Block Release" color="error" variant="subtle" />
408
+ <UBadge v-if="agent.authority.orchestrate" label="Orchestrate" color="primary" variant="subtle" />
409
+ </div>
410
+ </div>
411
+
412
+ <div v-if="agent.authority.delegates_to?.length">
413
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-2">Delegates To</p>
414
+ <div class="flex flex-wrap gap-2">
415
+ <UBadge
416
+ v-for="d in agent.authority.delegates_to"
417
+ :key="d"
418
+ :label="d"
419
+ variant="outline"
420
+ size="sm"
421
+ />
422
+ </div>
423
+ </div>
424
+
425
+ <div v-if="agent.authority.escalates_to">
426
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Escalates To</p>
427
+ <p class="text-sm font-mono">{{ agent.authority.escalates_to }}</p>
428
+ </div>
429
+
430
+ <div v-if="!agent.authority.escalates_to && !agent.authority.delegates_to?.length && !agent.authority.veto">
431
+ <p class="text-sm text-muted">Standard execution authority.</p>
432
+ </div>
433
+ </div>
434
+ </UCard>
435
+ </div>
436
+
437
+ <div v-else class="py-8 text-center">
438
+ <p class="text-sm text-muted">No authority data available.</p>
439
+ </div>
440
+ </div>
441
+
442
+ <!-- ===== TAB: EXPERTISE ===== -->
443
+ <div v-else-if="item.value === 'expertise'" class="space-y-4 mt-6">
444
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
445
+ <UCard>
446
+ <div class="space-y-3">
447
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">Domains</p>
448
+ <div v-if="agent.expertise_domains?.length" class="flex flex-wrap gap-2">
449
+ <UBadge
450
+ v-for="d in agent.expertise_domains"
451
+ :key="d"
452
+ :label="d"
453
+ color="primary"
454
+ variant="subtle"
455
+ size="sm"
456
+ />
457
+ </div>
458
+ <p v-else class="text-sm text-muted">No domains listed.</p>
459
+ </div>
460
+ </UCard>
461
+
462
+ <UCard>
463
+ <div class="space-y-3">
464
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider">Frameworks</p>
465
+ <ul v-if="agent.frameworks?.length" class="space-y-2">
466
+ <li
467
+ v-for="f in agent.frameworks"
468
+ :key="f"
469
+ class="flex items-center gap-2 text-sm"
470
+ >
471
+ <UIcon name="i-lucide-check" class="size-3.5 text-primary shrink-0" />
472
+ {{ f }}
473
+ </li>
474
+ </ul>
475
+ <p v-else class="text-sm text-muted">No frameworks listed.</p>
476
+ </div>
477
+ </UCard>
478
+ </div>
479
+
480
+ <UCard v-if="agent.expertise_depth || agent.expertise_years">
481
+ <div class="flex flex-wrap gap-6">
482
+ <div v-if="agent.expertise_depth">
483
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Depth</p>
484
+ <UBadge
485
+ :label="agent.expertise_depth"
486
+ :color="(depthColor[agent.expertise_depth] ?? 'neutral') as any"
487
+ variant="subtle"
488
+ size="lg"
489
+ />
490
+ </div>
491
+ <div v-if="agent.expertise_years">
492
+ <p class="text-xs font-semibold text-muted uppercase tracking-wider mb-1">Experience</p>
493
+ <p class="text-2xl font-bold">
494
+ {{ agent.expertise_years }}
495
+ <span class="text-sm font-normal text-muted">years</span>
496
+ </p>
497
+ </div>
498
+ </div>
499
+ </UCard>
500
+ </div>
501
+ </template>
502
+ </UTabs>
503
+ </div>
504
+ </template>
505
+ </UDashboardPanel>
506
+ </template>