Security Guidelines
CRITICAL: NEVER hardcode secrets or IDs in code. This is the most important rule.
Never Hardcode Secrets
NEVER write these in code:- API keys, tokens, passwords
- Project IDs, organization IDs
- Vercel project/org IDs
- Supabase URLs
- Discord IDs
- Database connection strings
- Any infrastructure identifier
.env files.
// ❌ WRONG - Hardcoded secret
const API_KEY = "AIzaSy...";
const SUPABASE_URL = "https://xyz.supabase.co";
// ✅ CORRECT - Environment variable
const API_KEY = process.env.GOOGLE_API_KEY;
const SUPABASE_URL = process.env.SUPABASE_URL;
Using Environment Variables
For Node.js:// Load environment variables
require('dotenv').config();
// Use process.env
const apiKey = process.env.API_KEY;
const dbUrl = process.env.DATABASE_URL;
// Always check for missing variables
if (!apiKey) {
throw new Error('API_KEY environment variable is required');
}
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Use os.environ.get()
api_key = os.environ.get('API_KEY')
db_url = os.environ.get('DATABASE_URL')
# Always check for missing variables
if not api_key:
raise ValueError('API_KEY environment variable is required')
.env File Structure
Create.env.example with placeholder values:
# .env.example
API_KEY=<YOUR_API_KEY>
DATABASE_URL=<YOUR_DATABASE_URL>
SUPABASE_URL=<YOUR_SUPABASE_URL>
SUPABASE_SERVICE_ROLE_KEY=<YOUR_SUPABASE_KEY>
.env is in .gitignore:
# .gitignore
.env
.env.local
.env.*.local
If You Accidentally Commit a Secret
If you commit a secret, it’s compromised FOREVER (git history). Act immediately.
Path Handling
Always Use Relative Paths
# ✅ Correct - Relative paths
.claude/scripts/
.claude/hooks/
.claude/agents/
$CLAUDE_PROJECT_DIR/.claude/
# ❌ Wrong - Absolute paths
/Users/username/.claude/
/home/user/project/.claude/
~/project/.claude/
C:\Users\username\.claude\
Cross-Platform Compatibility
Usepath.join() for cross-platform paths:
const path = require('path');
// ✅ Correct - Cross-platform
const configPath = path.join('.claude', 'settings.json');
const scriptPath = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude', 'scripts', 'hook.py');
// ❌ Wrong - Unix-only
const configPath = '.claude/settings.json';
const scriptPath = process.env.CLAUDE_PROJECT_DIR + '/.claude/scripts/hook.py';
Path Constants
Define path constants for reusability:const PATHS = {
CLAUDE_DIR: '.claude',
AGENTS_DIR: path.join('.claude', 'agents'),
COMMANDS_DIR: path.join('.claude', 'commands'),
HOOKS_DIR: path.join('.claude', 'hooks'),
SCRIPTS_DIR: path.join('.claude', 'scripts'),
SETTINGS_FILE: path.join('.claude', 'settings.json'),
MCP_FILE: '.mcp.json'
};
// Use constants
const agentPath = path.join(PATHS.AGENTS_DIR, `${agentName}.md`);
Naming Conventions
File Names
# JavaScript/TypeScript files
kebab-case.js # ✅ Utility files, scripts
PascalCase.js # ✅ Class definitions
index.js # ✅ Module entry points
# Component files (Markdown)
frontend-developer.md # ✅ Kebab-case
generate-tests.md # ✅ Kebab-case
# JSON configuration files
settings.json # ✅ Lowercase
package.json # ✅ Standard
components.json # ✅ Lowercase
# Python files
generate_components_json.py # ✅ Snake case
data_processor.py # ✅ Snake case
Variables and Functions
// Variables: camelCase
const userName = 'John';
const componentList = [];
let isActive = false;
// Functions: camelCase
function getUserData() { }
function processComponent(name) { }
const fetchAgents = async () => { };
// Constants: UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = 'https://api.example.com';
const DEFAULT_TIMEOUT = 30000;
// Classes: PascalCase
class ComponentProcessor { }
class DataCache { }
class WebSocketServer { }
// Private variables: _prefix
class Example {
constructor() {
this._privateData = null;
}
}
Component Names
# Agent names: kebab-case
frontend-developer
api-security-auditor
database-optimizer
# Command names: kebab-case
generate-tests
setup-development-environment
code-review
# Hook names: kebab-case
simple-notifications
prevent-force-push
format-on-save
# Category names: kebab-case
development-team
domain-experts
code-generation
Code Style
JavaScript/Node.js
General Guidelines
// Use const by default, let when reassignment needed
const config = loadConfig();
let counter = 0;
// Use arrow functions for callbacks
array.map(item => item.name);
array.filter(item => item.active);
// Use template literals
const message = `Hello, ${userName}!`;
const path = `${baseDir}/${fileName}`;
// Use async/await over promises
async function fetchData() {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch:', error);
throw error;
}
}
// Use destructuring
const { name, age } = user;
const [first, second] = array;
// Use spread operator
const newArray = [...oldArray, newItem];
const newObject = { ...oldObject, newKey: 'value' };
Error Handling
// Always use try/catch for async operations
async function processComponent(name) {
try {
const component = await fetchComponent(name);
const result = await installComponent(component);
return result;
} catch (error) {
// Log with context
console.error(`Failed to process component ${name}:`, error);
// Provide helpful error message
throw new Error(`Component installation failed: ${error.message}`);
}
}
// Validate input parameters
function installAgent(name) {
if (!name || typeof name !== 'string') {
throw new TypeError('Agent name must be a non-empty string');
}
if (name.includes('/')) {
throw new Error('Agent name cannot contain slashes');
}
// Proceed with installation
}
// Use fallback mechanisms
function getConfig() {
try {
return JSON.parse(fs.readFileSync('config.json', 'utf8'));
} catch (error) {
console.warn('Config file not found, using defaults');
return DEFAULT_CONFIG;
}
}
Async Operations
// Use Promise.all for parallel operations
const [agents, commands, hooks] = await Promise.all([
fetchAgents(),
fetchCommands(),
fetchHooks()
]);
// Use Promise.allSettled to handle failures gracefully
const results = await Promise.allSettled([
downloadAgent('agent1'),
downloadAgent('agent2'),
downloadAgent('agent3')
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Agent ${index} downloaded successfully`);
} else {
console.error(`Agent ${index} failed:`, result.reason);
}
});
Python
General Guidelines
# Use type hints
def process_component(name: str, category: str) -> dict:
"""Process a component and return metadata."""
return {'name': name, 'category': category}
# Use f-strings for formatting
message = f"Processing {component_name} in {category}"
path = f"{base_dir}/{file_name}"
# Use list comprehensions
names = [agent['name'] for agent in agents]
active_users = [u for u in users if u.is_active]
# Use context managers
with open('file.txt', 'r') as f:
content = f.read()
# Use pathlib for paths
from pathlib import Path
base_dir = Path('cli-tool/components')
agent_file = base_dir / 'agents' / 'development-team' / 'frontend-developer.md'
Error Handling
# Use specific exception types
try:
component = load_component(name)
except FileNotFoundError:
print(f"Component {name} not found")
return None
except json.JSONDecodeError as e:
print(f"Invalid JSON in component: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
raise
# Provide context in error messages
if not component_name:
raise ValueError("Component name cannot be empty")
if '/' in component_name:
raise ValueError(f"Invalid component name: {component_name}")
Documentation Standards
Code Comments
// Good comments explain WHY, not WHAT
// ❌ Bad comment - explains obvious WHAT
// Loop through agents
for (const agent of agents) { }
// ✅ Good comment - explains WHY
// Process agents in parallel batches to avoid rate limiting
for (const batch of agentBatches) {
await Promise.all(batch.map(processAgent));
}
// Document complex logic
/**
* Merges hook configurations from multiple sources.
* Priority: User config > Template config > Default config
*
* @param {Object} userHooks - User-defined hooks
* @param {Object} templateHooks - Template default hooks
* @returns {Object} Merged hook configuration
*/
function mergeHooks(userHooks, templateHooks) {
// Implementation
}
JSDoc Comments
/**
* Install a component to the user's project.
*
* @param {string} componentType - Type of component (agent, command, hook, etc.)
* @param {string} componentName - Name of the component to install
* @param {Object} options - Installation options
* @param {boolean} options.dryRun - If true, don't actually install
* @param {boolean} options.force - If true, overwrite existing files
* @returns {Promise<Object>} Installation result with status and files created
* @throws {Error} If component not found or installation fails
*/
async function installComponent(componentType, componentName, options = {}) {
// Implementation
}
Python Docstrings
def generate_component_catalog(component_dir: str, output_file: str) -> dict:
"""
Generate a JSON catalog of all components.
Scans the component directory for agents, commands, hooks, etc.
and generates a comprehensive JSON catalog with metadata and content.
Args:
component_dir: Path to the cli-tool/components directory
output_file: Path where the catalog JSON should be written
Returns:
Dictionary with component counts by type
Raises:
FileNotFoundError: If component directory doesn't exist
ValueError: If output path is invalid
"""
# Implementation
Testing Standards
Test Structure
// Use descriptive test names
describe('ComponentInstaller', () => {
describe('installAgent', () => {
it('should install agent to .claude/agents/ directory', async () => {
// Test implementation
});
it('should throw error if agent name is invalid', async () => {
// Test implementation
});
it('should not overwrite existing agent without force flag', async () => {
// Test implementation
});
});
});
// Follow AAA pattern: Arrange, Act, Assert
it('should merge hook configurations correctly', () => {
// Arrange
const userHooks = { PreToolUse: ['hook1'] };
const templateHooks = { PostToolUse: ['hook2'] };
// Act
const result = mergeHooks(userHooks, templateHooks);
// Assert
expect(result.PreToolUse).toEqual(['hook1']);
expect(result.PostToolUse).toEqual(['hook2']);
});
Test Coverage Goals
- Aim for 70%+ code coverage
- Test all critical paths
- Test error handling
- Test edge cases
- Test boundary conditions
Git Workflow
Commit Messages
# Use conventional commits format
feat: Add new security-auditor agent
fix: Correct path handling in Windows
docs: Update contributing guidelines
chore: Bump version to 1.28.17
refactor: Simplify hook installation logic
test: Add tests for component validation
# Provide context in commit body
git commit -m "feat: Add PostgreSQL MCP integration" -m "Adds Model Context Protocol server for PostgreSQL database access. Includes connection pooling and query execution support."
Branch Naming
feature/add-rust-template
fix/windows-path-handling
docs/update-testing-guide
chore/update-dependencies
refactor/simplify-installer
Performance Considerations
Caching
// Cache expensive operations
const componentCache = new Map();
async function getComponent(name) {
if (componentCache.has(name)) {
return componentCache.get(name);
}
const component = await fetchComponent(name);
componentCache.set(name, component);
return component;
}
// Clear cache when needed
function clearCache() {
componentCache.clear();
}
Async Optimization
// Parallelize independent operations
const [catalog, downloads, stats] = await Promise.all([
loadComponentCatalog(),
fetchDownloadStats(),
calculateStatistics()
]);
// Use streaming for large files
const stream = fs.createReadStream('large-file.json');
stream.pipe(parser).on('data', processChunk);
Next Steps
Component Guidelines
Best practices for creating components
Testing Workflow
Complete testing guide
Architecture
Project architecture overview
Publishing Workflow
Publishing to npm