arkaos 2.3.0 → 2.3.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.
Files changed (64) 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/knowledge/INDEX.md +34 -0
  42. package/knowledge/agents-registry.json +254 -0
  43. package/knowledge/channels-config.json +6 -0
  44. package/knowledge/commands-keywords.json +466 -0
  45. package/knowledge/commands-registry.json +2791 -0
  46. package/knowledge/commands-registry.json.bak +2791 -0
  47. package/knowledge/ecosystems.json +7 -0
  48. package/knowledge/obsidian-config.json +112 -0
  49. package/package.json +10 -6
  50. package/pyproject.toml +1 -1
  51. package/scripts/check-version.js +13 -0
  52. package/scripts/dashboard-api.py +636 -0
  53. package/scripts/knowledge-index.py +113 -0
  54. package/scripts/skill_validator.py +217 -0
  55. package/scripts/start-dashboard.sh +103 -0
  56. package/scripts/synapse-bridge.py +199 -0
  57. package/scripts/tools/brand_voice_analyzer.py +192 -0
  58. package/scripts/tools/dcf_calculator.py +168 -0
  59. package/scripts/tools/headline_scorer.py +215 -0
  60. package/scripts/tools/okr_cascade.py +207 -0
  61. package/scripts/tools/rice_prioritizer.py +230 -0
  62. package/scripts/tools/saas_metrics.py +234 -0
  63. package/scripts/tools/seo_checker.py +197 -0
  64. package/scripts/tools/tech_debt_analyzer.py +206 -0
@@ -0,0 +1,597 @@
1
+ <script setup lang="ts">
2
+ import type { Persona } from '~/types'
3
+
4
+ const { fetchApi, apiBase } = useApi()
5
+ const toast = useToast()
6
+
7
+ // --- Fetch personas (no await — non-blocking) ---
8
+ const { data, status, error, refresh } = fetchApi<{ personas: Persona[]; total: number }>('/api/personas')
9
+
10
+ const personas = computed(() => data.value?.personas ?? [])
11
+
12
+ // --- Form visibility ---
13
+ const showForm = ref(false)
14
+
15
+ // --- Form state ---
16
+ function defaultForm() {
17
+ return {
18
+ name: '',
19
+ title: '',
20
+ source: '',
21
+ tagline: '',
22
+ mbti: '',
23
+ disc_primary: '',
24
+ disc_secondary: '',
25
+ enneagram_type: '',
26
+ enneagram_wing: '',
27
+ big_five_o: 50,
28
+ big_five_c: 50,
29
+ big_five_e: 50,
30
+ big_five_a: 50,
31
+ big_five_n: 50,
32
+ mental_models: '',
33
+ expertise_domains: '',
34
+ frameworks: '',
35
+ communication_tone: '',
36
+ }
37
+ }
38
+
39
+ const form = ref(defaultForm())
40
+ const creating = ref(false)
41
+
42
+ // --- Options ---
43
+ const mbtiTypes = [
44
+ 'INTJ', 'INTP', 'ENTJ', 'ENTP',
45
+ 'INFJ', 'INFP', 'ENFJ', 'ENFP',
46
+ 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ',
47
+ 'ISTP', 'ISFP', 'ESTP', 'ESFP',
48
+ ].map(t => ({ label: t, value: t }))
49
+
50
+ const discTypes = [
51
+ { label: 'D — Dominance', value: 'D' },
52
+ { label: 'I — Influence', value: 'I' },
53
+ { label: 'S — Steadiness', value: 'S' },
54
+ { label: 'C — Conscientiousness', value: 'C' },
55
+ ]
56
+
57
+ const enneagramTypes = Array.from({ length: 9 }, (_, i) => ({
58
+ label: `Type ${i + 1}`,
59
+ value: String(i + 1),
60
+ }))
61
+
62
+ const departmentOptions = [
63
+ 'dev', 'marketing', 'brand', 'finance', 'strategy',
64
+ 'ecom', 'kb', 'ops', 'pm', 'saas',
65
+ 'landing', 'content', 'community', 'sales', 'leadership', 'org',
66
+ ].map(d => ({ label: d, value: d }))
67
+
68
+ const tierOptions = [
69
+ { label: 'Tier 1 — Squad Leads', value: '1' },
70
+ { label: 'Tier 2 — Specialists', value: '2' },
71
+ { label: 'Tier 3 — Support', value: '3' },
72
+ ]
73
+
74
+ // --- Create persona ---
75
+ async function createPersona() {
76
+ if (!form.value.name.trim()) return
77
+
78
+ creating.value = true
79
+ try {
80
+ await $fetch(`${apiBase}/api/personas`, {
81
+ method: 'POST',
82
+ body: {
83
+ name: form.value.name,
84
+ title: form.value.title,
85
+ source: form.value.source,
86
+ tagline: form.value.tagline,
87
+ mbti: form.value.mbti,
88
+ disc: {
89
+ primary: form.value.disc_primary,
90
+ secondary: form.value.disc_secondary,
91
+ },
92
+ enneagram: {
93
+ type: form.value.enneagram_type ? Number(form.value.enneagram_type) : null,
94
+ wing: form.value.enneagram_wing ? Number(form.value.enneagram_wing) : null,
95
+ },
96
+ big_five: {
97
+ openness: form.value.big_five_o,
98
+ conscientiousness: form.value.big_five_c,
99
+ extraversion: form.value.big_five_e,
100
+ agreeableness: form.value.big_five_a,
101
+ neuroticism: form.value.big_five_n,
102
+ },
103
+ mental_models: form.value.mental_models
104
+ ? form.value.mental_models.split(',').map(s => s.trim()).filter(Boolean)
105
+ : [],
106
+ expertise_domains: form.value.expertise_domains
107
+ ? form.value.expertise_domains.split(',').map(s => s.trim()).filter(Boolean)
108
+ : [],
109
+ frameworks: form.value.frameworks
110
+ ? form.value.frameworks.split(',').map(s => s.trim()).filter(Boolean)
111
+ : [],
112
+ communication: {
113
+ tone: form.value.communication_tone,
114
+ },
115
+ },
116
+ })
117
+
118
+ toast.add({ title: 'Persona created', description: `${form.value.name} has been added.`, color: 'success' })
119
+ form.value = defaultForm()
120
+ showForm.value = false
121
+ await refresh()
122
+ } catch {
123
+ toast.add({ title: 'Error', description: 'Failed to create persona.', color: 'error' })
124
+ } finally {
125
+ creating.value = false
126
+ }
127
+ }
128
+
129
+ // --- Delete persona ---
130
+ const deleting = ref<string | null>(null)
131
+
132
+ async function deletePersona(persona: Persona) {
133
+ deleting.value = persona.id
134
+ try {
135
+ await $fetch(`${apiBase}/api/personas/${persona.id}`, { method: 'DELETE' })
136
+ toast.add({ title: 'Persona deleted', description: `${persona.name} has been removed.`, color: 'success' })
137
+ await refresh()
138
+ } catch {
139
+ toast.add({ title: 'Error', description: 'Failed to delete persona.', color: 'error' })
140
+ } finally {
141
+ deleting.value = null
142
+ }
143
+ }
144
+
145
+ // --- Clone to agent (inline expansion, no modal) ---
146
+ const cloneExpandedId = ref<string | null>(null)
147
+ const cloneDepartment = ref('')
148
+ const cloneTier = ref('')
149
+ const cloning = ref(false)
150
+
151
+ function toggleClone(persona: Persona) {
152
+ if (cloneExpandedId.value === persona.id) {
153
+ cloneExpandedId.value = null
154
+ } else {
155
+ cloneExpandedId.value = persona.id
156
+ cloneDepartment.value = ''
157
+ cloneTier.value = ''
158
+ }
159
+ }
160
+
161
+ async function cloneToAgent(persona: Persona) {
162
+ if (!cloneDepartment.value || !cloneTier.value) return
163
+
164
+ cloning.value = true
165
+ try {
166
+ await $fetch(`${apiBase}/api/personas/${persona.id}/clone`, {
167
+ method: 'POST',
168
+ body: {
169
+ department: cloneDepartment.value,
170
+ tier: Number(cloneTier.value),
171
+ },
172
+ })
173
+ toast.add({
174
+ title: 'Agent created',
175
+ description: `${persona.name} cloned to ${cloneDepartment.value} department.`,
176
+ color: 'success',
177
+ })
178
+ cloneExpandedId.value = null
179
+ await refresh()
180
+ } catch {
181
+ toast.add({ title: 'Error', description: 'Failed to clone persona to agent.', color: 'error' })
182
+ } finally {
183
+ cloning.value = false
184
+ }
185
+ }
186
+
187
+ // --- DNA badge colors ---
188
+ function mbtiColor(mbti: string): string {
189
+ if (!mbti) return 'neutral'
190
+ const analysts = ['INTJ', 'INTP', 'ENTJ', 'ENTP']
191
+ const diplomats = ['INFJ', 'INFP', 'ENFJ', 'ENFP']
192
+ const sentinels = ['ISTJ', 'ISFJ', 'ESTJ', 'ESFJ']
193
+ if (analysts.includes(mbti)) return 'primary'
194
+ if (diplomats.includes(mbti)) return 'success'
195
+ if (sentinels.includes(mbti)) return 'warning'
196
+ return 'error'
197
+ }
198
+
199
+ function discColor(disc: string): string {
200
+ const colors: Record<string, string> = { D: 'error', I: 'warning', S: 'success', C: 'primary' }
201
+ return colors[disc] ?? 'neutral'
202
+ }
203
+ </script>
204
+
205
+ <template>
206
+ <UDashboardPanel id="personas">
207
+ <template #header>
208
+ <UDashboardNavbar title="Personas">
209
+ <template #leading>
210
+ <UDashboardSidebarCollapse />
211
+ </template>
212
+
213
+ <template #trailing>
214
+ <UBadge v-if="data?.total" :label="data.total" variant="subtle" />
215
+ </template>
216
+
217
+ <template #right>
218
+ <UButton
219
+ :label="showForm ? 'Cancel' : 'New Persona'"
220
+ :icon="showForm ? 'i-lucide-x' : 'i-lucide-plus'"
221
+ :variant="showForm ? 'ghost' : 'solid'"
222
+ size="sm"
223
+ @click="showForm = !showForm"
224
+ />
225
+ </template>
226
+ </UDashboardNavbar>
227
+ </template>
228
+
229
+ <template #body>
230
+ <div class="overflow-y-auto h-[calc(100vh-4rem)]">
231
+ <!-- Loading -->
232
+ <div v-if="status === 'pending'" class="flex items-center justify-center py-12" aria-label="Loading personas">
233
+ <UIcon name="i-lucide-loader-2" class="size-8 animate-spin text-muted" />
234
+ </div>
235
+
236
+ <!-- Error -->
237
+ <div v-else-if="error" class="flex flex-col items-center justify-center gap-4 py-12" role="alert">
238
+ <UIcon name="i-lucide-alert-triangle" class="size-12 text-red-500" />
239
+ <p class="text-sm text-muted">Failed to load personas.</p>
240
+ <UButton label="Retry" variant="outline" color="primary" icon="i-lucide-refresh-cw" @click="refresh()" />
241
+ </div>
242
+
243
+ <!-- Content -->
244
+ <template v-else>
245
+ <!-- Create Persona Form -->
246
+ <UCard v-if="showForm" class="mb-8">
247
+ <form @submit.prevent="createPersona" class="space-y-8 p-2">
248
+ <!-- Identity -->
249
+ <fieldset>
250
+ <legend class="text-xs font-bold uppercase tracking-widest text-muted mb-4">Identity</legend>
251
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
252
+ <UFormField label="Name" required>
253
+ <UInput
254
+ v-model="form.name"
255
+ placeholder="e.g. Alex Hormozi"
256
+ aria-label="Persona name"
257
+ class="w-full"
258
+ required
259
+ />
260
+ </UFormField>
261
+ <UFormField label="Title">
262
+ <UInput
263
+ v-model="form.title"
264
+ placeholder="e.g. Business Strategy"
265
+ aria-label="Persona title"
266
+ class="w-full"
267
+ />
268
+ </UFormField>
269
+ <UFormField label="Source">
270
+ <UInput
271
+ v-model="form.source"
272
+ placeholder="e.g. Alex Hormozi"
273
+ aria-label="Persona source"
274
+ class="w-full"
275
+ />
276
+ </UFormField>
277
+ <UFormField label="Tagline">
278
+ <UInput
279
+ v-model="form.tagline"
280
+ placeholder="e.g. The Natural Commander"
281
+ aria-label="Persona tagline"
282
+ class="w-full"
283
+ />
284
+ </UFormField>
285
+ </div>
286
+ </fieldset>
287
+
288
+ <!-- Behavioral DNA -->
289
+ <fieldset>
290
+ <legend class="text-xs font-bold uppercase tracking-widest text-muted mb-4">Behavioral DNA</legend>
291
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
292
+ <UFormField label="MBTI">
293
+ <USelect
294
+ v-model="form.mbti"
295
+ :items="mbtiTypes"
296
+ placeholder="Select MBTI"
297
+ aria-label="MBTI type"
298
+ class="w-full"
299
+ />
300
+ </UFormField>
301
+ <UFormField label="DISC Primary">
302
+ <USelect
303
+ v-model="form.disc_primary"
304
+ :items="discTypes"
305
+ placeholder="Primary"
306
+ aria-label="DISC primary type"
307
+ class="w-full"
308
+ />
309
+ </UFormField>
310
+ <UFormField label="DISC Secondary">
311
+ <USelect
312
+ v-model="form.disc_secondary"
313
+ :items="discTypes"
314
+ placeholder="Secondary"
315
+ aria-label="DISC secondary type"
316
+ class="w-full"
317
+ />
318
+ </UFormField>
319
+ <UFormField label="Enneagram Type">
320
+ <USelect
321
+ v-model="form.enneagram_type"
322
+ :items="enneagramTypes"
323
+ placeholder="Type"
324
+ aria-label="Enneagram type"
325
+ class="w-full"
326
+ />
327
+ </UFormField>
328
+ </div>
329
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
330
+ <UFormField label="Enneagram Wing (1-9)">
331
+ <UInput
332
+ v-model="form.enneagram_wing"
333
+ type="number"
334
+ :min="1"
335
+ :max="9"
336
+ placeholder="e.g. 4"
337
+ aria-label="Enneagram wing"
338
+ class="w-full"
339
+ />
340
+ </UFormField>
341
+ </div>
342
+ </fieldset>
343
+
344
+ <!-- Big Five -->
345
+ <fieldset>
346
+ <legend class="text-xs font-bold uppercase tracking-widest text-muted mb-4">Big Five / OCEAN (0-100)</legend>
347
+ <div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
348
+ <UFormField label="Openness">
349
+ <UInput
350
+ v-model.number="form.big_five_o"
351
+ type="number"
352
+ :min="0"
353
+ :max="100"
354
+ aria-label="Openness score"
355
+ class="w-full"
356
+ />
357
+ </UFormField>
358
+ <UFormField label="Conscientiousness">
359
+ <UInput
360
+ v-model.number="form.big_five_c"
361
+ type="number"
362
+ :min="0"
363
+ :max="100"
364
+ aria-label="Conscientiousness score"
365
+ class="w-full"
366
+ />
367
+ </UFormField>
368
+ <UFormField label="Extraversion">
369
+ <UInput
370
+ v-model.number="form.big_five_e"
371
+ type="number"
372
+ :min="0"
373
+ :max="100"
374
+ aria-label="Extraversion score"
375
+ class="w-full"
376
+ />
377
+ </UFormField>
378
+ <UFormField label="Agreeableness">
379
+ <UInput
380
+ v-model.number="form.big_five_a"
381
+ type="number"
382
+ :min="0"
383
+ :max="100"
384
+ aria-label="Agreeableness score"
385
+ class="w-full"
386
+ />
387
+ </UFormField>
388
+ <UFormField label="Neuroticism">
389
+ <UInput
390
+ v-model.number="form.big_five_n"
391
+ type="number"
392
+ :min="0"
393
+ :max="100"
394
+ aria-label="Neuroticism score"
395
+ class="w-full"
396
+ />
397
+ </UFormField>
398
+ </div>
399
+ </fieldset>
400
+
401
+ <!-- Knowledge & Communication -->
402
+ <fieldset>
403
+ <legend class="text-xs font-bold uppercase tracking-widest text-muted mb-4">Knowledge & Communication</legend>
404
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
405
+ <UFormField label="Mental Models" hint="Comma-separated">
406
+ <UInput
407
+ v-model="form.mental_models"
408
+ placeholder="e.g. Grand Slam Offer, Value Equation"
409
+ aria-label="Mental models, comma-separated"
410
+ class="w-full"
411
+ />
412
+ </UFormField>
413
+ <UFormField label="Expertise Domains" hint="Comma-separated">
414
+ <UInput
415
+ v-model="form.expertise_domains"
416
+ placeholder="e.g. business strategy, offer creation"
417
+ aria-label="Expertise domains, comma-separated"
418
+ class="w-full"
419
+ />
420
+ </UFormField>
421
+ <UFormField label="Frameworks" hint="Comma-separated">
422
+ <UInput
423
+ v-model="form.frameworks"
424
+ placeholder="e.g. $100M Offers, Value Equation"
425
+ aria-label="Frameworks, comma-separated"
426
+ class="w-full"
427
+ />
428
+ </UFormField>
429
+ <UFormField label="Communication Tone">
430
+ <UInput
431
+ v-model="form.communication_tone"
432
+ placeholder="e.g. direct, high-energy"
433
+ aria-label="Communication tone"
434
+ class="w-full"
435
+ />
436
+ </UFormField>
437
+ </div>
438
+ </fieldset>
439
+
440
+ <!-- Submit -->
441
+ <UButton
442
+ type="submit"
443
+ label="Create Persona"
444
+ icon="i-lucide-sparkles"
445
+ size="lg"
446
+ block
447
+ :loading="creating"
448
+ :disabled="!form.name.trim()"
449
+ />
450
+ </form>
451
+ </UCard>
452
+
453
+ <!-- Empty state -->
454
+ <div v-if="!personas.length && !showForm" class="flex flex-col items-center justify-center gap-6 py-20">
455
+ <div class="rounded-full bg-muted/10 p-6">
456
+ <UIcon name="i-lucide-users" class="size-12 text-muted" />
457
+ </div>
458
+ <div class="text-center space-y-2">
459
+ <h3 class="text-base font-semibold">No personas yet</h3>
460
+ <p class="text-sm text-muted max-w-sm">
461
+ Personas define the behavioral DNA for your AI agents. Create one to get started.
462
+ </p>
463
+ </div>
464
+ <UButton
465
+ label="Create your first persona"
466
+ icon="i-lucide-plus"
467
+ size="lg"
468
+ @click="showForm = true"
469
+ />
470
+ </div>
471
+
472
+ <!-- Personas Grid -->
473
+ <div v-if="personas.length" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
474
+ <UCard
475
+ v-for="persona in personas"
476
+ :key="persona.id"
477
+ class="group flex flex-col"
478
+ >
479
+ <div class="flex flex-col gap-3 flex-1">
480
+ <!-- Header -->
481
+ <div>
482
+ <h3 class="text-base font-bold truncate">{{ persona.name }}</h3>
483
+ <p v-if="persona.title" class="text-sm text-muted truncate mt-0.5">{{ persona.title }}</p>
484
+ <p v-if="persona.source" class="text-xs text-muted/60 mt-1">
485
+ Source: {{ persona.source }}
486
+ </p>
487
+ </div>
488
+
489
+ <!-- Tagline -->
490
+ <p v-if="persona.tagline" class="text-sm text-muted italic leading-relaxed">
491
+ "{{ persona.tagline }}"
492
+ </p>
493
+
494
+ <!-- DNA Badges -->
495
+ <div class="flex flex-wrap gap-1.5">
496
+ <UBadge
497
+ v-if="persona.mbti"
498
+ :label="persona.mbti"
499
+ :color="mbtiColor(persona.mbti) as any"
500
+ variant="subtle"
501
+ size="xs"
502
+ />
503
+ <UBadge
504
+ v-if="persona.disc?.primary"
505
+ :label="`DISC: ${persona.disc.primary}${persona.disc.secondary ? '/' + persona.disc.secondary : ''}`"
506
+ :color="discColor(persona.disc.primary) as any"
507
+ variant="subtle"
508
+ size="xs"
509
+ />
510
+ <UBadge
511
+ v-if="persona.enneagram?.type"
512
+ :label="`E${persona.enneagram.type}${persona.enneagram.wing ? 'w' + persona.enneagram.wing : ''}`"
513
+ variant="outline"
514
+ size="xs"
515
+ />
516
+ </div>
517
+
518
+ <!-- Expertise domains -->
519
+ <div v-if="persona.expertise_domains?.length" class="flex flex-wrap gap-1">
520
+ <UBadge
521
+ v-for="domain in persona.expertise_domains.slice(0, 3)"
522
+ :key="domain"
523
+ :label="domain"
524
+ variant="outline"
525
+ size="xs"
526
+ color="neutral"
527
+ />
528
+ <UBadge
529
+ v-if="persona.expertise_domains.length > 3"
530
+ :label="`+${persona.expertise_domains.length - 3}`"
531
+ variant="outline"
532
+ size="xs"
533
+ color="neutral"
534
+ />
535
+ </div>
536
+
537
+ <!-- Actions -->
538
+ <div class="pt-3 mt-auto border-t border-default space-y-3">
539
+ <div class="flex gap-2">
540
+ <UButton
541
+ label="Clone to Agent"
542
+ icon="i-lucide-copy"
543
+ size="sm"
544
+ variant="solid"
545
+ class="flex-1"
546
+ @click="toggleClone(persona)"
547
+ />
548
+ <UButton
549
+ icon="i-lucide-trash-2"
550
+ size="sm"
551
+ variant="ghost"
552
+ color="error"
553
+ :loading="deleting === persona.id"
554
+ aria-label="Delete persona"
555
+ @click="deletePersona(persona)"
556
+ />
557
+ </div>
558
+
559
+ <!-- Inline clone expansion -->
560
+ <div v-if="cloneExpandedId === persona.id" class="space-y-3 pt-2">
561
+ <UFormField label="Department" required>
562
+ <USelect
563
+ v-model="cloneDepartment"
564
+ :items="departmentOptions"
565
+ placeholder="Select department"
566
+ aria-label="Target department"
567
+ class="w-full"
568
+ />
569
+ </UFormField>
570
+ <UFormField label="Tier" required>
571
+ <USelect
572
+ v-model="cloneTier"
573
+ :items="tierOptions"
574
+ placeholder="Select tier"
575
+ aria-label="Agent tier"
576
+ class="w-full"
577
+ />
578
+ </UFormField>
579
+ <UButton
580
+ label="Confirm Clone"
581
+ icon="i-lucide-check"
582
+ size="sm"
583
+ block
584
+ :loading="cloning"
585
+ :disabled="!cloneDepartment || !cloneTier"
586
+ @click="cloneToAgent(persona)"
587
+ />
588
+ </div>
589
+ </div>
590
+ </div>
591
+ </UCard>
592
+ </div>
593
+ </template>
594
+ </div>
595
+ </template>
596
+ </UDashboardPanel>
597
+ </template>