@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.
- package/.claude/rules/common/general-rules.md +141 -0
- package/.claude/rules/lessons/lessons.md +91 -0
- package/.claude/rules/role-rules/dev-fe-rules.md +146 -0
- package/.claude/rules/role-rules/sa-rules.md +226 -0
- package/.claude/skills/SKILL-INDEX.md +299 -0
- package/.claude/skills/community/security-validator/SKILL.md +392 -0
- package/.claude/skills/core/agent-creation/SKILL.md +338 -0
- package/.claude/skills/core/code-review/SKILL.md +154 -0
- package/.claude/skills/core/git-automation/SKILL.md +93 -0
- package/.claude/skills/core/retrospect-work/SKILL.md +172 -0
- package/.claude/skills/domain/architecture/adr-writing/SKILL.md +254 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-best-practices.md +257 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-examples.md +246 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-template.md +160 -0
- package/.claude/skills/domain/architecture/architecture-patterns/SKILL.md +316 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/event-driven.md +393 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/microservices.md +315 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/monolith.md +321 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/serverless.md +457 -0
- package/.claude/skills/domain/architecture/performance-engineering/SKILL.md +227 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/benchmarking.md +336 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/caching-strategies.md +284 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/optimization.md +298 -0
- package/.claude/skills/domain/architecture/security-architecture/SKILL.md +206 -0
- package/.claude/skills/domain/architecture/security-architecture/references/auth-patterns.md +209 -0
- package/.claude/skills/domain/architecture/security-architecture/references/compliance.md +246 -0
- package/.claude/skills/domain/architecture/security-architecture/references/threat-modeling.md +219 -0
- package/.claude/skills/domain/architecture/system-design/SKILL.md +227 -0
- package/.claude/skills/domain/architecture/system-design/references/distributed-systems.md +231 -0
- package/.claude/skills/domain/architecture/system-design/references/resilience.md +344 -0
- package/.claude/skills/domain/architecture/system-design/references/scalability.md +303 -0
- package/.claude/skills/domain/architecture/tech-selection/SKILL.md +192 -0
- package/.claude/skills/domain/architecture/tech-selection/references/build-vs-buy.md +258 -0
- package/.claude/skills/domain/architecture/tech-selection/references/evaluation-framework.md +203 -0
- package/.claude/skills/domain/architecture/tech-selection/references/tech-radar.md +257 -0
- package/.claude/skills/domain/backend/api-design/SKILL.md +121 -0
- package/.claude/skills/domain/backend/database-design/SKILL.md +156 -0
- package/.claude/skills/domain/backend/performance-be/SKILL.md +210 -0
- package/.claude/skills/domain/backend/security/SKILL.md +138 -0
- package/.claude/skills/domain/backend/testing-be/SKILL.md +203 -0
- package/.claude/skills/domain/devops/ci-cd/SKILL.md +188 -0
- package/.claude/skills/domain/devops/containerization/SKILL.md +177 -0
- package/.claude/skills/domain/devops/deployment/SKILL.md +198 -0
- package/.claude/skills/domain/devops/infrastructure-as-code/SKILL.md +178 -0
- package/.claude/skills/domain/devops/monitoring/SKILL.md +163 -0
- package/.claude/skills/domain/frontend/accessibility/SKILL.md +179 -0
- package/.claude/skills/domain/frontend/frontend-design/SKILL.md +138 -0
- package/.claude/skills/domain/frontend/performance-fe/SKILL.md +195 -0
- package/.claude/skills/domain/frontend/state-management/SKILL.md +190 -0
- package/.claude/skills/domain/frontend/testing-fe/SKILL.md +193 -0
- package/.claude/skills/domain/product/requirements-gathering/SKILL.md +136 -0
- package/.claude/skills/domain/product/roadmap-planning/SKILL.md +169 -0
- package/.claude/skills/domain/product/sprint-planning/SKILL.md +151 -0
- package/.claude/skills/domain/product/stakeholder-communication/SKILL.md +162 -0
- package/.claude/skills/domain/product/user-stories/SKILL.md +141 -0
- package/.claude/skills/domain/quality/bug-reporting/SKILL.md +150 -0
- package/.claude/skills/domain/quality/regression-testing/SKILL.md +178 -0
- package/.claude/skills/domain/quality/test-automation/SKILL.md +185 -0
- package/.claude/skills/domain/quality/test-planning/SKILL.md +177 -0
- package/.claude/skills/leadership/code-review-advanced/SKILL.md +167 -0
- package/.claude/skills/leadership/mentoring/SKILL.md +151 -0
- package/.claude/skills/leadership/technical-debt/SKILL.md +166 -0
- package/.claude/skills/leadership/technical-decision/SKILL.md +160 -0
- package/.claude/skills/security-reports/.gitkeep +0 -0
- package/.claude/skills/skills-registry.yaml +441 -0
- package/README.md +232 -0
- package/bin/agent-team.js +107 -0
- package/package.json +51 -0
- package/src/commands/add.js +227 -0
- package/src/commands/init.js +136 -0
- package/src/commands/list.js +66 -0
- package/src/commands/remove.js +71 -0
- package/src/commands/switch.js +53 -0
- package/src/index.js +11 -0
- package/src/interactive/prompts.js +153 -0
- package/src/server/api/agents.js +150 -0
- package/src/server/api/roles.js +97 -0
- package/src/server/api/skills.js +79 -0
- package/src/server/index.js +78 -0
- package/src/ui/agents.html +174 -0
- package/src/ui/css/styles.css +470 -0
- package/src/ui/index.html +107 -0
- package/src/ui/roles.html +371 -0
- package/src/ui/skills.html +332 -0
- package/src/utils/file-utils.js +193 -0
- package/src/utils/skill-resolver.js +594 -0
- package/src/utils/skill-scanner.js +154 -0
- package/templates/CLAUDE.md.tmpl +42 -0
- 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 };
|