@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,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command - Initialize .claude/ structure in project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { getClaudeDir, getProjectRoot, copyRules } from '../utils/file-utils.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize .claude/ structure
|
|
12
|
+
*/
|
|
13
|
+
export async function initCommand(options) {
|
|
14
|
+
const projectRoot = getProjectRoot();
|
|
15
|
+
const claudeDir = getClaudeDir(projectRoot);
|
|
16
|
+
|
|
17
|
+
console.log(chalk.blue('📁 Initializing project...'));
|
|
18
|
+
console.log(chalk.gray(` Project: ${projectRoot}`));
|
|
19
|
+
console.log('');
|
|
20
|
+
|
|
21
|
+
// Check if already initialized
|
|
22
|
+
if (await fs.exists(claudeDir)) {
|
|
23
|
+
if (!options.force) {
|
|
24
|
+
console.log(chalk.yellow('⚠️ .claude/ directory already exists'));
|
|
25
|
+
console.log(chalk.gray(' Use --force to overwrite'));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.log(chalk.yellow(' Removing existing .claude/ directory...'));
|
|
29
|
+
await fs.remove(claudeDir);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create directory structure
|
|
33
|
+
console.log(chalk.blue(' Creating directory structure...'));
|
|
34
|
+
|
|
35
|
+
await fs.ensureDir(path.join(claudeDir, 'agents'));
|
|
36
|
+
await fs.ensureDir(path.join(claudeDir, 'shared-skills'));
|
|
37
|
+
await fs.ensureDir(path.join(claudeDir, 'rules', 'common'));
|
|
38
|
+
await fs.ensureDir(path.join(claudeDir, 'rules', 'role-rules'));
|
|
39
|
+
await fs.ensureDir(path.join(claudeDir, 'rules', 'lessons'));
|
|
40
|
+
await fs.ensureDir(path.join(claudeDir, 'commands', 'roles'));
|
|
41
|
+
await fs.ensureDir(path.join(claudeDir, 'commands', 'utils'));
|
|
42
|
+
await fs.ensureDir(path.join(claudeDir, 'commands', 'workflows'));
|
|
43
|
+
|
|
44
|
+
// Create CLAUDE.md
|
|
45
|
+
const claudeMdPath = path.join(claudeDir, 'CLAUDE.md');
|
|
46
|
+
if (!(await fs.exists(claudeMdPath)) || options.force) {
|
|
47
|
+
const projectName = path.basename(projectRoot);
|
|
48
|
+
const claudeMdContent = `# Project Context: ${projectName}
|
|
49
|
+
|
|
50
|
+
## Description
|
|
51
|
+
[Brief description of the project]
|
|
52
|
+
|
|
53
|
+
## Tech Stack
|
|
54
|
+
- Frontend:
|
|
55
|
+
- Backend:
|
|
56
|
+
- Database:
|
|
57
|
+
- Infrastructure:
|
|
58
|
+
|
|
59
|
+
## Team Members (Agents)
|
|
60
|
+
[List of configured agents - run 'npx @wipal/agent-team list' to see]
|
|
61
|
+
|
|
62
|
+
## Getting Started
|
|
63
|
+
1. Add agents: \`npx @wipal/agent-team add <name> <role> [options]\`
|
|
64
|
+
2. List agents: \`npx @wipal/agent-team list\`
|
|
65
|
+
3. Switch agent: Use the agent's CLAUDE.md file
|
|
66
|
+
|
|
67
|
+
## Notes
|
|
68
|
+
- Use Context7 MCP for documentation lookup
|
|
69
|
+
- Run \`/retrospect-work\` at end of session
|
|
70
|
+
- Update this file with project-specific context
|
|
71
|
+
`;
|
|
72
|
+
await fs.writeFile(claudeMdPath, claudeMdContent);
|
|
73
|
+
console.log(chalk.green(' ✓ Created CLAUDE.md'));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Create settings.json
|
|
77
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
78
|
+
if (!(await fs.exists(settingsPath)) || options.force) {
|
|
79
|
+
const settingsContent = {
|
|
80
|
+
mcpServers: {
|
|
81
|
+
context7: {
|
|
82
|
+
command: 'npx',
|
|
83
|
+
args: ['-y', '@context7/mcp-server']
|
|
84
|
+
},
|
|
85
|
+
github: {
|
|
86
|
+
command: 'npx',
|
|
87
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
88
|
+
env: {
|
|
89
|
+
GITHUB_TOKEN: '${GITHUB_TOKEN}'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
await fs.writeJson(settingsPath, settingsContent, { spaces: 2 });
|
|
95
|
+
console.log(chalk.green(' ✓ Created settings.json'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Copy rules
|
|
99
|
+
console.log(chalk.blue(' Copying rules...'));
|
|
100
|
+
try {
|
|
101
|
+
await copyRules(projectRoot);
|
|
102
|
+
console.log(chalk.green(' ✓ Copied rules'));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.log(chalk.yellow(' ⚠ Could not copy rules (package may not have rules)'));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Create lessons.md template
|
|
108
|
+
const lessonsPath = path.join(claudeDir, 'rules', 'lessons', 'lessons.md');
|
|
109
|
+
if (!(await fs.exists(lessonsPath))) {
|
|
110
|
+
const lessonsContent = `# Lessons Learned
|
|
111
|
+
|
|
112
|
+
## Format
|
|
113
|
+
### YYYY-MM-DD: Lesson Title
|
|
114
|
+
**What happened:** Description
|
|
115
|
+
**Lesson:** What we learned
|
|
116
|
+
**Rule added:** Rule to prevent same mistake
|
|
117
|
+
|
|
118
|
+
## Recent Lessons
|
|
119
|
+
(No lessons yet - run /retrospect-work after sessions)
|
|
120
|
+
`;
|
|
121
|
+
await fs.writeFile(lessonsPath, lessonsContent);
|
|
122
|
+
console.log(chalk.green(' ✓ Created lessons.md'));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(chalk.green('✅ Project initialized successfully!'));
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(chalk.blue('Next steps:'));
|
|
129
|
+
console.log(chalk.gray(' 1. Add an agent:'));
|
|
130
|
+
console.log(chalk.cyan(' npx @wipal/agent-team add <name> <role> [options]'));
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log(chalk.gray(' 2. Or use interactive mode:'));
|
|
133
|
+
console.log(chalk.cyan(' npx @wipal/agent-team add'));
|
|
134
|
+
console.log('');
|
|
135
|
+
console.log(chalk.gray(' 3. Available roles: dev-fe, dev-be, sa, tech-lead, devops, pm, qa'));
|
|
136
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List command - List all agents in the project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { isInitialized, getAgents } from '../utils/file-utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* List all agents
|
|
10
|
+
*/
|
|
11
|
+
export async function listCommand() {
|
|
12
|
+
const projectRoot = process.cwd();
|
|
13
|
+
|
|
14
|
+
// Check if initialized
|
|
15
|
+
if (!(await isInitialized(projectRoot))) {
|
|
16
|
+
console.log(chalk.yellow('⚠️ Project not initialized'));
|
|
17
|
+
console.log(chalk.gray(' Run `npx @wipal/agent-team init` first'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Get agents
|
|
22
|
+
const agents = await getAgents(projectRoot);
|
|
23
|
+
|
|
24
|
+
if (agents.length === 0) {
|
|
25
|
+
console.log(chalk.yellow('📋 No agents found'));
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(chalk.gray(' Create one with:'));
|
|
28
|
+
console.log(chalk.cyan(' npx @wipal/agent-team add <name> <role>'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(chalk.blue(`📋 Agents in project (${agents.length})`));
|
|
33
|
+
console.log('');
|
|
34
|
+
|
|
35
|
+
// Table header
|
|
36
|
+
console.log(chalk.gray('┌─────────────────┬─────────────┬──────────────────────────────────────┐'));
|
|
37
|
+
console.log(chalk.gray('│') + chalk.white(' Agent ') + chalk.gray('│') + chalk.white(' Role ') + chalk.gray('│') + chalk.white(' Variants ') + chalk.gray('│'));
|
|
38
|
+
console.log(chalk.gray('├─────────────────┼─────────────┼──────────────────────────────────────┤'));
|
|
39
|
+
|
|
40
|
+
// Table rows
|
|
41
|
+
for (const agent of agents) {
|
|
42
|
+
const name = agent.name.padEnd(15).slice(0, 15);
|
|
43
|
+
const role = (agent.variants?.base_role || 'unknown').padEnd(11).slice(0, 11);
|
|
44
|
+
|
|
45
|
+
// Format variants
|
|
46
|
+
let variantsStr = '';
|
|
47
|
+
if (agent.variants?.installed_variants) {
|
|
48
|
+
const v = Object.entries(agent.variants.installed_variants)
|
|
49
|
+
.filter(([_, val]) => val)
|
|
50
|
+
.map(([k, val]) => `${k}=${val}`)
|
|
51
|
+
.join(', ');
|
|
52
|
+
variantsStr = v.padEnd(36).slice(0, 36);
|
|
53
|
+
} else {
|
|
54
|
+
variantsStr = '(none)'.padEnd(36);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(chalk.gray('│') + chalk.cyan(name) + chalk.gray('│') + chalk.yellow(role) + chalk.gray('│') + chalk.gray(variantsStr) + chalk.gray('│'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Table footer
|
|
61
|
+
console.log(chalk.gray('└─────────────────┴─────────────┴──────────────────────────────────────┘'));
|
|
62
|
+
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(chalk.gray('Switch to an agent:'));
|
|
65
|
+
console.log(chalk.cyan(' npx @wipal/agent-team switch <name>'));
|
|
66
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove command - Remove an agent from the project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
import { isInitialized, agentExists, getAgentDir, getAgents } from '../utils/file-utils.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Remove an agent
|
|
12
|
+
*/
|
|
13
|
+
export async function removeCommand(name, options) {
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Check if initialized
|
|
17
|
+
if (!(await isInitialized(projectRoot))) {
|
|
18
|
+
console.log(chalk.red('❌ Project not initialized'));
|
|
19
|
+
console.log(chalk.gray(' Run `npx @wipal/agent-team init` first'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check if agent exists
|
|
24
|
+
if (!(await agentExists(name, projectRoot))) {
|
|
25
|
+
console.log(chalk.red(`❌ Agent '${name}' not found`));
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(chalk.gray('List available agents:'));
|
|
28
|
+
console.log(chalk.cyan(' npx @wipal/agent-team list'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const agentDir = getAgentDir(name, projectRoot);
|
|
33
|
+
|
|
34
|
+
// Confirm deletion unless --force
|
|
35
|
+
if (!options.force) {
|
|
36
|
+
console.log(chalk.yellow(`⚠️ This will remove agent '${name}' and all its data`));
|
|
37
|
+
console.log(chalk.gray(` Location: ${agentDir}`));
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
const answer = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: 'confirm',
|
|
43
|
+
name: 'proceed',
|
|
44
|
+
message: `Remove agent '${name}'?`,
|
|
45
|
+
default: false
|
|
46
|
+
}
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
if (!answer.proceed) {
|
|
50
|
+
console.log(chalk.gray('Cancelled.'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Remove the agent
|
|
56
|
+
console.log(chalk.blue(`Removing agent '${name}'...`));
|
|
57
|
+
await fs.remove(agentDir);
|
|
58
|
+
|
|
59
|
+
console.log(chalk.green(`✅ Agent '${name}' removed`));
|
|
60
|
+
console.log('');
|
|
61
|
+
|
|
62
|
+
// Show remaining agents
|
|
63
|
+
const remainingAgents = await getAgents(projectRoot);
|
|
64
|
+
if (remainingAgents.length > 0) {
|
|
65
|
+
console.log(chalk.gray(`Remaining agents: ${remainingAgents.length}`));
|
|
66
|
+
console.log(chalk.cyan(' npx @wipal/agent-team list'));
|
|
67
|
+
} else {
|
|
68
|
+
console.log(chalk.gray('No agents remaining'));
|
|
69
|
+
console.log(chalk.cyan(' npx @wipal/agent-team add <name> <role>'));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch command - Show instructions to switch to an agent
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { isInitialized, agentExists, getAgentDir } from '../utils/file-utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Switch to an agent
|
|
10
|
+
*/
|
|
11
|
+
export async function switchCommand(name) {
|
|
12
|
+
const projectRoot = process.cwd();
|
|
13
|
+
|
|
14
|
+
// Check if initialized
|
|
15
|
+
if (!(await isInitialized(projectRoot))) {
|
|
16
|
+
console.log(chalk.red('❌ Project not initialized'));
|
|
17
|
+
console.log(chalk.gray(' Run `npx @wipal/agent-team init` first'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if agent exists
|
|
22
|
+
if (!(await agentExists(name, projectRoot))) {
|
|
23
|
+
console.log(chalk.red(`❌ Agent '${name}' not found`));
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(chalk.gray('List available agents:'));
|
|
26
|
+
console.log(chalk.cyan(' npx @wipal/agent-team list'));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const agentDir = getAgentDir(name, projectRoot);
|
|
31
|
+
const claudeMdPath = `${agentDir}/CLAUDE.md`;
|
|
32
|
+
|
|
33
|
+
console.log(chalk.green(`✓ Switching to agent: ${name}`));
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log(chalk.blue('Agent configuration:'));
|
|
36
|
+
console.log(chalk.gray(` ${claudeMdPath}`));
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk.blue('How to use in Claude Code:'));
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log(chalk.white('Option 1: Use the agent\'s CLAUDE.md directly'));
|
|
41
|
+
console.log(chalk.gray(' Claude Code will read instructions from:'));
|
|
42
|
+
console.log(chalk.cyan(` ${claudeMdPath}`));
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log(chalk.white('Option 2: Copy content to project root CLAUDE.md'));
|
|
45
|
+
console.log(chalk.gray(' If you want this agent to be the default:'));
|
|
46
|
+
console.log(chalk.cyan(` cp ${claudeMdPath} .claude/CLAUDE.md`));
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log(chalk.white('Option 3: Use /switch command (if available)'));
|
|
49
|
+
console.log(chalk.cyan(` /switch ${name}`));
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log(chalk.gray('The agent has the following skills available:'));
|
|
52
|
+
console.log(chalk.gray(` ${agentDir}/skills/`));
|
|
53
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wipal/agent-team
|
|
3
|
+
* Main entry point for programmatic usage
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { initCommand } from './commands/init.js';
|
|
7
|
+
export { addCommand } from './commands/add.js';
|
|
8
|
+
export { listCommand } from './commands/list.js';
|
|
9
|
+
export { switchCommand } from './commands/switch.js';
|
|
10
|
+
export { removeCommand } from './commands/remove.js';
|
|
11
|
+
export { resolveSkills } from './utils/skill-resolver.js';
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for agent-team CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import inquirer from 'inquirer';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import {
|
|
8
|
+
AVAILABLE_ROLES,
|
|
9
|
+
VARIANT_CATEGORIES,
|
|
10
|
+
getVariantsForRole,
|
|
11
|
+
getAllSkills
|
|
12
|
+
} from '../utils/skill-resolver.js';
|
|
13
|
+
import { createAgent } from '../commands/add.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run interactive add mode
|
|
17
|
+
*/
|
|
18
|
+
export async function runInteractiveAdd(projectRoot, options = {}) {
|
|
19
|
+
console.log(chalk.blue('🎮 Interactive Mode'));
|
|
20
|
+
console.log('');
|
|
21
|
+
|
|
22
|
+
// Step 1: Agent name
|
|
23
|
+
const nameAnswer = await inquirer.prompt([
|
|
24
|
+
{
|
|
25
|
+
type: 'input',
|
|
26
|
+
name: 'name',
|
|
27
|
+
message: 'Agent name:',
|
|
28
|
+
default: options.name || 'my-agent',
|
|
29
|
+
validate: (input) => {
|
|
30
|
+
if (!input.trim()) return 'Agent name is required';
|
|
31
|
+
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
32
|
+
return 'Use only lowercase letters, numbers, and hyphens';
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// Step 2: Select role
|
|
40
|
+
const roleAnswer = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: 'list',
|
|
43
|
+
name: 'role',
|
|
44
|
+
message: 'Select role:',
|
|
45
|
+
choices: AVAILABLE_ROLES.map(r => ({
|
|
46
|
+
name: `${r.name.padEnd(12)} - ${r.description}`,
|
|
47
|
+
value: r.name
|
|
48
|
+
})),
|
|
49
|
+
default: options.role
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
const selectedRole = roleAnswer.role;
|
|
54
|
+
|
|
55
|
+
// Step 3: Select variants based on role
|
|
56
|
+
const availableVariants = getVariantsForRole(selectedRole);
|
|
57
|
+
const selectedVariants = {};
|
|
58
|
+
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(chalk.blue('📦 Select variants (press Enter to skip a category):'));
|
|
61
|
+
console.log('');
|
|
62
|
+
|
|
63
|
+
for (const [category, config] of Object.entries(availableVariants)) {
|
|
64
|
+
const choices = config.options.map(opt => ({
|
|
65
|
+
name: opt.label,
|
|
66
|
+
value: opt.value
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
// Add "None" option
|
|
70
|
+
choices.unshift({ name: 'None (skip)', value: null });
|
|
71
|
+
|
|
72
|
+
if (config.type === 'select') {
|
|
73
|
+
const answer = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'list',
|
|
76
|
+
name: category,
|
|
77
|
+
message: `${config.label}:`,
|
|
78
|
+
choices,
|
|
79
|
+
default: null
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
if (answer[category]) {
|
|
84
|
+
selectedVariants[category] = answer[category];
|
|
85
|
+
}
|
|
86
|
+
} else if (config.type === 'checkbox') {
|
|
87
|
+
const answer = await inquirer.prompt([
|
|
88
|
+
{
|
|
89
|
+
type: 'checkbox',
|
|
90
|
+
name: category,
|
|
91
|
+
message: `${config.label}:`,
|
|
92
|
+
choices: config.options.map(opt => ({
|
|
93
|
+
name: opt.label,
|
|
94
|
+
value: opt.value,
|
|
95
|
+
checked: false
|
|
96
|
+
}))
|
|
97
|
+
}
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
if (answer[category] && answer[category].length > 0) {
|
|
101
|
+
// For checkbox, store as array or join if single
|
|
102
|
+
selectedVariants[category] = answer[category].join(',');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Step 4: Show summary and confirm
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(chalk.cyan('═══════════════════════════════════════════════════════════════'));
|
|
110
|
+
console.log(chalk.cyan(' Summary'));
|
|
111
|
+
console.log(chalk.cyan('═══════════════════════════════════════════════════════════════'));
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log(chalk.white(` Agent Name: ${chalk.yellow(nameAnswer.name)}`));
|
|
114
|
+
console.log(chalk.white(` Role: ${chalk.yellow(selectedRole)}`));
|
|
115
|
+
console.log('');
|
|
116
|
+
|
|
117
|
+
// Show selected variants
|
|
118
|
+
const variantEntries = Object.entries(selectedVariants).filter(([_, v]) => v);
|
|
119
|
+
if (variantEntries.length > 0) {
|
|
120
|
+
console.log(chalk.white(' Variants:'));
|
|
121
|
+
for (const [category, value] of variantEntries) {
|
|
122
|
+
console.log(chalk.gray(` - ${category}: ${value}`));
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
console.log(chalk.gray(' Variants: None'));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Show skills that will be installed
|
|
129
|
+
const skills = getAllSkills(selectedRole, selectedVariants);
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(chalk.white(` Skills to install (${skills.length}):`));
|
|
132
|
+
console.log(chalk.gray(` ${skills.join(', ')}`));
|
|
133
|
+
|
|
134
|
+
console.log('');
|
|
135
|
+
|
|
136
|
+
// Confirm
|
|
137
|
+
const confirmAnswer = await inquirer.prompt([
|
|
138
|
+
{
|
|
139
|
+
type: 'confirm',
|
|
140
|
+
name: 'proceed',
|
|
141
|
+
message: 'Create this agent?',
|
|
142
|
+
default: true
|
|
143
|
+
}
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
if (!confirmAnswer.proceed) {
|
|
147
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Create the agent
|
|
152
|
+
await createAgent(nameAnswer.name, selectedRole, selectedVariants, projectRoot);
|
|
153
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents API Routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import {
|
|
9
|
+
getClaudeDir,
|
|
10
|
+
getAgentsDir,
|
|
11
|
+
getAgentDir,
|
|
12
|
+
isInitialized,
|
|
13
|
+
agentExists,
|
|
14
|
+
getAgents
|
|
15
|
+
} from '../../utils/file-utils.js';
|
|
16
|
+
import {
|
|
17
|
+
resolveSkills,
|
|
18
|
+
getAllSkills,
|
|
19
|
+
AVAILABLE_ROLES,
|
|
20
|
+
VARIANT_CATEGORIES,
|
|
21
|
+
getVariantsForRole
|
|
22
|
+
} from '../../utils/skill-resolver.js';
|
|
23
|
+
import { createAgent } from '../../commands/add.js';
|
|
24
|
+
|
|
25
|
+
const router = Router();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* List all agents
|
|
29
|
+
*/
|
|
30
|
+
router.get('/', async (req, res) => {
|
|
31
|
+
try {
|
|
32
|
+
const projectRoot = req.app.get('projectRoot');
|
|
33
|
+
|
|
34
|
+
if (!(await isInitialized(projectRoot))) {
|
|
35
|
+
return res.json({ initialized: false, agents: [] });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const agents = await getAgents(projectRoot);
|
|
39
|
+
res.json({ initialized: true, agents });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
res.status(500).json({ error: error.message });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get single agent
|
|
47
|
+
*/
|
|
48
|
+
router.get('/:name', async (req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const { name } = req.params;
|
|
51
|
+
const projectRoot = req.app.get('projectRoot');
|
|
52
|
+
const agentDir = getAgentDir(name, projectRoot);
|
|
53
|
+
|
|
54
|
+
if (!(await fs.exists(agentDir))) {
|
|
55
|
+
return res.status(404).json({ error: 'Agent not found' });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const variants = await fs.readJson(path.join(agentDir, 'variants.json'));
|
|
59
|
+
const claudeMd = await fs.readFile(path.join(agentDir, 'CLAUDE.md'), 'utf-8');
|
|
60
|
+
|
|
61
|
+
// Get skills list
|
|
62
|
+
const skillsDir = path.join(agentDir, 'skills');
|
|
63
|
+
let skills = [];
|
|
64
|
+
if (await fs.exists(skillsDir)) {
|
|
65
|
+
const skillEntries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
66
|
+
skills = skillEntries.filter(e => e.isDirectory()).map(e => e.name);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
res.json({
|
|
70
|
+
name,
|
|
71
|
+
path: agentDir,
|
|
72
|
+
variants,
|
|
73
|
+
skills,
|
|
74
|
+
claudeMd
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
res.status(500).json({ error: error.message });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create new agent
|
|
83
|
+
*/
|
|
84
|
+
router.post('/', async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
const { name, role, variants } = req.body;
|
|
87
|
+
const projectRoot = req.app.get('projectRoot');
|
|
88
|
+
|
|
89
|
+
if (!(await isInitialized(projectRoot))) {
|
|
90
|
+
return res.status(400).json({ error: 'Project not initialized' });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (await agentExists(name, projectRoot)) {
|
|
94
|
+
return res.status(400).json({ error: 'Agent already exists' });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await createAgent(name, role, variants || {}, projectRoot);
|
|
98
|
+
|
|
99
|
+
res.json({ success: true, name, role });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
res.status(500).json({ error: error.message });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Delete agent
|
|
107
|
+
*/
|
|
108
|
+
router.delete('/:name', async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const { name } = req.params;
|
|
111
|
+
const projectRoot = req.app.get('projectRoot');
|
|
112
|
+
const agentDir = getAgentDir(name, projectRoot);
|
|
113
|
+
|
|
114
|
+
if (!(await fs.exists(agentDir))) {
|
|
115
|
+
return res.status(404).json({ error: 'Agent not found' });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await fs.remove(agentDir);
|
|
119
|
+
res.json({ success: true });
|
|
120
|
+
} catch (error) {
|
|
121
|
+
res.status(500).json({ error: error.message });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get available roles
|
|
127
|
+
*/
|
|
128
|
+
router.get('/meta/roles', (req, res) => {
|
|
129
|
+
res.json(AVAILABLE_ROLES);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get available variants for a role
|
|
134
|
+
*/
|
|
135
|
+
router.get('/meta/variants/:role', (req, res) => {
|
|
136
|
+
const { role } = req.params;
|
|
137
|
+
const variants = getVariantsForRole(role);
|
|
138
|
+
res.json(variants);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Preview skills for role + variants
|
|
143
|
+
*/
|
|
144
|
+
router.post('/preview-skills', (req, res) => {
|
|
145
|
+
const { role, variants } = req.body;
|
|
146
|
+
const skills = resolveSkills(role, variants || {});
|
|
147
|
+
res.json(skills);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export default router;
|