create-dev-agents 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/LICENSE +21 -0
- package/README.md +263 -0
- package/bin/cli.js +74 -0
- package/package.json +54 -0
- package/src/index.js +339 -0
- package/templates/CLAUDE.md +38 -0
- package/templates/agents/code-review/review.md +158 -0
- package/templates/agents/feature/develop.md +113 -0
- package/templates/agents/git/commit-pr.md +222 -0
- package/templates/agents/jira/ticket.md +148 -0
- package/templates/agents/project/scaffold.md +118 -0
- package/templates/commands/commit.md +130 -0
- package/templates/commands/feature.md +99 -0
- package/templates/commands/new-project.md +325 -0
- package/templates/commands/pr.md +175 -0
- package/templates/commands/review.md +200 -0
- package/templates/commands/ticket.md +135 -0
- package/templates/commit/examples.md +149 -0
- package/templates/jira/bug.md +49 -0
- package/templates/jira/story.md +36 -0
- package/templates/mcp-settings.json +32 -0
- package/templates/pr/bugfix.md +57 -0
- package/templates/pr/feature.md +48 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import enquirer from 'enquirer';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
11
|
+
|
|
12
|
+
const COMMANDS = {
|
|
13
|
+
'new-project': 'Create new projects (Expo, React Native, React, Next.js)',
|
|
14
|
+
'feature': 'Implement complete features with components, hooks, tests',
|
|
15
|
+
'commit': 'Generate conventional commit messages',
|
|
16
|
+
'pr': 'Create pull requests with proper descriptions',
|
|
17
|
+
'ticket': 'Create Jira tickets with templates',
|
|
18
|
+
'review': 'Code review with actionable feedback'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const AGENTS = {
|
|
22
|
+
'project': 'Project scaffolding agent',
|
|
23
|
+
'feature': 'Feature development agent',
|
|
24
|
+
'git': 'Git workflow agent (commits, PRs)',
|
|
25
|
+
'jira': 'Jira ticket agent',
|
|
26
|
+
'code-review': 'Code review agent'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export async function init(options = {}) {
|
|
30
|
+
const spinner = ora();
|
|
31
|
+
const targetDir = options.global ? path.join(process.env.HOME, '.claude') : process.cwd();
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Prompt for what to install
|
|
35
|
+
let selections;
|
|
36
|
+
if (options.all) {
|
|
37
|
+
selections = { commands: Object.keys(COMMANDS), extras: ['agents', 'templates', 'scripts', 'mcp'] };
|
|
38
|
+
} else if (options.minimal) {
|
|
39
|
+
selections = { commands: ['commit', 'pr', 'review'], extras: [] };
|
|
40
|
+
} else {
|
|
41
|
+
const { Select, MultiSelect } = enquirer;
|
|
42
|
+
|
|
43
|
+
const commandPrompt = new MultiSelect({
|
|
44
|
+
name: 'commands',
|
|
45
|
+
message: 'Select commands to install',
|
|
46
|
+
choices: Object.entries(COMMANDS).map(([name, desc]) => ({
|
|
47
|
+
name,
|
|
48
|
+
message: `/${name}`,
|
|
49
|
+
hint: desc
|
|
50
|
+
})),
|
|
51
|
+
initial: Object.keys(COMMANDS)
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const extrasPrompt = new MultiSelect({
|
|
55
|
+
name: 'extras',
|
|
56
|
+
message: 'Select additional components',
|
|
57
|
+
choices: [
|
|
58
|
+
{ name: 'agents', message: 'Agent definitions', hint: 'Detailed agent behaviors' },
|
|
59
|
+
{ name: 'templates', message: 'Templates', hint: 'PR, commit, Jira templates' },
|
|
60
|
+
{ name: 'scripts', message: 'Shell scripts', hint: 'Automation scripts' },
|
|
61
|
+
{ name: 'mcp', message: 'MCP config', hint: 'GitHub, Jira, Git servers' }
|
|
62
|
+
],
|
|
63
|
+
initial: ['mcp']
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const commands = await commandPrompt.run();
|
|
67
|
+
const extras = await extrasPrompt.run();
|
|
68
|
+
selections = { commands, extras };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create directories
|
|
72
|
+
spinner.start('Creating directories...');
|
|
73
|
+
const dirs = ['.claude/commands'];
|
|
74
|
+
if (selections.extras.includes('agents')) dirs.push('agents');
|
|
75
|
+
if (selections.extras.includes('templates')) dirs.push('templates');
|
|
76
|
+
if (selections.extras.includes('scripts')) dirs.push('scripts');
|
|
77
|
+
|
|
78
|
+
for (const dir of dirs) {
|
|
79
|
+
const fullPath = path.join(targetDir, dir);
|
|
80
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
spinner.succeed('Directories created');
|
|
83
|
+
|
|
84
|
+
// Copy commands
|
|
85
|
+
spinner.start('Installing commands...');
|
|
86
|
+
for (const cmd of selections.commands) {
|
|
87
|
+
const src = path.join(TEMPLATES_DIR, 'commands', `${cmd}.md`);
|
|
88
|
+
const dest = path.join(targetDir, '.claude', 'commands', `${cmd}.md`);
|
|
89
|
+
if (fs.existsSync(src)) {
|
|
90
|
+
fs.copyFileSync(src, dest);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
spinner.succeed(`Installed ${selections.commands.length} commands`);
|
|
94
|
+
|
|
95
|
+
// Copy extras
|
|
96
|
+
if (selections.extras.includes('agents')) {
|
|
97
|
+
spinner.start('Installing agents...');
|
|
98
|
+
copyDir(path.join(TEMPLATES_DIR, 'agents'), path.join(targetDir, 'agents'));
|
|
99
|
+
spinner.succeed('Agents installed');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (selections.extras.includes('templates')) {
|
|
103
|
+
spinner.start('Installing templates...');
|
|
104
|
+
copyDir(path.join(TEMPLATES_DIR, 'templates'), path.join(targetDir, 'templates'));
|
|
105
|
+
spinner.succeed('Templates installed');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (selections.extras.includes('scripts')) {
|
|
109
|
+
spinner.start('Installing scripts...');
|
|
110
|
+
copyDir(path.join(TEMPLATES_DIR, 'scripts'), path.join(targetDir, 'scripts'));
|
|
111
|
+
// Make scripts executable
|
|
112
|
+
const scriptsDir = path.join(targetDir, 'scripts');
|
|
113
|
+
if (fs.existsSync(scriptsDir)) {
|
|
114
|
+
for (const file of fs.readdirSync(scriptsDir)) {
|
|
115
|
+
if (file.endsWith('.sh')) {
|
|
116
|
+
fs.chmodSync(path.join(scriptsDir, file), '755');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
spinner.succeed('Scripts installed');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (selections.extras.includes('mcp')) {
|
|
124
|
+
spinner.start('Installing MCP config...');
|
|
125
|
+
const src = path.join(TEMPLATES_DIR, 'mcp-settings.json');
|
|
126
|
+
const dest = path.join(targetDir, '.claude', 'settings.json');
|
|
127
|
+
if (fs.existsSync(src) && !fs.existsSync(dest)) {
|
|
128
|
+
fs.copyFileSync(src, dest);
|
|
129
|
+
}
|
|
130
|
+
spinner.succeed('MCP config installed');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Create CLAUDE.md if not exists
|
|
134
|
+
const claudeMd = path.join(targetDir, 'CLAUDE.md');
|
|
135
|
+
if (!fs.existsSync(claudeMd) && !options.global) {
|
|
136
|
+
const template = path.join(TEMPLATES_DIR, 'CLAUDE.md');
|
|
137
|
+
if (fs.existsSync(template)) {
|
|
138
|
+
fs.copyFileSync(template, claudeMd);
|
|
139
|
+
spinner.succeed('Created CLAUDE.md');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Success message
|
|
144
|
+
console.log(chalk.green.bold('\n✅ Dev Agents installed successfully!\n'));
|
|
145
|
+
console.log(chalk.white('Available commands:'));
|
|
146
|
+
for (const cmd of selections.commands) {
|
|
147
|
+
console.log(chalk.yellow(` /${cmd}`) + chalk.gray(` - ${COMMANDS[cmd]}`));
|
|
148
|
+
}
|
|
149
|
+
console.log();
|
|
150
|
+
console.log(chalk.white('Next steps:'));
|
|
151
|
+
console.log(chalk.gray(' 1. Open this project in Claude Code'));
|
|
152
|
+
console.log(chalk.gray(' 2. Type a command like /new-project or /commit'));
|
|
153
|
+
if (selections.extras.includes('mcp')) {
|
|
154
|
+
console.log(chalk.gray(' 3. Run `dev-agents mcp` to configure API tokens'));
|
|
155
|
+
}
|
|
156
|
+
console.log();
|
|
157
|
+
|
|
158
|
+
} catch (error) {
|
|
159
|
+
spinner.fail('Installation failed');
|
|
160
|
+
console.error(chalk.red(error.message));
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function addCommand(command) {
|
|
166
|
+
const spinner = ora();
|
|
167
|
+
|
|
168
|
+
if (!COMMANDS[command]) {
|
|
169
|
+
console.log(chalk.red(`Unknown command: ${command}`));
|
|
170
|
+
console.log(chalk.white('\nAvailable commands:'));
|
|
171
|
+
Object.keys(COMMANDS).forEach(cmd => {
|
|
172
|
+
console.log(chalk.yellow(` ${cmd}`) + chalk.gray(` - ${COMMANDS[cmd]}`));
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
spinner.start(`Adding /${command}...`);
|
|
178
|
+
|
|
179
|
+
const targetDir = process.cwd();
|
|
180
|
+
fs.mkdirSync(path.join(targetDir, '.claude', 'commands'), { recursive: true });
|
|
181
|
+
|
|
182
|
+
const src = path.join(TEMPLATES_DIR, 'commands', `${command}.md`);
|
|
183
|
+
const dest = path.join(targetDir, '.claude', 'commands', `${command}.md`);
|
|
184
|
+
|
|
185
|
+
if (fs.existsSync(src)) {
|
|
186
|
+
fs.copyFileSync(src, dest);
|
|
187
|
+
spinner.succeed(`Added /${command}`);
|
|
188
|
+
} else {
|
|
189
|
+
spinner.fail(`Template not found for ${command}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function listCommands() {
|
|
194
|
+
console.log(chalk.blue.bold('\n📋 Available Commands\n'));
|
|
195
|
+
|
|
196
|
+
console.log(chalk.white.bold('Slash Commands (/.claude/commands/):'));
|
|
197
|
+
Object.entries(COMMANDS).forEach(([name, desc]) => {
|
|
198
|
+
console.log(chalk.yellow(` /${name}`) + chalk.gray(` - ${desc}`));
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
console.log(chalk.white.bold('\nAgents (/agents/):'));
|
|
202
|
+
Object.entries(AGENTS).forEach(([name, desc]) => {
|
|
203
|
+
console.log(chalk.cyan(` ${name}`) + chalk.gray(` - ${desc}`));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
console.log(chalk.white.bold('\nTemplates (/templates/):'));
|
|
207
|
+
console.log(chalk.gray(' pr/feature.md, pr/bugfix.md'));
|
|
208
|
+
console.log(chalk.gray(' jira/story.md, jira/bug.md'));
|
|
209
|
+
console.log(chalk.gray(' commit/examples.md'));
|
|
210
|
+
console.log();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function setupMcp() {
|
|
214
|
+
const { Input, Password } = enquirer;
|
|
215
|
+
|
|
216
|
+
console.log(chalk.blue.bold('\n🔌 MCP Server Configuration\n'));
|
|
217
|
+
console.log(chalk.gray('Configure API tokens for GitHub and Jira integration.\n'));
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
// GitHub
|
|
221
|
+
console.log(chalk.white.bold('GitHub Configuration'));
|
|
222
|
+
console.log(chalk.gray('Create token at: https://github.com/settings/tokens'));
|
|
223
|
+
console.log(chalk.gray('Required scopes: repo, read:user\n'));
|
|
224
|
+
|
|
225
|
+
const githubPrompt = new Password({
|
|
226
|
+
name: 'github',
|
|
227
|
+
message: 'GitHub Token (leave empty to skip)'
|
|
228
|
+
});
|
|
229
|
+
const githubToken = await githubPrompt.run();
|
|
230
|
+
|
|
231
|
+
// Jira
|
|
232
|
+
console.log(chalk.white.bold('\nJira Configuration'));
|
|
233
|
+
console.log(chalk.gray('Create token at: https://id.atlassian.com/manage-profile/security/api-tokens\n'));
|
|
234
|
+
|
|
235
|
+
const jiraHostPrompt = new Input({
|
|
236
|
+
name: 'host',
|
|
237
|
+
message: 'Jira Host (e.g., company.atlassian.net)',
|
|
238
|
+
initial: ''
|
|
239
|
+
});
|
|
240
|
+
const jiraHost = await jiraHostPrompt.run();
|
|
241
|
+
|
|
242
|
+
let jiraEmail = '', jiraToken = '';
|
|
243
|
+
if (jiraHost) {
|
|
244
|
+
const emailPrompt = new Input({ name: 'email', message: 'Jira Email' });
|
|
245
|
+
const tokenPrompt = new Password({ name: 'token', message: 'Jira API Token' });
|
|
246
|
+
jiraEmail = await emailPrompt.run();
|
|
247
|
+
jiraToken = await tokenPrompt.run();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Write to env file
|
|
251
|
+
const envContent = `# Dev Agents MCP Configuration
|
|
252
|
+
# Source this file: source ~/.dev-agents-env
|
|
253
|
+
|
|
254
|
+
# GitHub
|
|
255
|
+
export GITHUB_TOKEN="${githubToken}"
|
|
256
|
+
|
|
257
|
+
# Jira
|
|
258
|
+
export JIRA_HOST="${jiraHost}"
|
|
259
|
+
export JIRA_EMAIL="${jiraEmail}"
|
|
260
|
+
export JIRA_API_TOKEN="${jiraToken}"
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
const envPath = path.join(process.env.HOME, '.dev-agents-env');
|
|
264
|
+
fs.writeFileSync(envPath, envContent, { mode: 0o600 });
|
|
265
|
+
|
|
266
|
+
console.log(chalk.green.bold('\n✅ Configuration saved!\n'));
|
|
267
|
+
console.log(chalk.white('Add to your shell profile:'));
|
|
268
|
+
console.log(chalk.yellow(` echo 'source ~/.dev-agents-env' >> ~/.zshrc`));
|
|
269
|
+
console.log(chalk.white('\nOr run:'));
|
|
270
|
+
console.log(chalk.yellow(` source ~/.dev-agents-env`));
|
|
271
|
+
console.log();
|
|
272
|
+
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (error.message !== 'cancelled') {
|
|
275
|
+
console.error(chalk.red(error.message));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export async function installGlobal() {
|
|
281
|
+
const spinner = ora();
|
|
282
|
+
const globalDir = path.join(process.env.HOME, '.claude');
|
|
283
|
+
|
|
284
|
+
spinner.start('Installing globally to ~/.claude...');
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
// Create directories
|
|
288
|
+
fs.mkdirSync(path.join(globalDir, 'commands'), { recursive: true });
|
|
289
|
+
|
|
290
|
+
// Copy all commands
|
|
291
|
+
const commandsDir = path.join(TEMPLATES_DIR, 'commands');
|
|
292
|
+
if (fs.existsSync(commandsDir)) {
|
|
293
|
+
for (const file of fs.readdirSync(commandsDir)) {
|
|
294
|
+
fs.copyFileSync(
|
|
295
|
+
path.join(commandsDir, file),
|
|
296
|
+
path.join(globalDir, 'commands', file)
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Copy MCP settings if not exists
|
|
302
|
+
const settingsSrc = path.join(TEMPLATES_DIR, 'mcp-settings.json');
|
|
303
|
+
const settingsDest = path.join(globalDir, 'settings.json');
|
|
304
|
+
if (fs.existsSync(settingsSrc) && !fs.existsSync(settingsDest)) {
|
|
305
|
+
fs.copyFileSync(settingsSrc, settingsDest);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
spinner.succeed('Installed globally');
|
|
309
|
+
|
|
310
|
+
console.log(chalk.green.bold('\n✅ Commands available in all projects!\n'));
|
|
311
|
+
console.log(chalk.white('Installed commands:'));
|
|
312
|
+
Object.keys(COMMANDS).forEach(cmd => {
|
|
313
|
+
console.log(chalk.yellow(` /${cmd}`));
|
|
314
|
+
});
|
|
315
|
+
console.log();
|
|
316
|
+
|
|
317
|
+
} catch (error) {
|
|
318
|
+
spinner.fail('Global installation failed');
|
|
319
|
+
console.error(chalk.red(error.message));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Helper: Copy directory recursively
|
|
324
|
+
function copyDir(src, dest) {
|
|
325
|
+
if (!fs.existsSync(src)) return;
|
|
326
|
+
|
|
327
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
328
|
+
|
|
329
|
+
for (const item of fs.readdirSync(src)) {
|
|
330
|
+
const srcPath = path.join(src, item);
|
|
331
|
+
const destPath = path.join(dest, item);
|
|
332
|
+
|
|
333
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
334
|
+
copyDir(srcPath, destPath);
|
|
335
|
+
} else {
|
|
336
|
+
fs.copyFileSync(srcPath, destPath);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Project Development Guide
|
|
2
|
+
|
|
3
|
+
## Available Commands
|
|
4
|
+
|
|
5
|
+
| Command | Description |
|
|
6
|
+
|---------|-------------|
|
|
7
|
+
| `/new-project` | Create new project (Expo, React, Next.js) |
|
|
8
|
+
| `/feature [name]` | Implement a complete feature |
|
|
9
|
+
| `/commit` | Generate conventional commit message |
|
|
10
|
+
| `/pr` | Create pull request with description |
|
|
11
|
+
| `/ticket` | Create Jira ticket |
|
|
12
|
+
| `/review [file]` | Code review with suggestions |
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Create a new project
|
|
18
|
+
/new-project
|
|
19
|
+
|
|
20
|
+
# Implement a feature
|
|
21
|
+
/feature user-authentication
|
|
22
|
+
|
|
23
|
+
# After making changes
|
|
24
|
+
/commit
|
|
25
|
+
/pr
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## MCP Integrations
|
|
29
|
+
|
|
30
|
+
If configured, these commands can interact with:
|
|
31
|
+
- **GitHub** - Create PRs, issues, request reviews
|
|
32
|
+
- **Jira** - Create and update tickets
|
|
33
|
+
- **Git** - Commits, branches, diffs
|
|
34
|
+
|
|
35
|
+
Run `dev-agents mcp` to configure API tokens.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
*Powered by [create-dev-agents](https://github.com/yourusername/create-dev-agents)*
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Code Review Agent
|
|
2
|
+
|
|
3
|
+
You are a code review assistant that helps review code for quality, security, and best practices.
|
|
4
|
+
|
|
5
|
+
## Review Checklist
|
|
6
|
+
|
|
7
|
+
### 1. Code Quality
|
|
8
|
+
- [ ] Code is readable and self-documenting
|
|
9
|
+
- [ ] Functions are small and focused (Single Responsibility)
|
|
10
|
+
- [ ] No code duplication (DRY principle)
|
|
11
|
+
- [ ] Proper error handling
|
|
12
|
+
- [ ] Meaningful variable/function names
|
|
13
|
+
- [ ] No magic numbers/strings (use constants)
|
|
14
|
+
|
|
15
|
+
### 2. TypeScript/Type Safety
|
|
16
|
+
- [ ] Proper types defined (no `any` without justification)
|
|
17
|
+
- [ ] Interfaces for complex objects
|
|
18
|
+
- [ ] Null/undefined handled properly
|
|
19
|
+
- [ ] Generic types used appropriately
|
|
20
|
+
- [ ] Type guards where needed
|
|
21
|
+
|
|
22
|
+
### 3. React/React Native Specific
|
|
23
|
+
- [ ] Components follow single responsibility
|
|
24
|
+
- [ ] Proper use of hooks (dependencies correct)
|
|
25
|
+
- [ ] Memoization where beneficial (React.memo, useMemo, useCallback)
|
|
26
|
+
- [ ] No unnecessary re-renders
|
|
27
|
+
- [ ] Proper key props in lists
|
|
28
|
+
- [ ] Event handlers properly bound
|
|
29
|
+
|
|
30
|
+
### 4. Performance
|
|
31
|
+
- [ ] No expensive operations in render
|
|
32
|
+
- [ ] Large lists use virtualization (FlatList)
|
|
33
|
+
- [ ] Images optimized
|
|
34
|
+
- [ ] Lazy loading implemented where appropriate
|
|
35
|
+
- [ ] No memory leaks (cleanup in useEffect)
|
|
36
|
+
|
|
37
|
+
### 5. Security
|
|
38
|
+
- [ ] No sensitive data in logs
|
|
39
|
+
- [ ] Input validation present
|
|
40
|
+
- [ ] SQL injection prevention (prepared statements)
|
|
41
|
+
- [ ] XSS prevention
|
|
42
|
+
- [ ] Secrets not hardcoded
|
|
43
|
+
|
|
44
|
+
### 6. Accessibility
|
|
45
|
+
- [ ] Proper accessibility labels
|
|
46
|
+
- [ ] Touch targets >= 44pt
|
|
47
|
+
- [ ] Color contrast sufficient
|
|
48
|
+
- [ ] Screen reader support
|
|
49
|
+
|
|
50
|
+
### 7. Testing
|
|
51
|
+
- [ ] Unit tests for logic
|
|
52
|
+
- [ ] Component tests for UI
|
|
53
|
+
- [ ] Edge cases covered
|
|
54
|
+
- [ ] Error scenarios tested
|
|
55
|
+
|
|
56
|
+
## Review Output Format
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
## Code Review Summary
|
|
60
|
+
|
|
61
|
+
**Files Reviewed:** [number]
|
|
62
|
+
**Overall Quality:** [Excellent/Good/Needs Improvement/Major Issues]
|
|
63
|
+
|
|
64
|
+
### Critical Issues (Must Fix)
|
|
65
|
+
1. **[File:Line]** - [Issue description]
|
|
66
|
+
- **Problem:** [What's wrong]
|
|
67
|
+
- **Impact:** [Why it matters]
|
|
68
|
+
- **Suggestion:** [How to fix]
|
|
69
|
+
|
|
70
|
+
### Improvements (Should Fix)
|
|
71
|
+
1. **[File:Line]** - [Issue description]
|
|
72
|
+
- **Suggestion:** [Improvement]
|
|
73
|
+
|
|
74
|
+
### Suggestions (Nice to Have)
|
|
75
|
+
1. **[File:Line]** - [Suggestion]
|
|
76
|
+
|
|
77
|
+
### Positive Highlights
|
|
78
|
+
- [Good practice observed]
|
|
79
|
+
- [Well-written code section]
|
|
80
|
+
|
|
81
|
+
### Summary
|
|
82
|
+
[Overall assessment and recommendation]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Common Issues to Check
|
|
86
|
+
|
|
87
|
+
### React Hooks
|
|
88
|
+
```typescript
|
|
89
|
+
// Bad: Missing dependency
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
fetchData(userId);
|
|
92
|
+
}, []); // userId should be in deps
|
|
93
|
+
|
|
94
|
+
// Good: All dependencies included
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
fetchData(userId);
|
|
97
|
+
}, [userId]);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Memory Leaks
|
|
101
|
+
```typescript
|
|
102
|
+
// Bad: No cleanup
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const subscription = eventEmitter.subscribe(handler);
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
// Good: Proper cleanup
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const subscription = eventEmitter.subscribe(handler);
|
|
110
|
+
return () => subscription.unsubscribe();
|
|
111
|
+
}, []);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Type Safety
|
|
115
|
+
```typescript
|
|
116
|
+
// Bad: Using any
|
|
117
|
+
const handleData = (data: any) => { ... }
|
|
118
|
+
|
|
119
|
+
// Good: Proper typing
|
|
120
|
+
interface UserData {
|
|
121
|
+
id: string;
|
|
122
|
+
name: string;
|
|
123
|
+
}
|
|
124
|
+
const handleData = (data: UserData) => { ... }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Error Handling
|
|
128
|
+
```typescript
|
|
129
|
+
// Bad: Silent failure
|
|
130
|
+
try {
|
|
131
|
+
await saveData();
|
|
132
|
+
} catch (e) {}
|
|
133
|
+
|
|
134
|
+
// Good: Proper handling
|
|
135
|
+
try {
|
|
136
|
+
await saveData();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
logger.error('Failed to save data', error);
|
|
139
|
+
showErrorToast('Could not save. Please try again.');
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Review Commands
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Review specific files
|
|
147
|
+
review_files(paths: ["src/components/Button.tsx"])
|
|
148
|
+
|
|
149
|
+
# Review PR
|
|
150
|
+
review_pull_request(number: 123)
|
|
151
|
+
|
|
152
|
+
# Suggest changes
|
|
153
|
+
suggest_change(
|
|
154
|
+
file: "src/utils.ts",
|
|
155
|
+
line: 42,
|
|
156
|
+
suggestion: "Consider using optional chaining"
|
|
157
|
+
)
|
|
158
|
+
```
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Feature Development Agent
|
|
2
|
+
|
|
3
|
+
You are a feature development assistant that helps implement features following best practices and project conventions.
|
|
4
|
+
|
|
5
|
+
## Workflow
|
|
6
|
+
|
|
7
|
+
### Step 1: Feature Analysis
|
|
8
|
+
When given a feature request:
|
|
9
|
+
1. Understand the requirements
|
|
10
|
+
2. Identify affected areas (UI, API, database, state)
|
|
11
|
+
3. Break down into tasks
|
|
12
|
+
4. Estimate complexity
|
|
13
|
+
|
|
14
|
+
### Step 2: Planning
|
|
15
|
+
Create an implementation plan:
|
|
16
|
+
- List files to create/modify
|
|
17
|
+
- Define component hierarchy
|
|
18
|
+
- Plan state management approach
|
|
19
|
+
- Identify API endpoints needed
|
|
20
|
+
- Consider edge cases and error handling
|
|
21
|
+
|
|
22
|
+
### Step 3: Implementation
|
|
23
|
+
For each task:
|
|
24
|
+
1. Create types/interfaces first
|
|
25
|
+
2. Implement core logic
|
|
26
|
+
3. Create UI components
|
|
27
|
+
4. Add state management
|
|
28
|
+
5. Connect everything
|
|
29
|
+
6. Add error handling
|
|
30
|
+
7. Write tests
|
|
31
|
+
|
|
32
|
+
## Feature Template
|
|
33
|
+
|
|
34
|
+
### Component Feature
|
|
35
|
+
```typescript
|
|
36
|
+
// types/[feature].types.ts
|
|
37
|
+
export interface Feature {
|
|
38
|
+
id: string;
|
|
39
|
+
// ... properties
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface FeatureState {
|
|
43
|
+
items: Feature[];
|
|
44
|
+
loading: boolean;
|
|
45
|
+
error: string | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// hooks/use[Feature].ts
|
|
49
|
+
export function useFeature() {
|
|
50
|
+
// Hook implementation
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// components/[Feature]/[Feature].tsx
|
|
54
|
+
export function Feature({ ...props }: FeatureProps) {
|
|
55
|
+
// Component implementation
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// services/[feature].service.ts
|
|
59
|
+
export const featureService = {
|
|
60
|
+
getAll: async () => {},
|
|
61
|
+
getById: async (id: string) => {},
|
|
62
|
+
create: async (data: CreateFeatureInput) => {},
|
|
63
|
+
update: async (id: string, data: UpdateFeatureInput) => {},
|
|
64
|
+
delete: async (id: string) => {},
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Conventions
|
|
69
|
+
|
|
70
|
+
### Naming
|
|
71
|
+
- Components: PascalCase (UserProfile.tsx)
|
|
72
|
+
- Hooks: camelCase with 'use' prefix (useAuth.ts)
|
|
73
|
+
- Services: camelCase with '.service' suffix
|
|
74
|
+
- Types: PascalCase for interfaces, camelCase for type aliases
|
|
75
|
+
- Constants: UPPER_SNAKE_CASE
|
|
76
|
+
|
|
77
|
+
### File Structure
|
|
78
|
+
Each feature should contain:
|
|
79
|
+
```
|
|
80
|
+
features/[feature]/
|
|
81
|
+
├── components/
|
|
82
|
+
│ ├── [Component].tsx
|
|
83
|
+
│ ├── [Component].styles.ts (or .module.css)
|
|
84
|
+
│ └── index.ts
|
|
85
|
+
├── hooks/
|
|
86
|
+
│ ├── use[Feature].ts
|
|
87
|
+
│ └── index.ts
|
|
88
|
+
├── services/
|
|
89
|
+
│ ├── [feature].service.ts
|
|
90
|
+
│ └── index.ts
|
|
91
|
+
├── types/
|
|
92
|
+
│ ├── [feature].types.ts
|
|
93
|
+
│ └── index.ts
|
|
94
|
+
├── utils/
|
|
95
|
+
│ └── [feature].utils.ts
|
|
96
|
+
└── index.ts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Code Quality
|
|
100
|
+
- Use TypeScript strict mode
|
|
101
|
+
- Add JSDoc comments for public APIs
|
|
102
|
+
- Handle loading and error states
|
|
103
|
+
- Implement proper accessibility
|
|
104
|
+
- Follow Single Responsibility Principle
|
|
105
|
+
|
|
106
|
+
## Output Format
|
|
107
|
+
|
|
108
|
+
When implementing a feature, provide:
|
|
109
|
+
1. **Plan**: Summary of what will be created
|
|
110
|
+
2. **Files**: List of files with their purposes
|
|
111
|
+
3. **Code**: Complete implementation
|
|
112
|
+
4. **Usage**: Example of how to use the feature
|
|
113
|
+
5. **Tests**: Test cases to verify functionality
|