@wipal/agent-team 1.0.3 → 1.1.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/commands/skills/discover.md +127 -0
- package/.claude/commands/skills/install.md +225 -0
- package/.claude/commands/skills/review.md +234 -0
- package/.claude/commands/utils/learn.md +142 -0
- package/.claude/commands/utils/retrospect.md +62 -0
- package/.claude/commands/utils/switch.md +113 -0
- package/.claude/commands/utils/sync.md +183 -0
- package/.claude/rules/common/general-rules.md +6 -0
- package/.claude/rules/role-rules/dev-be-rules.md +241 -0
- package/.claude/rules/role-rules/dev-fe-rules.md +76 -0
- package/.claude/skills/SKILL-INDEX.md +24 -5
- package/.claude/skills/core/knowledge-graph/SKILL.md +214 -0
- package/.claude/skills/core/sequential-thinking/SKILL.md +112 -0
- package/.claude/skills/core/sequential-thinking/references/advanced.md +122 -0
- package/.claude/skills/core/sequential-thinking/references/examples.md +274 -0
- package/.claude/skills/domain/architecture/c4-architecture/SKILL.md +314 -0
- package/.claude/skills/domain/architecture/c4-architecture/references/advanced-patterns.md +552 -0
- package/.claude/skills/domain/architecture/c4-architecture/references/c4-syntax.md +492 -0
- package/.claude/skills/domain/architecture/c4-architecture/references/common-mistakes.md +437 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/SKILL.md +238 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/advanced-features.md +556 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/architecture-diagrams.md +192 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/c4-diagrams.md +410 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/class-diagrams.md +361 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/erd-diagrams.md +510 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/flowcharts.md +450 -0
- package/.claude/skills/domain/architecture/mermaid-diagrams/references/sequence-diagrams.md +394 -0
- package/.claude/skills/domain/backend/testing-be/SKILL.md +121 -17
- package/.claude/skills/domain/design/design-system/SKILL.md +169 -0
- package/.claude/skills/domain/design/html-css-output/SKILL.md +253 -0
- package/.claude/skills/domain/design/mockup-creation/SKILL.md +230 -0
- package/.claude/skills/domain/design/responsive-design/SKILL.md +207 -0
- package/.claude/skills/domain/design/ui-design/SKILL.md +124 -0
- package/.claude/skills/domain/frontend/testing-fe/SKILL.md +143 -38
- package/.claude/skills/domain/frontend/ui-ux-pro-max/README.md +45 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/SKILL.md +404 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/domain/frontend/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/domain/product/requirements-clarity/SKILL.md +340 -0
- package/.claude/skills/skills-registry.yaml +103 -8
- package/README.md +107 -33
- package/README.npm.md +252 -0
- package/TUTORIAL.md +256 -0
- package/bin/agent-team.js +26 -7
- package/config/roles.yaml +107 -0
- package/docs/01-architecture.md +699 -0
- package/docs/02-setup-guide.md +634 -0
- package/docs/03-skills-guide.md +628 -0
- package/docs/04-workflows.md +792 -0
- package/docs/05-model-strategy.md +550 -0
- package/docs/06-extend-guide.md +1226 -0
- package/docs/07-quick-reference.md +578 -0
- package/docs/08-skills-discovery.md +342 -0
- package/docs/README.md +134 -0
- package/docs/rqm.md +560 -0
- package/package.json +10 -4
- package/scripts/postinstall.js +46 -0
- package/src/commands/add.js +131 -67
- package/src/commands/init.js +419 -9
- package/src/commands/list.js +20 -16
- package/src/commands/projects.js +127 -0
- package/src/commands/setup-hooks.js +261 -0
- package/src/index.js +0 -1
- package/src/utils/file-utils.js +147 -50
- package/src/utils/global-registry.js +224 -0
- package/templates/CLAUDE.md.tmpl +128 -20
- package/templates/MEMORY.md.tmpl +119 -0
- package/templates/agent.md.tmpl +205 -0
- package/templates/code/nestjs-controller.ts.tmpl +49 -0
- package/templates/code/nestjs-dto.ts.tmpl +63 -0
- package/templates/code/nestjs-service.ts.tmpl +45 -0
- package/templates/code/react-component.tsx.tmpl +24 -0
- package/templates/code/react-hook.ts.tmpl +54 -0
- package/templates/code/test.spec.ts.tmpl +50 -0
- package/templates/code/vue-component.vue.tmpl +49 -0
- package/templates/code/vue-composable.ts.tmpl +54 -0
- package/templates/knowledge.md.tmpl +152 -17
- package/templates/meeting-notes.md.tmpl +110 -0
- package/templates/memory/hooks.memory.json +50 -0
- package/templates/memory/settings.memory.json +16 -0
- package/templates/reports/bug-report.md.tmpl +164 -0
- package/templates/reports/code-review.md.tmpl +201 -0
- package/templates/reports/sprint-report.md.tmpl +218 -0
- package/templates/roles/ba.md +53 -0
- package/templates/roles/designer.md +82 -0
- package/templates/roles/dev-be.md +49 -0
- package/templates/roles/dev-fe.md +49 -0
- package/templates/roles/devops.md +53 -0
- package/templates/roles/pm.md +49 -0
- package/templates/roles/qa.md +53 -0
- package/templates/roles/sa.md +49 -0
- package/templates/roles/tech-lead.md +132 -0
- package/templates/skills/memory/memory-status.md +78 -0
- package/templates/skills/memory/recall.md +160 -0
- package/templates/skills/memory/reflect.md +168 -0
- package/templates/skills/memory/remember.md +105 -0
- package/templates/tasks/lessons.md.tmpl +77 -0
- package/templates/tasks/todo.md.tmpl +53 -0
- package/src/commands/switch.js +0 -53
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup-hooks command - Configure Claude Code hooks
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import { getClaudeDir, isInitialized } from '../utils/file-utils.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Setup hooks command handler
|
|
13
|
+
*/
|
|
14
|
+
export async function setupHooksCommand(options) {
|
|
15
|
+
const projectRoot = process.cwd();
|
|
16
|
+
|
|
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
|
+
if (options.list) {
|
|
24
|
+
await listHooks();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (options.enable) {
|
|
29
|
+
await enableHook(options.enable, projectRoot);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (options.disable) {
|
|
34
|
+
await disableHook(options.disable, projectRoot);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Interactive setup
|
|
39
|
+
await interactiveSetup(projectRoot, options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* List available hooks
|
|
44
|
+
*/
|
|
45
|
+
async function listHooks() {
|
|
46
|
+
console.log(chalk.blue('📋 Available Claude Code Hooks'));
|
|
47
|
+
console.log('');
|
|
48
|
+
|
|
49
|
+
const hooks = [
|
|
50
|
+
{
|
|
51
|
+
name: 'pre_tool_use',
|
|
52
|
+
description: 'Runs before any tool is called. Can block tool execution.'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'post_tool_use',
|
|
56
|
+
description: 'Runs after a tool call completes. Receives tool result.'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'notification',
|
|
60
|
+
description: 'Runs when a notification is triggered.'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'stop',
|
|
64
|
+
description: 'Runs when a Claude Code session stops.'
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
for (const hook of hooks) {
|
|
69
|
+
console.log(chalk.cyan(` ${hook.name}`));
|
|
70
|
+
console.log(chalk.gray(` ${hook.description}`));
|
|
71
|
+
console.log('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.gray('Usage:'));
|
|
75
|
+
console.log(chalk.cyan(' npx @wipal/agent-team setup-hooks --enable pre_tool_use'));
|
|
76
|
+
console.log(chalk.cyan(' npx @wipal/agent-team setup-hooks --disable post_tool_use'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Enable a specific hook
|
|
81
|
+
*/
|
|
82
|
+
async function enableHook(hookName, projectRoot) {
|
|
83
|
+
const claudeDir = getClaudeDir(projectRoot);
|
|
84
|
+
const hooksDir = path.join(claudeDir, 'hooks');
|
|
85
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
86
|
+
|
|
87
|
+
const validHooks = ['pre_tool_use', 'post_tool_use', 'notification', 'stop'];
|
|
88
|
+
if (!validHooks.includes(hookName)) {
|
|
89
|
+
console.log(chalk.red(`❌ Invalid hook: ${hookName}`));
|
|
90
|
+
console.log(chalk.gray(` Valid hooks: ${validHooks.join(', ')}`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await fs.ensureDir(hooksDir);
|
|
95
|
+
|
|
96
|
+
// Create hook script
|
|
97
|
+
const hookScript = getHookScript(hookName);
|
|
98
|
+
const hookPath = path.join(hooksDir, `${hookName}.sh`);
|
|
99
|
+
await fs.writeFile(hookPath, hookScript);
|
|
100
|
+
await fs.chmod(hookPath, '755');
|
|
101
|
+
|
|
102
|
+
// Update settings.json
|
|
103
|
+
let settings = {};
|
|
104
|
+
if (await fs.exists(settingsPath)) {
|
|
105
|
+
settings = await fs.readJson(settingsPath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!settings.hooks) {
|
|
109
|
+
settings.hooks = {};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
settings.hooks[hookName] = {
|
|
113
|
+
command: `.claude/hooks/${hookName}.sh`,
|
|
114
|
+
enabled: true
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
await fs.writeJson(settingsPath, settings, { spaces: 2 });
|
|
118
|
+
|
|
119
|
+
console.log(chalk.green(`✓ Enabled hook: ${hookName}`));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Disable a specific hook
|
|
124
|
+
*/
|
|
125
|
+
async function disableHook(hookName, projectRoot) {
|
|
126
|
+
const claudeDir = getClaudeDir(projectRoot);
|
|
127
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
128
|
+
|
|
129
|
+
if (!(await fs.exists(settingsPath))) {
|
|
130
|
+
console.log(chalk.yellow('⚠ No settings.json found'));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const settings = await fs.readJson(settingsPath);
|
|
135
|
+
|
|
136
|
+
if (settings.hooks && settings.hooks[hookName]) {
|
|
137
|
+
settings.hooks[hookName].enabled = false;
|
|
138
|
+
await fs.writeJson(settingsPath, settings, { spaces: 2 });
|
|
139
|
+
console.log(chalk.green(`✓ Disabled hook: ${hookName}`));
|
|
140
|
+
} else {
|
|
141
|
+
console.log(chalk.yellow(`⚠ Hook ${hookName} is not configured`));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Interactive hooks setup
|
|
147
|
+
*/
|
|
148
|
+
async function interactiveSetup(projectRoot, options) {
|
|
149
|
+
const claudeDir = getClaudeDir(projectRoot);
|
|
150
|
+
|
|
151
|
+
console.log(chalk.blue('🔧 Claude Code Hooks Setup'));
|
|
152
|
+
console.log('');
|
|
153
|
+
|
|
154
|
+
const answer = await inquirer.prompt([
|
|
155
|
+
{
|
|
156
|
+
type: 'checkbox',
|
|
157
|
+
name: 'hooks',
|
|
158
|
+
message: 'Select hooks to enable:',
|
|
159
|
+
choices: [
|
|
160
|
+
{ name: 'pre_tool_use - Before any tool call', value: 'pre_tool_use', checked: true },
|
|
161
|
+
{ name: 'post_tool_use - After tool call completes', value: 'post_tool_use', checked: true },
|
|
162
|
+
{ name: 'notification - When notification triggered', value: 'notification', checked: false },
|
|
163
|
+
{ name: 'stop - When session stops', value: 'stop', checked: true }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
if (answer.hooks.length === 0) {
|
|
169
|
+
console.log(chalk.yellow('No hooks selected'));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const hooksDir = path.join(claudeDir, 'hooks');
|
|
174
|
+
await fs.ensureDir(hooksDir);
|
|
175
|
+
|
|
176
|
+
for (const hookName of answer.hooks) {
|
|
177
|
+
const hookScript = getHookScript(hookName);
|
|
178
|
+
const hookPath = path.join(hooksDir, `${hookName}.sh`);
|
|
179
|
+
await fs.writeFile(hookPath, hookScript);
|
|
180
|
+
await fs.chmod(hookPath, '755');
|
|
181
|
+
console.log(chalk.green(` ✓ Created hook: ${hookName}.sh`));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Update settings.json
|
|
185
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
186
|
+
let settings = {};
|
|
187
|
+
|
|
188
|
+
if (await fs.exists(settingsPath)) {
|
|
189
|
+
settings = await fs.readJson(settingsPath);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
settings.hooks = {};
|
|
193
|
+
for (const hookName of answer.hooks) {
|
|
194
|
+
settings.hooks[hookName] = {
|
|
195
|
+
command: `.claude/hooks/${hookName}.sh`,
|
|
196
|
+
enabled: true
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await fs.writeJson(settingsPath, settings, { spaces: 2 });
|
|
201
|
+
console.log(chalk.green(' ✓ Updated settings.json with hooks configuration'));
|
|
202
|
+
|
|
203
|
+
console.log('');
|
|
204
|
+
console.log(chalk.green('✅ Hooks setup complete!'));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get hook script content
|
|
209
|
+
*/
|
|
210
|
+
function getHookScript(hookName) {
|
|
211
|
+
const scripts = {
|
|
212
|
+
pre_tool_use: `#!/bin/bash
|
|
213
|
+
# Pre-tool-use hook for agent-team
|
|
214
|
+
# This runs before any tool is called
|
|
215
|
+
|
|
216
|
+
# Read stdin for tool info
|
|
217
|
+
read -r TOOL_INFO
|
|
218
|
+
|
|
219
|
+
# Log tool usage (optional)
|
|
220
|
+
# echo "Tool call: $TOOL_INFO" >> ~/.agent-team/logs/tool-usage.log
|
|
221
|
+
|
|
222
|
+
# Return 0 to allow tool, non-zero to block
|
|
223
|
+
exit 0
|
|
224
|
+
`,
|
|
225
|
+
post_tool_use: `#!/bin/bash
|
|
226
|
+
# Post-tool-use hook for agent-team
|
|
227
|
+
# This runs after a tool call completes
|
|
228
|
+
|
|
229
|
+
# Read stdin for result info
|
|
230
|
+
read -r TOOL_RESULT
|
|
231
|
+
|
|
232
|
+
# Log tool result (optional)
|
|
233
|
+
# echo "Tool result: $TOOL_RESULT" >> ~/.agent-team/logs/tool-usage.log
|
|
234
|
+
|
|
235
|
+
exit 0
|
|
236
|
+
`,
|
|
237
|
+
notification: `#!/bin/bash
|
|
238
|
+
# Notification hook for agent-team
|
|
239
|
+
# This runs when a notification is triggered
|
|
240
|
+
|
|
241
|
+
# Read notification content
|
|
242
|
+
read -r NOTIFICATION
|
|
243
|
+
|
|
244
|
+
# You can add custom notification handling here
|
|
245
|
+
# e.g., send to Slack, Discord, etc.
|
|
246
|
+
|
|
247
|
+
exit 0
|
|
248
|
+
`,
|
|
249
|
+
stop: `#!/bin/bash
|
|
250
|
+
# Stop hook for agent-team
|
|
251
|
+
# This runs when a session stops
|
|
252
|
+
|
|
253
|
+
# You can add cleanup or summary logic here
|
|
254
|
+
# e.g., save session state, generate summary
|
|
255
|
+
|
|
256
|
+
exit 0
|
|
257
|
+
`
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return scripts[hookName] || '';
|
|
261
|
+
}
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,5 @@
|
|
|
6
6
|
export { initCommand } from './commands/init.js';
|
|
7
7
|
export { addCommand } from './commands/add.js';
|
|
8
8
|
export { listCommand } from './commands/list.js';
|
|
9
|
-
export { switchCommand } from './commands/switch.js';
|
|
10
9
|
export { removeCommand } from './commands/remove.js';
|
|
11
10
|
export { resolveSkills } from './utils/skill-resolver.js';
|
package/src/utils/file-utils.js
CHANGED
|
@@ -39,12 +39,26 @@ export function getAgentsDir(projectRoot = getProjectRoot()) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Get specific agent directory path
|
|
42
|
+
* Get specific agent directory path (legacy - for backward compatibility)
|
|
43
43
|
*/
|
|
44
44
|
export function getAgentDir(agentName, projectRoot = getProjectRoot()) {
|
|
45
45
|
return path.join(getAgentsDir(projectRoot), agentName);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Get agent file path (new format: .claude/agents/<name>.md)
|
|
50
|
+
*/
|
|
51
|
+
export function getAgentFilePath(agentName, projectRoot = getProjectRoot()) {
|
|
52
|
+
return path.join(getAgentsDir(projectRoot), `${agentName}.md`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get agent memory directory path (.claude/agent-memory/<name>/)
|
|
57
|
+
*/
|
|
58
|
+
export function getAgentMemoryDir(agentName, projectRoot = getProjectRoot()) {
|
|
59
|
+
return path.join(getClaudeDir(projectRoot), 'agent-memory', agentName);
|
|
60
|
+
}
|
|
61
|
+
|
|
48
62
|
/**
|
|
49
63
|
* Check if .claude directory exists
|
|
50
64
|
*/
|
|
@@ -53,9 +67,15 @@ export async function isInitialized(projectRoot = getProjectRoot()) {
|
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/**
|
|
56
|
-
* Check if agent exists
|
|
70
|
+
* Check if agent exists (checks for .md file)
|
|
57
71
|
*/
|
|
58
72
|
export async function agentExists(agentName, projectRoot = getProjectRoot()) {
|
|
73
|
+
// Check for new format (.md file)
|
|
74
|
+
const agentFile = getAgentFilePath(agentName, projectRoot);
|
|
75
|
+
if (await fs.exists(agentFile)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Also check for legacy format (directory)
|
|
59
79
|
return fs.exists(getAgentDir(agentName, projectRoot));
|
|
60
80
|
}
|
|
61
81
|
|
|
@@ -73,7 +93,46 @@ export async function getAgents(projectRoot = getProjectRoot()) {
|
|
|
73
93
|
const agents = [];
|
|
74
94
|
|
|
75
95
|
for (const entry of entries) {
|
|
76
|
-
|
|
96
|
+
// New format: .md files
|
|
97
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
98
|
+
const agentName = entry.name.replace('.md', '');
|
|
99
|
+
const agentPath = path.join(agentsDir, entry.name);
|
|
100
|
+
|
|
101
|
+
// Parse frontmatter to get metadata
|
|
102
|
+
let metadata = { name: agentName, description: '', skills: [] };
|
|
103
|
+
try {
|
|
104
|
+
const content = await fs.readFile(agentPath, 'utf-8');
|
|
105
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
106
|
+
if (frontmatterMatch) {
|
|
107
|
+
const frontmatter = frontmatterMatch[1];
|
|
108
|
+
// Parse simple YAML fields
|
|
109
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
110
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
111
|
+
const skillsMatch = frontmatter.match(/^skills:\s*\n((?: - .+\n?)+)/m);
|
|
112
|
+
|
|
113
|
+
if (nameMatch) metadata.name = nameMatch[1].trim();
|
|
114
|
+
if (descMatch) metadata.description = descMatch[1].trim();
|
|
115
|
+
if (skillsMatch) {
|
|
116
|
+
metadata.skills = skillsMatch[1]
|
|
117
|
+
.split('\n')
|
|
118
|
+
.filter(line => line.trim().startsWith('-'))
|
|
119
|
+
.map(line => line.replace(/^\s*-\s*/, '').trim());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
// Ignore parse errors
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
agents.push({
|
|
127
|
+
name: agentName,
|
|
128
|
+
path: agentPath,
|
|
129
|
+
format: 'new',
|
|
130
|
+
description: metadata.description,
|
|
131
|
+
skills: metadata.skills
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Legacy format: directories
|
|
135
|
+
else if (entry.isDirectory()) {
|
|
77
136
|
const agentDir = path.join(agentsDir, entry.name);
|
|
78
137
|
const variantsPath = path.join(agentDir, 'variants.json');
|
|
79
138
|
const skillsDir = path.join(agentDir, 'skills');
|
|
@@ -103,6 +162,7 @@ export async function getAgents(projectRoot = getProjectRoot()) {
|
|
|
103
162
|
agents.push({
|
|
104
163
|
name: entry.name,
|
|
105
164
|
path: agentDir,
|
|
165
|
+
format: 'legacy',
|
|
106
166
|
variants,
|
|
107
167
|
skills
|
|
108
168
|
});
|
|
@@ -113,61 +173,85 @@ export async function getAgents(projectRoot = getProjectRoot()) {
|
|
|
113
173
|
}
|
|
114
174
|
|
|
115
175
|
/**
|
|
116
|
-
* Copy
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
path.join(skillsSourceDir, 'domain', 'backend', skillName),
|
|
135
|
-
// Domain skills - architecture
|
|
136
|
-
path.join(skillsSourceDir, 'domain', 'architecture', skillName),
|
|
137
|
-
// Domain skills - devops
|
|
138
|
-
path.join(skillsSourceDir, 'domain', 'devops', skillName),
|
|
139
|
-
// Domain skills - product
|
|
140
|
-
path.join(skillsSourceDir, 'domain', 'product', skillName),
|
|
141
|
-
// Domain skills - quality
|
|
142
|
-
path.join(skillsSourceDir, 'domain', 'quality', skillName),
|
|
143
|
-
// Leadership skills
|
|
144
|
-
path.join(skillsSourceDir, 'leadership', skillName),
|
|
145
|
-
// Community skills
|
|
146
|
-
path.join(skillsSourceDir, 'community', skillName),
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
for (const skillPath of possiblePaths) {
|
|
150
|
-
if (await fs.exists(skillPath)) {
|
|
151
|
-
const targetPath = path.join(targetSkillsDir, skillName);
|
|
152
|
-
await fs.copy(skillPath, targetPath);
|
|
153
|
-
copiedSkills.push(skillName);
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
176
|
+
* Copy rules from package to project
|
|
177
|
+
* @param {string} projectRoot - Project root directory
|
|
178
|
+
* @param {Object} options - Options for copying
|
|
179
|
+
* @param {string[]} options.includeRoleRules - List of roles to copy rules for
|
|
180
|
+
* @param {boolean} options.overwrite - Whether to overwrite existing files
|
|
181
|
+
*/
|
|
182
|
+
export async function copyRules(projectRoot = getProjectRoot(), options = {}) {
|
|
183
|
+
const { includeRoleRules = [], overwrite = false } = options;
|
|
184
|
+
const rulesSourceDir = path.join(PACKAGE_ROOT, '.claude', 'rules');
|
|
185
|
+
const targetRulesDir = path.join(getClaudeDir(projectRoot), 'rules');
|
|
186
|
+
|
|
187
|
+
await fs.ensureDir(targetRulesDir);
|
|
188
|
+
|
|
189
|
+
// Always copy common rules
|
|
190
|
+
const commonSourceDir = path.join(rulesSourceDir, 'common');
|
|
191
|
+
const commonTargetDir = path.join(targetRulesDir, 'common');
|
|
192
|
+
if (await fs.exists(commonSourceDir)) {
|
|
193
|
+
await fs.copy(commonSourceDir, commonTargetDir, { overwrite });
|
|
157
194
|
}
|
|
158
195
|
|
|
159
|
-
|
|
196
|
+
// Always ensure lessons directory exists and copy template if not present
|
|
197
|
+
const lessonsSourceDir = path.join(rulesSourceDir, 'lessons');
|
|
198
|
+
const lessonsTargetDir = path.join(targetRulesDir, 'lessons');
|
|
199
|
+
await fs.ensureDir(lessonsTargetDir);
|
|
200
|
+
|
|
201
|
+
const lessonsTemplate = path.join(lessonsSourceDir, 'lessons.md');
|
|
202
|
+
const lessonsTarget = path.join(lessonsTargetDir, 'lessons.md');
|
|
203
|
+
if (await fs.exists(lessonsTemplate) && (!(await fs.exists(lessonsTarget)) || overwrite)) {
|
|
204
|
+
await fs.copy(lessonsTemplate, lessonsTarget);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Create role-rules directory (empty by default)
|
|
208
|
+
const roleRulesTargetDir = path.join(targetRulesDir, 'role-rules');
|
|
209
|
+
await fs.ensureDir(roleRulesTargetDir);
|
|
210
|
+
|
|
211
|
+
// Only copy specific role rules if requested
|
|
212
|
+
for (const role of includeRoleRules) {
|
|
213
|
+
const roleRuleFile = `${role}-rules.md`;
|
|
214
|
+
const roleRuleSource = path.join(rulesSourceDir, 'role-rules', roleRuleFile);
|
|
215
|
+
const roleRuleTarget = path.join(roleRulesTargetDir, roleRuleFile);
|
|
216
|
+
if (await fs.exists(roleRuleSource)) {
|
|
217
|
+
await fs.copy(roleRuleSource, roleRuleTarget, { overwrite });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
160
220
|
}
|
|
161
221
|
|
|
162
222
|
/**
|
|
163
|
-
* Copy rules
|
|
223
|
+
* Copy role-specific rules when adding agent
|
|
224
|
+
* @param {string} role - Role name (e.g., 'dev-fe', 'sa')
|
|
225
|
+
* @param {string} projectRoot - Project root directory
|
|
226
|
+
* @returns {string|null} - Name of copied file or null if not copied
|
|
164
227
|
*/
|
|
165
|
-
export async function
|
|
166
|
-
const rulesSourceDir = path.join(PACKAGE_ROOT, '.claude', 'rules');
|
|
167
|
-
const targetRulesDir = path.join(getClaudeDir(projectRoot), 'rules');
|
|
228
|
+
export async function copyRoleRules(role, projectRoot = getProjectRoot()) {
|
|
229
|
+
const rulesSourceDir = path.join(PACKAGE_ROOT, '.claude', 'rules', 'role-rules');
|
|
230
|
+
const targetRulesDir = path.join(getClaudeDir(projectRoot), 'rules', 'role-rules');
|
|
168
231
|
|
|
169
232
|
await fs.ensureDir(targetRulesDir);
|
|
170
|
-
|
|
233
|
+
|
|
234
|
+
// Map role to rule file
|
|
235
|
+
const roleRuleMap = {
|
|
236
|
+
'dev-fe': 'dev-fe-rules.md',
|
|
237
|
+
'sa': 'sa-rules.md',
|
|
238
|
+
'dev-be': 'dev-be-rules.md',
|
|
239
|
+
'tech-lead': 'tech-lead-rules.md',
|
|
240
|
+
'devops': 'devops-rules.md',
|
|
241
|
+
'pm': 'pm-rules.md',
|
|
242
|
+
'qa': 'qa-rules.md'
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const ruleFile = roleRuleMap[role];
|
|
246
|
+
if (ruleFile) {
|
|
247
|
+
const source = path.join(rulesSourceDir, ruleFile);
|
|
248
|
+
const target = path.join(targetRulesDir, ruleFile);
|
|
249
|
+
if (await fs.exists(source) && !(await fs.exists(target))) {
|
|
250
|
+
await fs.copy(source, target);
|
|
251
|
+
return ruleFile;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
171
255
|
}
|
|
172
256
|
|
|
173
257
|
/**
|
|
@@ -191,3 +275,16 @@ export function processTemplate(template, variables) {
|
|
|
191
275
|
|
|
192
276
|
return result;
|
|
193
277
|
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Read role behavioral template
|
|
281
|
+
* @param {string} role - Role name (e.g., 'dev-fe', 'sa')
|
|
282
|
+
* @returns {string|null} - Template content or null if not found
|
|
283
|
+
*/
|
|
284
|
+
export async function readRoleTemplate(role) {
|
|
285
|
+
const templatePath = path.join(PACKAGE_ROOT, 'templates', 'roles', `${role}.md`);
|
|
286
|
+
if (await fs.exists(templatePath)) {
|
|
287
|
+
return fs.readFile(templatePath, 'utf-8');
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|