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.
- package/VERSION +1 -1
- package/arka/skills/conclave/SKILL.md +194 -0
- package/arka/skills/human-writing/SKILL.md +143 -0
- package/config/agent-memory-template.md +28 -0
- package/config/disc-profiles.json +108 -0
- package/config/disc-team-validator.sh +94 -0
- package/config/gotchas-fixes.json +148 -0
- package/config/profile-template.json +12 -0
- package/config/providers-registry.json +56 -0
- package/config/settings-template.json +42 -0
- package/config/standards/communication.md +64 -0
- package/config/standards/orchestration.md +91 -0
- package/config/statusline-v2.sh +101 -0
- package/config/statusline.sh +139 -0
- package/config/system-prompt.sh +190 -0
- package/dashboard/LICENSE +21 -0
- package/dashboard/README.md +64 -0
- package/dashboard/app/app.config.ts +8 -0
- package/dashboard/app/app.vue +42 -0
- package/dashboard/app/assets/css/main.css +18 -0
- package/dashboard/app/composables/useApi.ts +8 -0
- package/dashboard/app/composables/useDashboard.ts +19 -0
- package/dashboard/app/error.vue +24 -0
- package/dashboard/app/layouts/default.vue +114 -0
- package/dashboard/app/pages/agents/[id].vue +506 -0
- package/dashboard/app/pages/agents/index.vue +225 -0
- package/dashboard/app/pages/budget.vue +132 -0
- package/dashboard/app/pages/commands.vue +180 -0
- package/dashboard/app/pages/health.vue +98 -0
- package/dashboard/app/pages/index.vue +126 -0
- package/dashboard/app/pages/knowledge.vue +729 -0
- package/dashboard/app/pages/personas.vue +597 -0
- package/dashboard/app/pages/settings.vue +146 -0
- package/dashboard/app/pages/tasks.vue +203 -0
- package/dashboard/app/types/index.d.ts +181 -0
- package/dashboard/app/utils/index.ts +7 -0
- package/dashboard/nuxt.config.ts +39 -0
- package/dashboard/package.json +37 -0
- package/dashboard/pnpm-workspace.yaml +7 -0
- package/dashboard/tsconfig.json +10 -0
- package/knowledge/INDEX.md +34 -0
- package/knowledge/agents-registry.json +254 -0
- package/knowledge/channels-config.json +6 -0
- package/knowledge/commands-keywords.json +466 -0
- package/knowledge/commands-registry.json +2791 -0
- package/knowledge/commands-registry.json.bak +2791 -0
- package/knowledge/ecosystems.json +7 -0
- package/knowledge/obsidian-config.json +112 -0
- package/package.json +10 -6
- package/pyproject.toml +1 -1
- package/scripts/check-version.js +13 -0
- package/scripts/dashboard-api.py +636 -0
- package/scripts/knowledge-index.py +113 -0
- package/scripts/skill_validator.py +217 -0
- package/scripts/start-dashboard.sh +103 -0
- package/scripts/synapse-bridge.py +199 -0
- package/scripts/tools/brand_voice_analyzer.py +192 -0
- package/scripts/tools/dcf_calculator.py +168 -0
- package/scripts/tools/headline_scorer.py +215 -0
- package/scripts/tools/okr_cascade.py +207 -0
- package/scripts/tools/rice_prioritizer.py +230 -0
- package/scripts/tools/saas_metrics.py +234 -0
- package/scripts/tools/seo_checker.py +197 -0
- 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>
|