claude-code-orchestrator-kit 1.4.1 ā 1.4.15
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/.claude/agents/business/workers/lead-research-assistant.md +199 -0
- package/.claude/agents/database/workers/database-architect.md +3 -3
- package/.claude/agents/database/workers/supabase-auditor.md +7 -7
- package/.claude/agents/development/workers/code-reviewer.md +17 -2
- package/.claude/agents/frontend/workers/nextjs-ui-designer.md +30 -0
- package/.claude/agents/health/workers/bug-fixer.md +31 -2
- package/.claude/agents/health/workers/bug-hunter.md +0 -1
- package/.claude/agents/health/workers/dead-code-hunter.md +167 -75
- package/.claude/agents/health/workers/dead-code-remover.md +217 -66
- package/.claude/agents/health/workers/dependency-auditor.md +83 -24
- package/.claude/agents/health/workers/dependency-updater.md +0 -1
- package/.claude/agents/health/workers/security-scanner.md +0 -1
- package/.claude/agents/infrastructure/workers/deployment-engineer.md +446 -0
- package/.claude/agents/infrastructure/workers/infrastructure-specialist.md +2 -2
- package/.claude/agents/meta/workers/meta-agent-v3.md +22 -0
- package/.claude/agents/testing/workers/integration-tester.md +1 -1
- package/.claude/agents/testing/workers/test-writer.md +16 -0
- package/.claude/commands/health-bugs.md +14 -281
- package/.claude/commands/health-cleanup.md +14 -281
- package/.claude/commands/health-deps.md +14 -281
- package/.claude/commands/health-metrics.md +51 -709
- package/.claude/commands/health-reuse.md +14 -311
- package/.claude/commands/health-security.md +14 -281
- package/.claude/commands/push.md +17 -3
- package/.claude/commands/speckit.implement.md +0 -11
- package/.claude/commands/worktree.md +150 -0
- package/.claude/scripts/gates/check-bundle-size.sh +0 -0
- package/.claude/scripts/gates/check-coverage.sh +0 -0
- package/.claude/scripts/gates/check-security.sh +0 -0
- package/.claude/scripts/release.sh +469 -94
- package/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
- package/.claude/skills/algorithmic-art/SKILL.md +405 -0
- package/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
- package/.claude/skills/artifacts-builder/LICENSE.txt +202 -0
- package/.claude/skills/artifacts-builder/SKILL.md +74 -0
- package/.claude/skills/artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/.claude/skills/artifacts-builder/scripts/init-artifact.sh +322 -0
- package/.claude/skills/artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/.claude/skills/bug-health-inline/SKILL.md +221 -0
- package/.claude/skills/bug-health-inline/references/worker-prompts.md +182 -0
- package/.claude/skills/canvas-design/LICENSE.txt +202 -0
- package/.claude/skills/canvas-design/SKILL.md +130 -0
- package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
- package/.claude/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/.claude/skills/changelog-generator/SKILL.md +104 -0
- package/.claude/skills/cleanup-health-inline/SKILL.md +224 -0
- package/.claude/skills/code-reviewer/SKILL.md +209 -0
- package/.claude/skills/code-reviewer/references/code_review_checklist.md +103 -0
- package/.claude/skills/code-reviewer/references/coding_standards.md +103 -0
- package/.claude/skills/code-reviewer/references/common_antipatterns.md +103 -0
- package/.claude/skills/code-reviewer/scripts/code_quality_checker.py +114 -0
- package/.claude/skills/code-reviewer/scripts/pr_analyzer.py +114 -0
- package/.claude/skills/code-reviewer/scripts/review_report_generator.py +114 -0
- package/.claude/skills/content-research-writer/SKILL.md +538 -0
- package/.claude/skills/deps-health-inline/SKILL.md +227 -0
- package/.claude/skills/frontend-aesthetics/SKILL.md +51 -396
- package/.claude/skills/git-commit-helper/SKILL.md +203 -0
- package/.claude/skills/lead-research-assistant/SKILL.md +199 -0
- package/.claude/skills/reuse-health-inline/SKILL.md +248 -0
- package/.claude/skills/rollback-changes/SKILL.md +50 -524
- package/.claude/skills/run-quality-gate/SKILL.md +36 -346
- package/.claude/skills/security-health-inline/SKILL.md +224 -0
- package/.claude/skills/senior-devops/SKILL.md +209 -0
- package/.claude/skills/senior-devops/references/cicd_pipeline_guide.md +103 -0
- package/.claude/skills/senior-devops/references/deployment_strategies.md +103 -0
- package/.claude/skills/senior-devops/references/infrastructure_as_code.md +103 -0
- package/.claude/skills/senior-devops/scripts/deployment_manager.py +114 -0
- package/.claude/skills/senior-devops/scripts/pipeline_generator.py +114 -0
- package/.claude/skills/senior-devops/scripts/terraform_scaffolder.py +114 -0
- package/.claude/skills/senior-prompt-engineer/SKILL.md +226 -0
- package/.claude/skills/senior-prompt-engineer/references/agentic_system_design.md +80 -0
- package/.claude/skills/senior-prompt-engineer/references/llm_evaluation_frameworks.md +80 -0
- package/.claude/skills/senior-prompt-engineer/references/prompt_engineering_patterns.md +80 -0
- package/.claude/skills/senior-prompt-engineer/scripts/agent_orchestrator.py +100 -0
- package/.claude/skills/senior-prompt-engineer/scripts/prompt_optimizer.py +100 -0
- package/.claude/skills/senior-prompt-engineer/scripts/rag_evaluator.py +100 -0
- package/.claude/skills/setup-knip/SKILL.md +372 -0
- package/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/.claude/skills/systematic-debugging/SKILL.md +296 -0
- package/.claude/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/.claude/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/.claude/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/.claude/skills/systematic-debugging/find-polluter.sh +63 -0
- package/.claude/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/.claude/skills/systematic-debugging/test-academic.md +14 -0
- package/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/.claude/skills/theme-factory/LICENSE.txt +202 -0
- package/.claude/skills/theme-factory/SKILL.md +59 -0
- package/.claude/skills/theme-factory/theme-showcase.pdf +0 -0
- package/.claude/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/.claude/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/.claude/skills/theme-factory/themes/desert-rose.md +19 -0
- package/.claude/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/.claude/skills/theme-factory/themes/golden-hour.md +19 -0
- package/.claude/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/.claude/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/.claude/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/.claude/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/.claude/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/.claude/skills/ui-design-system/SKILL.md +32 -0
- package/.claude/skills/ui-design-system/scripts/design_token_generator.py +529 -0
- package/.claude/skills/ux-researcher-designer/SKILL.md +30 -0
- package/.claude/skills/ux-researcher-designer/scripts/persona_generator.py +508 -0
- package/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/.claude/skills/webapp-testing/SKILL.md +96 -0
- package/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/.gitignore +4 -0
- package/README.md +492 -1093
- package/README.ru.md +719 -0
- package/docs/Agents Ecosystem/AGENT-ORCHESTRATION.md +2 -2
- package/docs/COMMANDS-GUIDE.md +0 -15
- package/docs/reports/skills/new-skills-analysis-2025-12.md +331 -0
- package/package.json +11 -3
- package/.claude/agents/health/orchestrators/bug-orchestrator.md +0 -1084
- package/.claude/agents/health/orchestrators/dead-code-orchestrator.md +0 -1064
- package/.claude/agents/health/orchestrators/dependency-orchestrator.md +0 -1064
- package/.claude/agents/health/orchestrators/reuse-orchestrator.md +0 -1112
- package/.claude/agents/health/orchestrators/security-orchestrator.md +0 -1064
- package/.claude/commands/worktree-cleanup.md +0 -382
- package/.claude/commands/worktree-create.md +0 -287
- package/.claude/commands/worktree-list.md +0 -239
- package/.claude/commands/worktree-remove.md +0 -339
- package/.claude/project-index.md +0 -75
- package/.claude/skills/load-project-context/SKILL.md +0 -89
- package/.claude/skills/resume-session/SKILL.md +0 -164
- package/.claude/skills/save-session-context/SKILL.md +0 -123
- package/.claude/templates/project-index.template.md +0 -67
- package/.claude/templates/session/context.template.md +0 -40
- package/.claude/templates/session/log.template.md +0 -72
- package/.github/BRANCH_PROTECTION.md +0 -137
- package/.github/workflows/build.yml +0 -70
- package/.github/workflows/deploy-staging.yml +0 -90
- package/.github/workflows/test.yml +0 -104
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Data-Driven Persona Generator
|
|
4
|
+
Creates research-backed user personas from user data and interviews
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, List, Tuple
|
|
9
|
+
from collections import Counter, defaultdict
|
|
10
|
+
import random
|
|
11
|
+
|
|
12
|
+
class PersonaGenerator:
|
|
13
|
+
"""Generate data-driven personas from user research"""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.persona_components = {
|
|
17
|
+
'demographics': ['age', 'location', 'occupation', 'education', 'income'],
|
|
18
|
+
'psychographics': ['goals', 'frustrations', 'motivations', 'values'],
|
|
19
|
+
'behaviors': ['tech_savviness', 'usage_frequency', 'preferred_devices', 'key_activities'],
|
|
20
|
+
'needs': ['functional', 'emotional', 'social']
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
self.archetype_templates = {
|
|
24
|
+
'power_user': {
|
|
25
|
+
'characteristics': ['tech-savvy', 'frequent user', 'early adopter', 'efficiency-focused'],
|
|
26
|
+
'goals': ['maximize productivity', 'automate workflows', 'access advanced features'],
|
|
27
|
+
'frustrations': ['slow performance', 'limited customization', 'lack of shortcuts'],
|
|
28
|
+
'quote': "I need tools that can keep up with my workflow"
|
|
29
|
+
},
|
|
30
|
+
'casual_user': {
|
|
31
|
+
'characteristics': ['occasional user', 'basic needs', 'prefers simplicity'],
|
|
32
|
+
'goals': ['accomplish specific tasks', 'easy to use', 'minimal learning curve'],
|
|
33
|
+
'frustrations': ['complexity', 'too many options', 'unclear navigation'],
|
|
34
|
+
'quote': "I just want it to work without having to think about it"
|
|
35
|
+
},
|
|
36
|
+
'business_user': {
|
|
37
|
+
'characteristics': ['professional context', 'ROI-focused', 'team collaboration'],
|
|
38
|
+
'goals': ['improve team efficiency', 'track metrics', 'integrate with tools'],
|
|
39
|
+
'frustrations': ['lack of reporting', 'poor collaboration features', 'no enterprise features'],
|
|
40
|
+
'quote': "I need to show clear value to my stakeholders"
|
|
41
|
+
},
|
|
42
|
+
'mobile_first': {
|
|
43
|
+
'characteristics': ['primarily mobile', 'on-the-go usage', 'quick interactions'],
|
|
44
|
+
'goals': ['access anywhere', 'quick actions', 'offline capability'],
|
|
45
|
+
'frustrations': ['poor mobile experience', 'desktop-only features', 'slow loading'],
|
|
46
|
+
'quote': "My phone is my primary computing device"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def generate_persona_from_data(self, user_data: List[Dict],
|
|
51
|
+
interview_insights: List[Dict] = None) -> Dict:
|
|
52
|
+
"""Generate persona from user data and optional interview insights"""
|
|
53
|
+
|
|
54
|
+
# Analyze user data for patterns
|
|
55
|
+
patterns = self._analyze_user_patterns(user_data)
|
|
56
|
+
|
|
57
|
+
# Identify persona archetype
|
|
58
|
+
archetype = self._identify_archetype(patterns)
|
|
59
|
+
|
|
60
|
+
# Generate persona
|
|
61
|
+
persona = {
|
|
62
|
+
'name': self._generate_name(archetype),
|
|
63
|
+
'archetype': archetype,
|
|
64
|
+
'tagline': self._generate_tagline(patterns),
|
|
65
|
+
'demographics': self._aggregate_demographics(user_data),
|
|
66
|
+
'psychographics': self._extract_psychographics(patterns, interview_insights),
|
|
67
|
+
'behaviors': self._analyze_behaviors(user_data),
|
|
68
|
+
'needs_and_goals': self._identify_needs(patterns, interview_insights),
|
|
69
|
+
'frustrations': self._extract_frustrations(patterns, interview_insights),
|
|
70
|
+
'scenarios': self._generate_scenarios(archetype, patterns),
|
|
71
|
+
'quote': self._select_quote(interview_insights, archetype),
|
|
72
|
+
'data_points': self._calculate_data_points(user_data),
|
|
73
|
+
'design_implications': self._derive_design_implications(patterns)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return persona
|
|
77
|
+
|
|
78
|
+
def _analyze_user_patterns(self, user_data: List[Dict]) -> Dict:
|
|
79
|
+
"""Analyze patterns in user data"""
|
|
80
|
+
|
|
81
|
+
patterns = {
|
|
82
|
+
'usage_frequency': defaultdict(int),
|
|
83
|
+
'feature_usage': defaultdict(int),
|
|
84
|
+
'devices': defaultdict(int),
|
|
85
|
+
'contexts': defaultdict(int),
|
|
86
|
+
'pain_points': [],
|
|
87
|
+
'success_metrics': []
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for user in user_data:
|
|
91
|
+
# Frequency patterns
|
|
92
|
+
freq = user.get('usage_frequency', 'medium')
|
|
93
|
+
patterns['usage_frequency'][freq] += 1
|
|
94
|
+
|
|
95
|
+
# Feature usage
|
|
96
|
+
for feature in user.get('features_used', []):
|
|
97
|
+
patterns['feature_usage'][feature] += 1
|
|
98
|
+
|
|
99
|
+
# Device patterns
|
|
100
|
+
device = user.get('primary_device', 'desktop')
|
|
101
|
+
patterns['devices'][device] += 1
|
|
102
|
+
|
|
103
|
+
# Context patterns
|
|
104
|
+
context = user.get('usage_context', 'work')
|
|
105
|
+
patterns['contexts'][context] += 1
|
|
106
|
+
|
|
107
|
+
# Pain points
|
|
108
|
+
if 'pain_points' in user:
|
|
109
|
+
patterns['pain_points'].extend(user['pain_points'])
|
|
110
|
+
|
|
111
|
+
return patterns
|
|
112
|
+
|
|
113
|
+
def _identify_archetype(self, patterns: Dict) -> str:
|
|
114
|
+
"""Identify persona archetype based on patterns"""
|
|
115
|
+
|
|
116
|
+
# Simple heuristic-based archetype identification
|
|
117
|
+
freq_pattern = max(patterns['usage_frequency'].items(), key=lambda x: x[1])[0] if patterns['usage_frequency'] else 'medium'
|
|
118
|
+
device_pattern = max(patterns['devices'].items(), key=lambda x: x[1])[0] if patterns['devices'] else 'desktop'
|
|
119
|
+
|
|
120
|
+
if freq_pattern == 'daily' and len(patterns['feature_usage']) > 10:
|
|
121
|
+
return 'power_user'
|
|
122
|
+
elif device_pattern in ['mobile', 'tablet']:
|
|
123
|
+
return 'mobile_first'
|
|
124
|
+
elif patterns['contexts'].get('work', 0) > patterns['contexts'].get('personal', 0):
|
|
125
|
+
return 'business_user'
|
|
126
|
+
else:
|
|
127
|
+
return 'casual_user'
|
|
128
|
+
|
|
129
|
+
def _generate_name(self, archetype: str) -> str:
|
|
130
|
+
"""Generate persona name based on archetype"""
|
|
131
|
+
|
|
132
|
+
names = {
|
|
133
|
+
'power_user': ['Alex', 'Sam', 'Jordan', 'Morgan'],
|
|
134
|
+
'casual_user': ['Pat', 'Jamie', 'Casey', 'Riley'],
|
|
135
|
+
'business_user': ['Taylor', 'Cameron', 'Avery', 'Blake'],
|
|
136
|
+
'mobile_first': ['Quinn', 'Skylar', 'River', 'Sage']
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
name_pool = names.get(archetype, names['casual_user'])
|
|
140
|
+
first_name = random.choice(name_pool)
|
|
141
|
+
|
|
142
|
+
roles = {
|
|
143
|
+
'power_user': 'the Power User',
|
|
144
|
+
'casual_user': 'the Casual User',
|
|
145
|
+
'business_user': 'the Business Professional',
|
|
146
|
+
'mobile_first': 'the Mobile Native'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return f"{first_name} {roles[archetype]}"
|
|
150
|
+
|
|
151
|
+
def _generate_tagline(self, patterns: Dict) -> str:
|
|
152
|
+
"""Generate persona tagline"""
|
|
153
|
+
|
|
154
|
+
freq = max(patterns['usage_frequency'].items(), key=lambda x: x[1])[0] if patterns['usage_frequency'] else 'regular'
|
|
155
|
+
context = max(patterns['contexts'].items(), key=lambda x: x[1])[0] if patterns['contexts'] else 'general'
|
|
156
|
+
|
|
157
|
+
return f"A {freq} user who primarily uses the product for {context} purposes"
|
|
158
|
+
|
|
159
|
+
def _aggregate_demographics(self, user_data: List[Dict]) -> Dict:
|
|
160
|
+
"""Aggregate demographic information"""
|
|
161
|
+
|
|
162
|
+
demographics = {
|
|
163
|
+
'age_range': '',
|
|
164
|
+
'location_type': '',
|
|
165
|
+
'occupation_category': '',
|
|
166
|
+
'education_level': '',
|
|
167
|
+
'tech_proficiency': ''
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if not user_data:
|
|
171
|
+
return demographics
|
|
172
|
+
|
|
173
|
+
# Age range
|
|
174
|
+
ages = [u.get('age', 30) for u in user_data if 'age' in u]
|
|
175
|
+
if ages:
|
|
176
|
+
avg_age = sum(ages) / len(ages)
|
|
177
|
+
if avg_age < 25:
|
|
178
|
+
demographics['age_range'] = '18-24'
|
|
179
|
+
elif avg_age < 35:
|
|
180
|
+
demographics['age_range'] = '25-34'
|
|
181
|
+
elif avg_age < 45:
|
|
182
|
+
demographics['age_range'] = '35-44'
|
|
183
|
+
else:
|
|
184
|
+
demographics['age_range'] = '45+'
|
|
185
|
+
|
|
186
|
+
# Location type
|
|
187
|
+
locations = [u.get('location_type', 'urban') for u in user_data if 'location_type' in u]
|
|
188
|
+
if locations:
|
|
189
|
+
demographics['location_type'] = Counter(locations).most_common(1)[0][0]
|
|
190
|
+
|
|
191
|
+
# Tech proficiency
|
|
192
|
+
tech_scores = [u.get('tech_proficiency', 5) for u in user_data if 'tech_proficiency' in u]
|
|
193
|
+
if tech_scores:
|
|
194
|
+
avg_tech = sum(tech_scores) / len(tech_scores)
|
|
195
|
+
if avg_tech < 3:
|
|
196
|
+
demographics['tech_proficiency'] = 'Beginner'
|
|
197
|
+
elif avg_tech < 7:
|
|
198
|
+
demographics['tech_proficiency'] = 'Intermediate'
|
|
199
|
+
else:
|
|
200
|
+
demographics['tech_proficiency'] = 'Advanced'
|
|
201
|
+
|
|
202
|
+
return demographics
|
|
203
|
+
|
|
204
|
+
def _extract_psychographics(self, patterns: Dict, interviews: List[Dict] = None) -> Dict:
|
|
205
|
+
"""Extract psychographic information"""
|
|
206
|
+
|
|
207
|
+
psychographics = {
|
|
208
|
+
'motivations': [],
|
|
209
|
+
'values': [],
|
|
210
|
+
'attitudes': [],
|
|
211
|
+
'lifestyle': ''
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Extract from patterns
|
|
215
|
+
if patterns['usage_frequency'].get('daily', 0) > 0:
|
|
216
|
+
psychographics['motivations'].append('Efficiency')
|
|
217
|
+
psychographics['values'].append('Time-saving')
|
|
218
|
+
|
|
219
|
+
if patterns['devices'].get('mobile', 0) > patterns['devices'].get('desktop', 0):
|
|
220
|
+
psychographics['lifestyle'] = 'On-the-go, mobile-first'
|
|
221
|
+
psychographics['values'].append('Flexibility')
|
|
222
|
+
|
|
223
|
+
# Extract from interviews if available
|
|
224
|
+
if interviews:
|
|
225
|
+
for interview in interviews:
|
|
226
|
+
if 'motivations' in interview:
|
|
227
|
+
psychographics['motivations'].extend(interview['motivations'])
|
|
228
|
+
if 'values' in interview:
|
|
229
|
+
psychographics['values'].extend(interview['values'])
|
|
230
|
+
|
|
231
|
+
# Deduplicate
|
|
232
|
+
psychographics['motivations'] = list(set(psychographics['motivations']))[:5]
|
|
233
|
+
psychographics['values'] = list(set(psychographics['values']))[:5]
|
|
234
|
+
|
|
235
|
+
return psychographics
|
|
236
|
+
|
|
237
|
+
def _analyze_behaviors(self, user_data: List[Dict]) -> Dict:
|
|
238
|
+
"""Analyze user behaviors"""
|
|
239
|
+
|
|
240
|
+
behaviors = {
|
|
241
|
+
'usage_patterns': [],
|
|
242
|
+
'feature_preferences': [],
|
|
243
|
+
'interaction_style': '',
|
|
244
|
+
'learning_preference': ''
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if not user_data:
|
|
248
|
+
return behaviors
|
|
249
|
+
|
|
250
|
+
# Usage patterns
|
|
251
|
+
frequencies = [u.get('usage_frequency', 'medium') for u in user_data]
|
|
252
|
+
freq_counter = Counter(frequencies)
|
|
253
|
+
behaviors['usage_patterns'] = [f"{freq}: {count} users" for freq, count in freq_counter.most_common(3)]
|
|
254
|
+
|
|
255
|
+
# Feature preferences
|
|
256
|
+
all_features = []
|
|
257
|
+
for user in user_data:
|
|
258
|
+
all_features.extend(user.get('features_used', []))
|
|
259
|
+
|
|
260
|
+
feature_counter = Counter(all_features)
|
|
261
|
+
behaviors['feature_preferences'] = [feat for feat, count in feature_counter.most_common(5)]
|
|
262
|
+
|
|
263
|
+
# Interaction style
|
|
264
|
+
if len(behaviors['feature_preferences']) > 10:
|
|
265
|
+
behaviors['interaction_style'] = 'Exploratory - uses many features'
|
|
266
|
+
else:
|
|
267
|
+
behaviors['interaction_style'] = 'Focused - uses core features'
|
|
268
|
+
|
|
269
|
+
return behaviors
|
|
270
|
+
|
|
271
|
+
def _identify_needs(self, patterns: Dict, interviews: List[Dict] = None) -> Dict:
|
|
272
|
+
"""Identify user needs and goals"""
|
|
273
|
+
|
|
274
|
+
needs = {
|
|
275
|
+
'primary_goals': [],
|
|
276
|
+
'secondary_goals': [],
|
|
277
|
+
'functional_needs': [],
|
|
278
|
+
'emotional_needs': []
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
# Derive from usage patterns
|
|
282
|
+
if patterns['usage_frequency'].get('daily', 0) > 0:
|
|
283
|
+
needs['primary_goals'].append('Complete tasks efficiently')
|
|
284
|
+
needs['functional_needs'].append('Speed and performance')
|
|
285
|
+
|
|
286
|
+
if patterns['contexts'].get('work', 0) > 0:
|
|
287
|
+
needs['primary_goals'].append('Professional productivity')
|
|
288
|
+
needs['functional_needs'].append('Integration with work tools')
|
|
289
|
+
|
|
290
|
+
# Common emotional needs
|
|
291
|
+
needs['emotional_needs'] = [
|
|
292
|
+
'Feel confident using the product',
|
|
293
|
+
'Trust the system with data',
|
|
294
|
+
'Feel supported when issues arise'
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
# Extract from interviews
|
|
298
|
+
if interviews:
|
|
299
|
+
for interview in interviews:
|
|
300
|
+
if 'goals' in interview:
|
|
301
|
+
needs['primary_goals'].extend(interview['goals'][:2])
|
|
302
|
+
if 'needs' in interview:
|
|
303
|
+
needs['functional_needs'].extend(interview['needs'][:3])
|
|
304
|
+
|
|
305
|
+
return needs
|
|
306
|
+
|
|
307
|
+
def _extract_frustrations(self, patterns: Dict, interviews: List[Dict] = None) -> List[str]:
|
|
308
|
+
"""Extract user frustrations"""
|
|
309
|
+
|
|
310
|
+
frustrations = []
|
|
311
|
+
|
|
312
|
+
# Common frustrations from patterns
|
|
313
|
+
if patterns['pain_points']:
|
|
314
|
+
frustration_counter = Counter(patterns['pain_points'])
|
|
315
|
+
frustrations = [pain for pain, count in frustration_counter.most_common(5)]
|
|
316
|
+
|
|
317
|
+
# Add archetype-specific frustrations if not enough from data
|
|
318
|
+
if len(frustrations) < 3:
|
|
319
|
+
frustrations.extend([
|
|
320
|
+
'Slow loading times',
|
|
321
|
+
'Confusing navigation',
|
|
322
|
+
'Lack of mobile optimization'
|
|
323
|
+
])
|
|
324
|
+
|
|
325
|
+
return frustrations[:5]
|
|
326
|
+
|
|
327
|
+
def _generate_scenarios(self, archetype: str, patterns: Dict) -> List[Dict]:
|
|
328
|
+
"""Generate usage scenarios"""
|
|
329
|
+
|
|
330
|
+
scenarios = []
|
|
331
|
+
|
|
332
|
+
# Common scenarios based on archetype
|
|
333
|
+
scenario_templates = {
|
|
334
|
+
'power_user': [
|
|
335
|
+
{
|
|
336
|
+
'title': 'Bulk Processing',
|
|
337
|
+
'context': 'Monday morning, needs to process week\'s data',
|
|
338
|
+
'goal': 'Complete batch operations quickly',
|
|
339
|
+
'steps': ['Import data', 'Apply bulk actions', 'Export results'],
|
|
340
|
+
'pain_points': ['No keyboard shortcuts', 'Slow processing']
|
|
341
|
+
}
|
|
342
|
+
],
|
|
343
|
+
'casual_user': [
|
|
344
|
+
{
|
|
345
|
+
'title': 'Quick Task',
|
|
346
|
+
'context': 'Needs to complete single task',
|
|
347
|
+
'goal': 'Get in, complete task, get out',
|
|
348
|
+
'steps': ['Find feature', 'Complete task', 'Save/Exit'],
|
|
349
|
+
'pain_points': ['Can\'t find feature', 'Too many steps']
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
'business_user': [
|
|
353
|
+
{
|
|
354
|
+
'title': 'Team Collaboration',
|
|
355
|
+
'context': 'Working with team on project',
|
|
356
|
+
'goal': 'Share and collaborate efficiently',
|
|
357
|
+
'steps': ['Create content', 'Share with team', 'Track feedback'],
|
|
358
|
+
'pain_points': ['No real-time collaboration', 'Poor permission management']
|
|
359
|
+
}
|
|
360
|
+
],
|
|
361
|
+
'mobile_first': [
|
|
362
|
+
{
|
|
363
|
+
'title': 'On-the-Go Access',
|
|
364
|
+
'context': 'Commuting, needs quick access',
|
|
365
|
+
'goal': 'Complete task on mobile',
|
|
366
|
+
'steps': ['Open mobile app', 'Quick action', 'Sync with desktop'],
|
|
367
|
+
'pain_points': ['Feature parity issues', 'Poor mobile UX']
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return scenario_templates.get(archetype, scenario_templates['casual_user'])
|
|
373
|
+
|
|
374
|
+
def _select_quote(self, interviews: List[Dict] = None, archetype: str = 'casual_user') -> str:
|
|
375
|
+
"""Select representative quote"""
|
|
376
|
+
|
|
377
|
+
if interviews:
|
|
378
|
+
# Try to find a real quote
|
|
379
|
+
for interview in interviews:
|
|
380
|
+
if 'quotes' in interview and interview['quotes']:
|
|
381
|
+
return interview['quotes'][0]
|
|
382
|
+
|
|
383
|
+
# Use archetype default
|
|
384
|
+
return self.archetype_templates[archetype]['quote']
|
|
385
|
+
|
|
386
|
+
def _calculate_data_points(self, user_data: List[Dict]) -> Dict:
|
|
387
|
+
"""Calculate supporting data points"""
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
'sample_size': len(user_data),
|
|
391
|
+
'confidence_level': 'High' if len(user_data) > 50 else 'Medium' if len(user_data) > 20 else 'Low',
|
|
392
|
+
'last_updated': 'Current',
|
|
393
|
+
'validation_method': 'Quantitative analysis + Qualitative interviews'
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
def _derive_design_implications(self, patterns: Dict) -> List[str]:
|
|
397
|
+
"""Derive design implications from persona"""
|
|
398
|
+
|
|
399
|
+
implications = []
|
|
400
|
+
|
|
401
|
+
# Based on frequency
|
|
402
|
+
if patterns['usage_frequency'].get('daily', 0) > patterns['usage_frequency'].get('weekly', 0):
|
|
403
|
+
implications.append('Optimize for speed and efficiency')
|
|
404
|
+
implications.append('Provide keyboard shortcuts and power features')
|
|
405
|
+
else:
|
|
406
|
+
implications.append('Focus on discoverability and guidance')
|
|
407
|
+
implications.append('Simplify onboarding experience')
|
|
408
|
+
|
|
409
|
+
# Based on device
|
|
410
|
+
if patterns['devices'].get('mobile', 0) > 0:
|
|
411
|
+
implications.append('Mobile-first responsive design')
|
|
412
|
+
implications.append('Touch-optimized interactions')
|
|
413
|
+
|
|
414
|
+
# Based on context
|
|
415
|
+
if patterns['contexts'].get('work', 0) > patterns['contexts'].get('personal', 0):
|
|
416
|
+
implications.append('Professional visual design')
|
|
417
|
+
implications.append('Enterprise features (SSO, audit logs)')
|
|
418
|
+
|
|
419
|
+
return implications[:5]
|
|
420
|
+
|
|
421
|
+
def format_persona_output(self, persona: Dict) -> str:
|
|
422
|
+
"""Format persona for display"""
|
|
423
|
+
|
|
424
|
+
output = []
|
|
425
|
+
output.append("=" * 60)
|
|
426
|
+
output.append(f"PERSONA: {persona['name']}")
|
|
427
|
+
output.append("=" * 60)
|
|
428
|
+
output.append(f"\nš {persona['tagline']}\n")
|
|
429
|
+
|
|
430
|
+
output.append(f"Archetype: {persona['archetype'].replace('_', ' ').title()}")
|
|
431
|
+
output.append(f"Quote: \"{persona['quote']}\"\n")
|
|
432
|
+
|
|
433
|
+
output.append("š¤ Demographics:")
|
|
434
|
+
for key, value in persona['demographics'].items():
|
|
435
|
+
if value:
|
|
436
|
+
output.append(f" ⢠{key.replace('_', ' ').title()}: {value}")
|
|
437
|
+
|
|
438
|
+
output.append("\nš§ Psychographics:")
|
|
439
|
+
if persona['psychographics']['motivations']:
|
|
440
|
+
output.append(f" Motivations: {', '.join(persona['psychographics']['motivations'])}")
|
|
441
|
+
if persona['psychographics']['values']:
|
|
442
|
+
output.append(f" Values: {', '.join(persona['psychographics']['values'])}")
|
|
443
|
+
|
|
444
|
+
output.append("\nšÆ Goals & Needs:")
|
|
445
|
+
for goal in persona['needs_and_goals'].get('primary_goals', [])[:3]:
|
|
446
|
+
output.append(f" ⢠{goal}")
|
|
447
|
+
|
|
448
|
+
output.append("\nš¤ Frustrations:")
|
|
449
|
+
for frustration in persona['frustrations'][:3]:
|
|
450
|
+
output.append(f" ⢠{frustration}")
|
|
451
|
+
|
|
452
|
+
output.append("\nš Behaviors:")
|
|
453
|
+
for pref in persona['behaviors'].get('feature_preferences', [])[:3]:
|
|
454
|
+
output.append(f" ⢠Frequently uses: {pref}")
|
|
455
|
+
|
|
456
|
+
output.append("\nš” Design Implications:")
|
|
457
|
+
for implication in persona['design_implications']:
|
|
458
|
+
output.append(f" ā {implication}")
|
|
459
|
+
|
|
460
|
+
output.append(f"\nš Data: Based on {persona['data_points']['sample_size']} users")
|
|
461
|
+
output.append(f" Confidence: {persona['data_points']['confidence_level']}")
|
|
462
|
+
|
|
463
|
+
return "\n".join(output)
|
|
464
|
+
|
|
465
|
+
def create_sample_user_data():
|
|
466
|
+
"""Create sample user data for testing"""
|
|
467
|
+
return [
|
|
468
|
+
{
|
|
469
|
+
'user_id': f'user_{i}',
|
|
470
|
+
'age': 25 + (i % 30),
|
|
471
|
+
'usage_frequency': ['daily', 'weekly', 'monthly'][i % 3],
|
|
472
|
+
'features_used': ['dashboard', 'reports', 'settings', 'sharing', 'export'][:3 + (i % 3)],
|
|
473
|
+
'primary_device': ['desktop', 'mobile', 'tablet'][i % 3],
|
|
474
|
+
'usage_context': ['work', 'personal'][i % 2],
|
|
475
|
+
'tech_proficiency': 3 + (i % 7),
|
|
476
|
+
'pain_points': ['slow loading', 'confusing UI', 'missing features'][:(i % 3) + 1]
|
|
477
|
+
}
|
|
478
|
+
for i in range(30)
|
|
479
|
+
]
|
|
480
|
+
|
|
481
|
+
def main():
|
|
482
|
+
import sys
|
|
483
|
+
|
|
484
|
+
generator = PersonaGenerator()
|
|
485
|
+
|
|
486
|
+
# Create sample data
|
|
487
|
+
user_data = create_sample_user_data()
|
|
488
|
+
|
|
489
|
+
# Optional interview insights
|
|
490
|
+
interview_insights = [
|
|
491
|
+
{
|
|
492
|
+
'quotes': ["I need to see all my data in one place"],
|
|
493
|
+
'motivations': ['Efficiency', 'Control'],
|
|
494
|
+
'goals': ['Save time', 'Make better decisions']
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
|
|
498
|
+
# Generate persona
|
|
499
|
+
persona = generator.generate_persona_from_data(user_data, interview_insights)
|
|
500
|
+
|
|
501
|
+
# Output
|
|
502
|
+
if len(sys.argv) > 1 and sys.argv[1] == 'json':
|
|
503
|
+
print(json.dumps(persona, indent=2))
|
|
504
|
+
else:
|
|
505
|
+
print(generator.format_persona_output(persona))
|
|
506
|
+
|
|
507
|
+
if __name__ == "__main__":
|
|
508
|
+
main()
|