@wipal/agent-team 1.0.0

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 (89) hide show
  1. package/.claude/rules/common/general-rules.md +141 -0
  2. package/.claude/rules/lessons/lessons.md +91 -0
  3. package/.claude/rules/role-rules/dev-fe-rules.md +146 -0
  4. package/.claude/rules/role-rules/sa-rules.md +226 -0
  5. package/.claude/skills/SKILL-INDEX.md +299 -0
  6. package/.claude/skills/community/security-validator/SKILL.md +392 -0
  7. package/.claude/skills/core/agent-creation/SKILL.md +338 -0
  8. package/.claude/skills/core/code-review/SKILL.md +154 -0
  9. package/.claude/skills/core/git-automation/SKILL.md +93 -0
  10. package/.claude/skills/core/retrospect-work/SKILL.md +172 -0
  11. package/.claude/skills/domain/architecture/adr-writing/SKILL.md +254 -0
  12. package/.claude/skills/domain/architecture/adr-writing/references/adr-best-practices.md +257 -0
  13. package/.claude/skills/domain/architecture/adr-writing/references/adr-examples.md +246 -0
  14. package/.claude/skills/domain/architecture/adr-writing/references/adr-template.md +160 -0
  15. package/.claude/skills/domain/architecture/architecture-patterns/SKILL.md +316 -0
  16. package/.claude/skills/domain/architecture/architecture-patterns/references/event-driven.md +393 -0
  17. package/.claude/skills/domain/architecture/architecture-patterns/references/microservices.md +315 -0
  18. package/.claude/skills/domain/architecture/architecture-patterns/references/monolith.md +321 -0
  19. package/.claude/skills/domain/architecture/architecture-patterns/references/serverless.md +457 -0
  20. package/.claude/skills/domain/architecture/performance-engineering/SKILL.md +227 -0
  21. package/.claude/skills/domain/architecture/performance-engineering/references/benchmarking.md +336 -0
  22. package/.claude/skills/domain/architecture/performance-engineering/references/caching-strategies.md +284 -0
  23. package/.claude/skills/domain/architecture/performance-engineering/references/optimization.md +298 -0
  24. package/.claude/skills/domain/architecture/security-architecture/SKILL.md +206 -0
  25. package/.claude/skills/domain/architecture/security-architecture/references/auth-patterns.md +209 -0
  26. package/.claude/skills/domain/architecture/security-architecture/references/compliance.md +246 -0
  27. package/.claude/skills/domain/architecture/security-architecture/references/threat-modeling.md +219 -0
  28. package/.claude/skills/domain/architecture/system-design/SKILL.md +227 -0
  29. package/.claude/skills/domain/architecture/system-design/references/distributed-systems.md +231 -0
  30. package/.claude/skills/domain/architecture/system-design/references/resilience.md +344 -0
  31. package/.claude/skills/domain/architecture/system-design/references/scalability.md +303 -0
  32. package/.claude/skills/domain/architecture/tech-selection/SKILL.md +192 -0
  33. package/.claude/skills/domain/architecture/tech-selection/references/build-vs-buy.md +258 -0
  34. package/.claude/skills/domain/architecture/tech-selection/references/evaluation-framework.md +203 -0
  35. package/.claude/skills/domain/architecture/tech-selection/references/tech-radar.md +257 -0
  36. package/.claude/skills/domain/backend/api-design/SKILL.md +121 -0
  37. package/.claude/skills/domain/backend/database-design/SKILL.md +156 -0
  38. package/.claude/skills/domain/backend/performance-be/SKILL.md +210 -0
  39. package/.claude/skills/domain/backend/security/SKILL.md +138 -0
  40. package/.claude/skills/domain/backend/testing-be/SKILL.md +203 -0
  41. package/.claude/skills/domain/devops/ci-cd/SKILL.md +188 -0
  42. package/.claude/skills/domain/devops/containerization/SKILL.md +177 -0
  43. package/.claude/skills/domain/devops/deployment/SKILL.md +198 -0
  44. package/.claude/skills/domain/devops/infrastructure-as-code/SKILL.md +178 -0
  45. package/.claude/skills/domain/devops/monitoring/SKILL.md +163 -0
  46. package/.claude/skills/domain/frontend/accessibility/SKILL.md +179 -0
  47. package/.claude/skills/domain/frontend/frontend-design/SKILL.md +138 -0
  48. package/.claude/skills/domain/frontend/performance-fe/SKILL.md +195 -0
  49. package/.claude/skills/domain/frontend/state-management/SKILL.md +190 -0
  50. package/.claude/skills/domain/frontend/testing-fe/SKILL.md +193 -0
  51. package/.claude/skills/domain/product/requirements-gathering/SKILL.md +136 -0
  52. package/.claude/skills/domain/product/roadmap-planning/SKILL.md +169 -0
  53. package/.claude/skills/domain/product/sprint-planning/SKILL.md +151 -0
  54. package/.claude/skills/domain/product/stakeholder-communication/SKILL.md +162 -0
  55. package/.claude/skills/domain/product/user-stories/SKILL.md +141 -0
  56. package/.claude/skills/domain/quality/bug-reporting/SKILL.md +150 -0
  57. package/.claude/skills/domain/quality/regression-testing/SKILL.md +178 -0
  58. package/.claude/skills/domain/quality/test-automation/SKILL.md +185 -0
  59. package/.claude/skills/domain/quality/test-planning/SKILL.md +177 -0
  60. package/.claude/skills/leadership/code-review-advanced/SKILL.md +167 -0
  61. package/.claude/skills/leadership/mentoring/SKILL.md +151 -0
  62. package/.claude/skills/leadership/technical-debt/SKILL.md +166 -0
  63. package/.claude/skills/leadership/technical-decision/SKILL.md +160 -0
  64. package/.claude/skills/security-reports/.gitkeep +0 -0
  65. package/.claude/skills/skills-registry.yaml +441 -0
  66. package/README.md +232 -0
  67. package/bin/agent-team.js +107 -0
  68. package/package.json +51 -0
  69. package/src/commands/add.js +227 -0
  70. package/src/commands/init.js +136 -0
  71. package/src/commands/list.js +66 -0
  72. package/src/commands/remove.js +71 -0
  73. package/src/commands/switch.js +53 -0
  74. package/src/index.js +11 -0
  75. package/src/interactive/prompts.js +153 -0
  76. package/src/server/api/agents.js +150 -0
  77. package/src/server/api/roles.js +97 -0
  78. package/src/server/api/skills.js +79 -0
  79. package/src/server/index.js +78 -0
  80. package/src/ui/agents.html +174 -0
  81. package/src/ui/css/styles.css +470 -0
  82. package/src/ui/index.html +107 -0
  83. package/src/ui/roles.html +371 -0
  84. package/src/ui/skills.html +332 -0
  85. package/src/utils/file-utils.js +193 -0
  86. package/src/utils/skill-resolver.js +594 -0
  87. package/src/utils/skill-scanner.js +154 -0
  88. package/templates/CLAUDE.md.tmpl +42 -0
  89. package/templates/knowledge.md.tmpl +31 -0
@@ -0,0 +1,594 @@
1
+ /**
2
+ * Skill Resolver - Resolve skills for roles and variants
3
+ * Uses config-driven approach with auto-discovery
4
+ *
5
+ * v2: Supports YAML config + per-project overrides
6
+ */
7
+
8
+ import fs from 'fs-extra';
9
+ import path from 'path';
10
+ import yaml from 'js-yaml';
11
+ import { getPackageRoot, getClaudeDir } from './file-utils.js';
12
+ import { getSkills, getSkillsByCategory, clearSkillsCache } from './skill-scanner.js';
13
+
14
+ // ============================================
15
+ // Config Loading
16
+ // ============================================
17
+
18
+ // Cache for loaded configs
19
+ let defaultRolesConfig = null;
20
+
21
+ /**
22
+ * Load YAML config file
23
+ */
24
+ async function loadYamlConfig(configPath) {
25
+ try {
26
+ if (await fs.exists(configPath)) {
27
+ const content = await fs.readFile(configPath, 'utf-8');
28
+ return yaml.load(content);
29
+ }
30
+ } catch (error) {
31
+ console.warn(`Failed to load config: ${configPath}`, error.message);
32
+ }
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Get roles config (default + per-project override)
38
+ */
39
+ async function getRolesConfig(projectRoot = process.cwd()) {
40
+ // Load default config
41
+ if (!defaultRolesConfig) {
42
+ const defaultPath = path.join(getPackageRoot(), 'config', 'roles.yaml');
43
+ defaultRolesConfig = await loadYamlConfig(defaultPath);
44
+ }
45
+
46
+ // Check for per-project override
47
+ const projectConfigPath = path.join(getClaudeDir(projectRoot), 'roles.yaml');
48
+ const projectConfig = await loadYamlConfig(projectConfigPath);
49
+
50
+ // Merge configs (project overrides default)
51
+ if (projectConfig && defaultRolesConfig) {
52
+ return {
53
+ ...defaultRolesConfig,
54
+ roles: {
55
+ ...defaultRolesConfig.roles,
56
+ ...(projectConfig.roles || {})
57
+ }
58
+ };
59
+ }
60
+
61
+ return defaultRolesConfig || { roles: {} };
62
+ }
63
+
64
+ /**
65
+ * Resolve skills for a role using config
66
+ * @param {string} roleName - Role name
67
+ * @param {Object} options - Options
68
+ * @param {boolean} options.includeOptional - Include optional skills
69
+ * @param {string} options.projectRoot - Project root for per-project config
70
+ * @returns {Promise<Object>} - { core, role, inherited, optional, all } skills
71
+ */
72
+ export async function resolveRoleSkills(roleName, options = {}) {
73
+ const { includeOptional = false, projectRoot = process.cwd() } = options;
74
+ const config = await getRolesConfig(projectRoot);
75
+ const allSkills = await getSkills();
76
+
77
+ const result = {
78
+ core: [],
79
+ role: [],
80
+ inherited: [],
81
+ optional: [],
82
+ all: []
83
+ };
84
+
85
+ // Get role definition
86
+ const roleDef = config.roles?.[roleName];
87
+ if (!roleDef) {
88
+ console.warn(`Role not found: ${roleName}`);
89
+ return result;
90
+ }
91
+
92
+ const skillsConfig = roleDef.skills || {};
93
+
94
+ // 1. Core skills (always included)
95
+ result.core = config.core_skills || ['code-review', 'git-automation', 'retrospect-work'];
96
+
97
+ // 2. Skills by category (include)
98
+ if (skillsConfig.include) {
99
+ for (const rule of skillsConfig.include) {
100
+ if (typeof rule === 'string') {
101
+ // Direct skill name
102
+ const skill = allSkills.find(s => s.name === rule);
103
+ if (skill) result.role.push(skill.name);
104
+ } else if (rule.category) {
105
+ // Category-based
106
+ const categorySkills = getSkillsByCategory(allSkills, rule.category);
107
+ result.role.push(...categorySkills.map(s => s.name));
108
+ }
109
+ }
110
+ }
111
+
112
+ // 3. Inherited skills from parent roles
113
+ if (skillsConfig.inherit && skillsConfig.inherit.length > 0) {
114
+ for (const parentRole of skillsConfig.inherit) {
115
+ const parentSkills = await resolveRoleSkills(parentRole, { projectRoot });
116
+ result.inherited.push(...parentSkills.all);
117
+ }
118
+ }
119
+
120
+ // 4. Optional skills (only if requested)
121
+ if (includeOptional && skillsConfig.optional) {
122
+ for (const rule of skillsConfig.optional) {
123
+ if (rule.category) {
124
+ const categorySkills = getSkillsByCategory(allSkills, rule.category);
125
+ result.optional.push(...categorySkills.map(s => s.name));
126
+ }
127
+ }
128
+ }
129
+
130
+ // 5. Exclude specified skills
131
+ if (skillsConfig.exclude && skillsConfig.exclude.length > 0) {
132
+ for (const exclude of skillsConfig.exclude) {
133
+ if (typeof exclude === 'string') {
134
+ result.role = result.role.filter(s => s !== exclude);
135
+ result.inherited = result.inherited.filter(s => s !== exclude);
136
+ }
137
+ }
138
+ }
139
+
140
+ // Combine all (deduplicated)
141
+ result.all = [...new Set([
142
+ ...result.core,
143
+ ...result.role,
144
+ ...result.inherited,
145
+ ...(includeOptional ? result.optional : [])
146
+ ])];
147
+
148
+ return result;
149
+ }
150
+
151
+ /**
152
+ * Get all skills for a role (simplified async)
153
+ */
154
+ export async function getAllSkillsForRole(roleName, projectRoot = process.cwd()) {
155
+ const resolved = await resolveRoleSkills(roleName, { projectRoot });
156
+ return resolved.all;
157
+ }
158
+
159
+ /**
160
+ * Get available roles with descriptions
161
+ */
162
+ export async function getAvailableRoles(projectRoot = process.cwd()) {
163
+ const config = await getRolesConfig(projectRoot);
164
+ const roles = [];
165
+
166
+ for (const [name, def] of Object.entries(config.roles || {})) {
167
+ roles.push({
168
+ name,
169
+ description: def.description || '',
170
+ inherits: def.skills?.inherit || []
171
+ });
172
+ }
173
+
174
+ return roles;
175
+ }
176
+
177
+ // ============================================
178
+ // Legacy Support (Sync functions for backward compatibility)
179
+ // ============================================
180
+
181
+ /**
182
+ * Core skills - always included for all agents
183
+ */
184
+ const CORE_SKILLS = [
185
+ 'code-review',
186
+ 'git-automation',
187
+ 'retrospect-work'
188
+ ];
189
+
190
+ /**
191
+ * Role to skills mapping (legacy - with inheritance for tech-lead)
192
+ */
193
+ const ROLE_SKILL_MAP = {
194
+ 'dev-fe': [
195
+ 'frontend-design',
196
+ 'accessibility',
197
+ 'state-management',
198
+ 'testing-fe',
199
+ 'performance-fe'
200
+ ],
201
+ 'dev-be': [
202
+ 'api-design',
203
+ 'database-design',
204
+ 'security',
205
+ 'testing-be',
206
+ 'performance-be'
207
+ ],
208
+ 'sa': [
209
+ 'system-design',
210
+ 'architecture-patterns',
211
+ 'adr-writing',
212
+ 'tech-selection',
213
+ 'performance-engineering',
214
+ 'security-architecture'
215
+ ],
216
+ // Tech Lead now inherits from dev-fe + dev-be
217
+ 'tech-lead': [
218
+ // Leadership skills
219
+ 'code-review-advanced',
220
+ 'technical-decision',
221
+ 'mentoring',
222
+ 'technical-debt',
223
+ // Inherited from dev-fe
224
+ 'frontend-design',
225
+ 'accessibility',
226
+ 'state-management',
227
+ 'testing-fe',
228
+ 'performance-fe',
229
+ // Inherited from dev-be
230
+ 'api-design',
231
+ 'database-design',
232
+ 'security',
233
+ 'testing-be',
234
+ 'performance-be'
235
+ ],
236
+ 'devops': [
237
+ 'ci-cd',
238
+ 'containerization',
239
+ 'infrastructure-as-code',
240
+ 'monitoring',
241
+ 'deployment'
242
+ ],
243
+ 'pm': [
244
+ 'requirements-gathering',
245
+ 'user-stories',
246
+ 'sprint-planning',
247
+ 'roadmap-planning',
248
+ 'stakeholder-communication'
249
+ ],
250
+ 'qa': [
251
+ 'test-planning',
252
+ 'bug-reporting',
253
+ 'test-automation',
254
+ 'regression-testing'
255
+ ]
256
+ };
257
+
258
+ /**
259
+ * Variant to skills mapping
260
+ */
261
+ const VARIANT_SKILL_MAP = {
262
+ // Framework - Frontend
263
+ 'framework:react': ['frontend-design'],
264
+ 'framework:vue': ['frontend-design'],
265
+ 'framework:angular': ['frontend-design'],
266
+ 'framework:svelte': ['frontend-design'],
267
+ 'framework:vanilla': ['frontend-design'],
268
+
269
+ // Meta-framework
270
+ 'metaFramework:nextjs': ['frontend-design'],
271
+ 'metaFramework:nuxt': ['frontend-design'],
272
+ 'metaFramework:remix': ['frontend-design'],
273
+ 'metaFramework:astro': ['frontend-design'],
274
+
275
+ // Styling
276
+ 'styling:tailwind': ['frontend-design'],
277
+ 'styling:css-modules': ['frontend-design'],
278
+ 'styling:styled-components': ['frontend-design'],
279
+ 'styling:scss': ['frontend-design'],
280
+
281
+ // State management
282
+ 'state:zustand': ['state-management'],
283
+ 'state:redux': ['state-management'],
284
+ 'state:recoil': ['state-management'],
285
+ 'state:jotai': ['state-management'],
286
+ 'state:mobx': ['state-management'],
287
+ 'state:pinia': ['state-management'],
288
+
289
+ // Data fetching
290
+ 'data:tanstack-query': ['state-management'],
291
+ 'data:swr': ['state-management'],
292
+ 'data:apollo': ['api-design'],
293
+ 'data:urql': ['api-design'],
294
+
295
+ // Testing - Frontend
296
+ 'testing:vitest': ['testing-fe'],
297
+ 'testing:jest': ['testing-fe'],
298
+ 'testing:cypress': ['testing-fe'],
299
+ 'testing:playwright': ['testing-fe'],
300
+ 'testing:testing-library': ['testing-fe'],
301
+
302
+ // Form handling
303
+ 'form:react-hook-form': ['frontend-design'],
304
+ 'form:formik': ['frontend-design'],
305
+ 'form:zod': ['frontend-design'],
306
+
307
+ // UI libraries
308
+ 'uiLib:shadcn': ['frontend-design'],
309
+ 'uiLib:mui': ['frontend-design'],
310
+ 'uiLib:ant-design': ['frontend-design'],
311
+ 'uiLib:chakra': ['frontend-design'],
312
+
313
+ // i18n
314
+ 'i18n:react-i18next': ['frontend-design'],
315
+ 'i18n:formatjs': ['frontend-design'],
316
+ 'i18n:next-intl': ['frontend-design'],
317
+
318
+ // Framework - Backend
319
+ 'framework:nestjs': ['api-design', 'database-design'],
320
+ 'framework:express': ['api-design'],
321
+ 'framework:fastify': ['api-design'],
322
+ 'framework:fastapi': ['api-design'],
323
+ 'framework:django': ['api-design', 'database-design'],
324
+ 'framework:gin': ['api-design'],
325
+ 'framework:echo': ['api-design'],
326
+
327
+ // Database
328
+ 'database:postgresql': ['database-design'],
329
+ 'database:mysql': ['database-design'],
330
+ 'database:mongodb': ['database-design'],
331
+ 'database:redis': ['database-design'],
332
+ 'database:sqlite': ['database-design'],
333
+
334
+ // ORM
335
+ 'orm:prisma': ['database-design'],
336
+ 'orm:typeorm': ['database-design'],
337
+ 'orm:sequelize': ['database-design'],
338
+ 'orm:mikro-orm': ['database-design'],
339
+ 'orm:mongoose': ['database-design'],
340
+
341
+ // Infrastructure
342
+ 'infra:docker': ['containerization'],
343
+ 'infra:kubernetes': ['containerization', 'deployment'],
344
+ 'infra:terraform': ['infrastructure-as-code'],
345
+ 'infra:ansible': ['infrastructure-as-code'],
346
+
347
+ // CI/CD
348
+ 'cicd:github-actions': ['ci-cd'],
349
+ 'cicd:gitlab-ci': ['ci-cd'],
350
+ 'cicd:jenkins': ['ci-cd'],
351
+
352
+ // Monitoring
353
+ 'monitoring:prometheus': ['monitoring'],
354
+ 'monitoring:grafana': ['monitoring'],
355
+ 'monitoring:datadog': ['monitoring'],
356
+ 'monitoring:sentry': ['monitoring']
357
+ };
358
+
359
+ /**
360
+ * Available roles with descriptions (legacy sync)
361
+ */
362
+ export const AVAILABLE_ROLES = [
363
+ { name: 'dev-fe', description: 'Frontend Developer - React, Vue, Angular' },
364
+ { name: 'dev-be', description: 'Backend Developer - Node.js, Python, Go' },
365
+ { name: 'sa', description: 'Solution Architect - System design, ADRs' },
366
+ { name: 'tech-lead', description: 'Tech Lead - Code review, mentoring (includes dev-fe + dev-be skills)' },
367
+ { name: 'devops', description: 'DevOps Engineer - CI/CD, infrastructure' },
368
+ { name: 'pm', description: 'Product Manager - Requirements, sprints' },
369
+ { name: 'qa', description: 'QA Engineer - Testing, automation' }
370
+ ];
371
+
372
+ /**
373
+ * Available variant categories and options
374
+ */
375
+ export const VARIANT_CATEGORIES = {
376
+ framework: {
377
+ label: 'Framework',
378
+ type: 'select',
379
+ options: [
380
+ { value: 'react', label: 'React', roles: ['dev-fe'] },
381
+ { value: 'vue', label: 'Vue', roles: ['dev-fe'] },
382
+ { value: 'angular', label: 'Angular', roles: ['dev-fe'] },
383
+ { value: 'svelte', label: 'Svelte', roles: ['dev-fe'] },
384
+ { value: 'vanilla', label: 'Vanilla JS', roles: ['dev-fe'] },
385
+ { value: 'nestjs', label: 'NestJS', roles: ['dev-be'] },
386
+ { value: 'express', label: 'Express', roles: ['dev-be'] },
387
+ { value: 'fastify', label: 'Fastify', roles: ['dev-be'] },
388
+ { value: 'fastapi', label: 'FastAPI (Python)', roles: ['dev-be'] },
389
+ { value: 'django', label: 'Django (Python)', roles: ['dev-be'] },
390
+ { value: 'gin', label: 'Gin (Go)', roles: ['dev-be'] }
391
+ ]
392
+ },
393
+ metaFramework: {
394
+ label: 'Meta Framework',
395
+ type: 'select',
396
+ options: [
397
+ { value: 'nextjs', label: 'Next.js', roles: ['dev-fe'] },
398
+ { value: 'nuxt', label: 'Nuxt', roles: ['dev-fe'] },
399
+ { value: 'remix', label: 'Remix', roles: ['dev-fe'] },
400
+ { value: 'astro', label: 'Astro', roles: ['dev-fe'] }
401
+ ]
402
+ },
403
+ styling: {
404
+ label: 'Styling',
405
+ type: 'select',
406
+ options: [
407
+ { value: 'tailwind', label: 'TailwindCSS' },
408
+ { value: 'css-modules', label: 'CSS Modules' },
409
+ { value: 'styled-components', label: 'Styled Components' },
410
+ { value: 'scss', label: 'SCSS/Sass' }
411
+ ]
412
+ },
413
+ state: {
414
+ label: 'State Management',
415
+ type: 'select',
416
+ options: [
417
+ { value: 'zustand', label: 'Zustand', roles: ['dev-fe'] },
418
+ { value: 'redux', label: 'Redux Toolkit', roles: ['dev-fe'] },
419
+ { value: 'recoil', label: 'Recoil', roles: ['dev-fe'] },
420
+ { value: 'jotai', label: 'Jotai', roles: ['dev-fe'] },
421
+ { value: 'mobx', label: 'MobX', roles: ['dev-fe'] },
422
+ { value: 'pinia', label: 'Pinia (Vue)', roles: ['dev-fe'] }
423
+ ]
424
+ },
425
+ data: {
426
+ label: 'Data Fetching',
427
+ type: 'select',
428
+ options: [
429
+ { value: 'tanstack-query', label: 'TanStack Query' },
430
+ { value: 'swr', label: 'SWR' },
431
+ { value: 'apollo', label: 'Apollo GraphQL' },
432
+ { value: 'urql', label: 'urql' }
433
+ ]
434
+ },
435
+ testing: {
436
+ label: 'Testing',
437
+ type: 'checkbox',
438
+ options: [
439
+ { value: 'vitest', label: 'Vitest' },
440
+ { value: 'jest', label: 'Jest' },
441
+ { value: 'cypress', label: 'Cypress' },
442
+ { value: 'playwright', label: 'Playwright' },
443
+ { value: 'testing-library', label: 'Testing Library' }
444
+ ]
445
+ },
446
+ form: {
447
+ label: 'Form Handling',
448
+ type: 'select',
449
+ options: [
450
+ { value: 'react-hook-form', label: 'React Hook Form' },
451
+ { value: 'formik', label: 'Formik' },
452
+ { value: 'zod', label: 'Zod (validation)' }
453
+ ]
454
+ },
455
+ uiLib: {
456
+ label: 'UI Library',
457
+ type: 'select',
458
+ options: [
459
+ { value: 'shadcn', label: 'shadcn/ui' },
460
+ { value: 'mui', label: 'Material UI' },
461
+ { value: 'ant-design', label: 'Ant Design' },
462
+ { value: 'chakra', label: 'Chakra UI' }
463
+ ]
464
+ },
465
+ i18n: {
466
+ label: 'Internationalization',
467
+ type: 'select',
468
+ options: [
469
+ { value: 'react-i18next', label: 'react-i18next' },
470
+ { value: 'formatjs', label: 'FormatJS' },
471
+ { value: 'next-intl', label: 'next-intl' }
472
+ ]
473
+ },
474
+ database: {
475
+ label: 'Database',
476
+ type: 'checkbox',
477
+ options: [
478
+ { value: 'postgresql', label: 'PostgreSQL', roles: ['dev-be'] },
479
+ { value: 'mysql', label: 'MySQL', roles: ['dev-be'] },
480
+ { value: 'mongodb', label: 'MongoDB', roles: ['dev-be'] },
481
+ { value: 'redis', label: 'Redis', roles: ['dev-be'] },
482
+ { value: 'sqlite', label: 'SQLite', roles: ['dev-be'] }
483
+ ]
484
+ },
485
+ orm: {
486
+ label: 'ORM',
487
+ type: 'select',
488
+ options: [
489
+ { value: 'prisma', label: 'Prisma', roles: ['dev-be'] },
490
+ { value: 'typeorm', label: 'TypeORM', roles: ['dev-be'] },
491
+ { value: 'sequelize', label: 'Sequelize', roles: ['dev-be'] },
492
+ { value: 'mikro-orm', label: 'MikroORM', roles: ['dev-be'] },
493
+ { value: 'mongoose', label: 'Mongoose', roles: ['dev-be'] }
494
+ ]
495
+ },
496
+ infra: {
497
+ label: 'Infrastructure',
498
+ type: 'checkbox',
499
+ options: [
500
+ { value: 'docker', label: 'Docker', roles: ['dev-be', 'devops'] },
501
+ { value: 'kubernetes', label: 'Kubernetes', roles: ['devops'] },
502
+ { value: 'terraform', label: 'Terraform', roles: ['devops'] },
503
+ { value: 'ansible', label: 'Ansible', roles: ['devops'] }
504
+ ]
505
+ },
506
+ cicd: {
507
+ label: 'CI/CD',
508
+ type: 'select',
509
+ options: [
510
+ { value: 'github-actions', label: 'GitHub Actions', roles: ['devops'] },
511
+ { value: 'gitlab-ci', label: 'GitLab CI', roles: ['devops'] },
512
+ { value: 'jenkins', label: 'Jenkins', roles: ['devops'] }
513
+ ]
514
+ },
515
+ monitoring: {
516
+ label: 'Monitoring',
517
+ type: 'checkbox',
518
+ options: [
519
+ { value: 'prometheus', label: 'Prometheus', roles: ['devops'] },
520
+ { value: 'grafana', label: 'Grafana', roles: ['devops'] },
521
+ { value: 'datadog', label: 'Datadog', roles: ['devops'] },
522
+ { value: 'sentry', label: 'Sentry', roles: ['dev-be', 'dev-fe'] }
523
+ ]
524
+ }
525
+ };
526
+
527
+ /**
528
+ * Resolve skills for a given role and variants (legacy sync)
529
+ * @param {string} role - The base role (dev-fe, dev-be, etc.)
530
+ * @param {Object} variants - Selected variants { category: value }
531
+ * @returns {Object} - { core, role, variants } skills arrays
532
+ */
533
+ export function resolveSkills(role, variants = {}) {
534
+ const result = {
535
+ core: [...CORE_SKILLS],
536
+ role: [],
537
+ variants: []
538
+ };
539
+
540
+ // Add role-specific skills (now includes inheritance for tech-lead)
541
+ if (ROLE_SKILL_MAP[role]) {
542
+ result.role = [...ROLE_SKILL_MAP[role]];
543
+ }
544
+
545
+ // Add variant-specific skills
546
+ for (const [category, value] of Object.entries(variants)) {
547
+ if (value) {
548
+ const key = `${category}:${value}`;
549
+ if (VARIANT_SKILL_MAP[key]) {
550
+ for (const skill of VARIANT_SKILL_MAP[key]) {
551
+ if (!result.variants.includes(skill) && !result.role.includes(skill)) {
552
+ result.variants.push(skill);
553
+ }
554
+ }
555
+ }
556
+ }
557
+ }
558
+
559
+ return result;
560
+ }
561
+
562
+ /**
563
+ * Get all unique skills for an agent (legacy sync)
564
+ */
565
+ export function getAllSkills(role, variants = {}) {
566
+ const { core, role: roleSkills, variants: variantSkills } = resolveSkills(role, variants);
567
+ return [...new Set([...core, ...roleSkills, ...variantSkills])];
568
+ }
569
+
570
+ /**
571
+ * Get variants applicable for a role
572
+ */
573
+ export function getVariantsForRole(role) {
574
+ const filtered = {};
575
+
576
+ for (const [category, config] of Object.entries(VARIANT_CATEGORIES)) {
577
+ const filteredOptions = config.options.filter(opt => {
578
+ if (!opt.roles) return true;
579
+ return opt.roles.includes(role);
580
+ });
581
+
582
+ if (filteredOptions.length > 0) {
583
+ filtered[category] = {
584
+ ...config,
585
+ options: filteredOptions
586
+ };
587
+ }
588
+ }
589
+
590
+ return filtered;
591
+ }
592
+
593
+ // Export scanner functions for advanced usage
594
+ export { getSkills, clearSkillsCache };