memberstack-ai-docs
Version:
AI-friendly documentation installer for Memberstack DOM package
759 lines (630 loc) ⢠26.2 kB
JavaScript
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const chalk = require('chalk');
const GITHUB_BASE_URL = 'https://raw.githubusercontent.com/julianmemberstack/memberstack-ai-docs/main';
const MEMBERSTACK_DIR = '.memberstack';
const CLAUDE_MARKER_START = '<!-- MEMBERSTACK-AI-DOCS-START -->';
const CLAUDE_MARKER_END = '<!-- MEMBERSTACK-AI-DOCS-END -->';
const CURSOR_MARKER_START = '# MEMBERSTACK-AI-DOCS-START';
const CURSOR_MARKER_END = '# MEMBERSTACK-AI-DOCS-END';
// Codex (AGENTS.md) uses hidden HTML markers like CLAUDE
const CODEX_MARKER_START = '<!-- MEMBERSTACK-AI-DOCS-START -->';
const CODEX_MARKER_END = '<!-- MEMBERSTACK-AI-DOCS-END -->';
class MemberstackInstaller {
constructor(options = {}) {
this.options = options;
this.projectRoot = process.cwd();
this.memberstackDir = path.join(this.projectRoot, MEMBERSTACK_DIR);
}
async install(options = {}) {
try {
console.log(chalk.blue('š¦ Installing Memberstack AI Documentation...'));
// Get AI tools to install for (default to all)
const aiTools = options.aiTools || ['claude', 'cursor', 'codex'];
if (options.dryRun) {
console.log(chalk.yellow('š DRY RUN MODE - No files will be modified'));
}
// Step 1: Create .memberstack directory
await this.createMemberstackDirectory(options);
// Step 2: Download documentation files
await this.downloadDocumentation(options);
// Step 3: Update AI-specific files based on selection
if (aiTools.includes('claude')) {
await this.updateClaudeFile(options);
} else {
console.log(chalk.gray('ā Skipping CLAUDE.md (Claude Code not selected)'));
}
if (aiTools.includes('cursor')) {
await this.updateCursorRules(options);
} else {
console.log(chalk.gray('ā Skipping .cursorrules (Cursor not selected)'));
}
if (aiTools.includes('codex')) {
await this.updateCodexAgents(options);
} else {
console.log(chalk.gray('ā Skipping AGENTS.md (Codex not selected)'));
}
// Step 4: Validate installation
if (!options.dryRun) {
await this.validate({ ...options, aiTools });
}
console.log(chalk.green.bold('\nā
Memberstack AI Documentation installed successfully!'));
// Show what was installed
console.log(chalk.cyan('\nš Installed for:'));
if (aiTools.includes('claude')) {
console.log(chalk.white(' ā Claude Code (CLAUDE.md)'));
}
if (aiTools.includes('cursor')) {
console.log(chalk.white(' ā Cursor (.cursorrules)'));
}
if (aiTools.includes('codex')) {
console.log(chalk.white(' ā Codex (AGENTS.md)'));
}
console.log(chalk.cyan('\nš Documentation available:'));
console.log(chalk.white(' ⢠30 common methods (quick reference)'));
console.log(chalk.white(' ⢠49 total methods (searchable index)'));
console.log(chalk.white(' ⢠Complete documentation with examples'));
console.log(chalk.gray('\nš” Commands:'));
console.log(chalk.white(' npx memberstack-ai-docs --update # Update to latest'));
console.log(chalk.white(' npx memberstack-ai-docs --remove # Uninstall cleanly'));
console.log(chalk.white(' npx memberstack-ai-docs --validate # Check installation'));
} catch (error) {
console.error(chalk.red('ā Installation failed:'), error.message);
throw error;
}
}
async createMemberstackDirectory(options) {
if (options.dryRun) {
console.log(chalk.gray(' Would create .memberstack/ directory'));
return;
}
if (!fs.existsSync(this.memberstackDir)) {
fs.mkdirSync(this.memberstackDir, { recursive: true });
console.log(chalk.green('ā Created .memberstack/ directory'));
} else {
console.log(chalk.blue('ā .memberstack/ directory already exists'));
}
}
async downloadDocumentation(options) {
const files = [
{ name: 'complete.md', url: `${GITHUB_BASE_URL}/docs/memberstack-complete.md` },
{ name: 'index.json', url: `${GITHUB_BASE_URL}/docs/memberstack-index.json` },
{ name: 'quickref.md', url: `${GITHUB_BASE_URL}/docs/memberstack-quickref.md` }
];
for (const file of files) {
const filePath = path.join(this.memberstackDir, file.name);
if (options.dryRun) {
console.log(chalk.gray(` Would download ${file.name}`));
continue;
}
try {
if (this.options.verbose) {
console.log(chalk.gray(` Fetching ${file.url}...`));
}
const response = await fetch(file.url);
if (!response.ok) {
throw new Error(`Failed to fetch ${file.name}: ${response.statusText}`);
}
const content = await response.text();
fs.writeFileSync(filePath, content);
console.log(chalk.green(`ā Downloaded ${file.name}`));
} catch (error) {
// If download fails, use local files if available
const localPath = path.join(__dirname, '..', 'docs', file.name);
if (fs.existsSync(localPath)) {
fs.copyFileSync(localPath, filePath);
console.log(chalk.yellow(`ā Used local ${file.name}`));
} else {
throw new Error(`Failed to download ${file.name}: ${error.message}`);
}
}
}
}
async updateClaudeFile(options) {
const claudePath = path.join(this.projectRoot, 'CLAUDE.md');
const templatePath = path.join(__dirname, '..', 'templates', 'claude-template.md');
if (options.dryRun) {
if (fs.existsSync(claudePath)) {
console.log(chalk.gray(' Would update existing CLAUDE.md'));
} else {
console.log(chalk.gray(' Would create new CLAUDE.md'));
}
return;
}
// Read template
let template;
if (fs.existsSync(templatePath)) {
template = fs.readFileSync(templatePath, 'utf-8');
} else {
// Use embedded template
template = this.getClaudeTemplate();
}
if (fs.existsSync(claudePath)) {
// Update existing file
await this.appendToFile(claudePath, template, CLAUDE_MARKER_START, CLAUDE_MARKER_END, 'CLAUDE.md');
} else {
// Create new file
fs.writeFileSync(claudePath, template);
console.log(chalk.green('ā Created CLAUDE.md'));
}
}
async updateCursorRules(options) {
const cursorPath = path.join(this.projectRoot, '.cursorrules');
const templatePath = path.join(__dirname, '..', 'templates', 'cursor-template.md');
if (options.dryRun) {
if (fs.existsSync(cursorPath)) {
console.log(chalk.gray(' Would update existing .cursorrules'));
} else {
console.log(chalk.gray(' Would create new .cursorrules'));
}
return;
}
// Read template
let template;
if (fs.existsSync(templatePath)) {
template = fs.readFileSync(templatePath, 'utf-8');
} else {
// Use embedded template (same as Claude but with different markers)
template = this.getCursorTemplate();
}
if (fs.existsSync(cursorPath)) {
// Update existing file
await this.appendToFile(cursorPath, template, CURSOR_MARKER_START, CURSOR_MARKER_END, '.cursorrules');
} else {
// Create new file
fs.writeFileSync(cursorPath, template);
console.log(chalk.green('ā Created .cursorrules'));
}
}
async updateCodexAgents(options) {
const agentsPath = path.join(this.projectRoot, 'AGENTS.md');
const templatePath = path.join(__dirname, '..', 'templates', 'codex-template.md');
if (options.dryRun) {
if (fs.existsSync(agentsPath)) {
console.log(chalk.gray(' Would update existing AGENTS.md'));
} else {
console.log(chalk.gray(' Would create new AGENTS.md'));
}
return;
}
// Read template
let template;
if (fs.existsSync(templatePath)) {
template = fs.readFileSync(templatePath, 'utf-8');
} else {
// Fallback to embedded template
template = this.getCodexTemplate();
}
if (fs.existsSync(agentsPath)) {
// Update existing file
await this.appendToFile(agentsPath, template, CODEX_MARKER_START, CODEX_MARKER_END, 'AGENTS.md');
} else {
// Create new file
fs.writeFileSync(agentsPath, template);
console.log(chalk.green('ā Created AGENTS.md'));
}
}
async appendToFile(filePath, content, markerStart, markerEnd, fileName) {
const existingContent = fs.readFileSync(filePath, 'utf-8');
// Check if markers already exist
if (existingContent.includes(markerStart)) {
// Replace existing section
const startIndex = existingContent.indexOf(markerStart);
const endIndex = existingContent.indexOf(markerEnd) + markerEnd.length;
if (endIndex > startIndex) {
const newContent =
existingContent.substring(0, startIndex) +
content +
existingContent.substring(endIndex);
fs.writeFileSync(filePath, newContent);
console.log(chalk.green(`ā Updated ${fileName} (replaced existing Memberstack section)`));
} else {
// Markers corrupted, append new section
fs.appendFileSync(filePath, '\n\n' + content);
console.log(chalk.yellow(`ā Updated ${fileName} (appended new section, markers were corrupted)`));
}
} else {
// Append new section
fs.appendFileSync(filePath, '\n\n' + content);
console.log(chalk.green(`ā Updated ${fileName} (added Memberstack section)`));
}
}
async remove(options) {
console.log(chalk.blue('šļø Removing Memberstack AI Documentation...'));
if (options.dryRun) {
console.log(chalk.yellow('š DRY RUN MODE - No files will be modified'));
}
// Remove .memberstack directory
if (fs.existsSync(this.memberstackDir)) {
if (!options.dryRun) {
fs.rmSync(this.memberstackDir, { recursive: true, force: true });
}
console.log(chalk.green('ā Removed .memberstack/ directory'));
}
// Remove from CLAUDE.md
await this.removeFromFile('CLAUDE.md', CLAUDE_MARKER_START, CLAUDE_MARKER_END, options);
// Remove from .cursorrules
await this.removeFromFile('.cursorrules', CURSOR_MARKER_START, CURSOR_MARKER_END, options);
// Remove from AGENTS.md (Codex)
await this.removeFromFile('AGENTS.md', CODEX_MARKER_START, CODEX_MARKER_END, options);
console.log(chalk.green.bold('\nā
Memberstack AI Documentation removed successfully!'));
}
async removeFromFile(fileName, markerStart, markerEnd, options) {
const filePath = path.join(this.projectRoot, fileName);
if (!fs.existsSync(filePath)) {
return;
}
if (options.dryRun) {
console.log(chalk.gray(` Would remove Memberstack section from ${fileName}`));
return;
}
const content = fs.readFileSync(filePath, 'utf-8');
if (content.includes(markerStart)) {
const startIndex = content.indexOf(markerStart);
const endIndex = content.indexOf(markerEnd) + markerEnd.length;
if (endIndex > startIndex) {
const newContent =
content.substring(0, startIndex).trimEnd() +
content.substring(endIndex);
fs.writeFileSync(filePath, newContent);
console.log(chalk.green(`ā Removed Memberstack section from ${fileName}`));
}
}
}
async update(options) {
console.log(chalk.blue('š Updating Memberstack AI Documentation...'));
// Simply re-run install with force flag
await this.install({ ...options, force: true });
}
async validate(options) {
console.log(chalk.blue('\nš Validating Memberstack AI Documentation installation...'));
// Get which AI tools to validate (check what exists if not specified)
let aiTools = options.aiTools;
if (!aiTools) {
aiTools = [];
if (fs.existsSync(path.join(this.projectRoot, 'CLAUDE.md'))) {
aiTools.push('claude');
}
if (fs.existsSync(path.join(this.projectRoot, '.cursorrules'))) {
aiTools.push('cursor');
}
if (fs.existsSync(path.join(this.projectRoot, 'AGENTS.md'))) {
aiTools.push('codex');
}
if (aiTools.length === 0) {
aiTools = ['claude', 'cursor', 'codex']; // Check all if none exists
}
}
let isValid = true;
const checks = [];
// Check .memberstack directory
if (fs.existsSync(this.memberstackDir)) {
checks.push({ status: 'ā', message: '.memberstack/ directory exists' });
// Check files
const requiredFiles = ['complete.md', 'index.json', 'quickref.md'];
for (const file of requiredFiles) {
const filePath = path.join(this.memberstackDir, file);
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath);
checks.push({
status: 'ā',
message: `${file} (${(stats.size / 1024).toFixed(1)} KB)`
});
} else {
checks.push({ status: 'ā', message: `${file} missing` });
isValid = false;
}
}
} else {
checks.push({ status: 'ā', message: '.memberstack/ directory missing' });
isValid = false;
}
// Check CLAUDE.md if Claude was selected
if (aiTools.includes('claude')) {
const claudePath = path.join(this.projectRoot, 'CLAUDE.md');
if (fs.existsSync(claudePath)) {
const content = fs.readFileSync(claudePath, 'utf-8');
if (content.includes(CLAUDE_MARKER_START)) {
checks.push({ status: 'ā', message: 'CLAUDE.md contains Memberstack section' });
} else {
checks.push({ status: 'ā ', message: 'CLAUDE.md exists but missing Memberstack section' });
}
} else {
checks.push({ status: 'ā ', message: 'CLAUDE.md not found' });
}
}
// Check AGENTS.md if Codex was selected
if (aiTools.includes('codex')) {
const agentsPath = path.join(this.projectRoot, 'AGENTS.md');
if (fs.existsSync(agentsPath)) {
const content = fs.readFileSync(agentsPath, 'utf-8');
if (content.includes(CODEX_MARKER_START)) {
checks.push({ status: 'ā', message: 'AGENTS.md contains Memberstack section' });
} else {
checks.push({ status: 'ā ', message: 'AGENTS.md exists but missing Memberstack section' });
}
} else {
checks.push({ status: 'ā ', message: 'AGENTS.md not found' });
}
}
// Check .cursorrules if Cursor was selected
if (aiTools.includes('cursor')) {
const cursorPath = path.join(this.projectRoot, '.cursorrules');
if (fs.existsSync(cursorPath)) {
const content = fs.readFileSync(cursorPath, 'utf-8');
if (content.includes(CURSOR_MARKER_START)) {
checks.push({ status: 'ā', message: '.cursorrules contains Memberstack section' });
} else {
checks.push({ status: 'ā ', message: '.cursorrules exists but missing Memberstack section' });
}
} else {
checks.push({ status: 'ā ', message: '.cursorrules not found' });
}
}
// Display results
console.log('');
checks.forEach(check => {
const color = check.status === 'ā' ? 'green' : check.status === 'ā' ? 'red' : 'yellow';
console.log(chalk[color](` ${check.status} ${check.message}`));
});
if (isValid) {
console.log(chalk.green.bold('\nā
Installation is valid!'));
} else {
console.log(chalk.red.bold('\nā Installation has issues. Run with --update to fix.'));
}
return isValid;
}
getClaudeTemplate() {
return `${CLAUDE_MARKER_START}
# Memberstack DOM Package - AI Documentation
## Quick Start
\`\`\`javascript
import memberstack from '@memberstack/dom';
const ms = memberstack.init({ publicKey: 'pk_...' });
\`\`\`
## Most Common Methods
For complete implementation examples, see \`.memberstack/quickref.md\`
### Authentication
- \`loginMemberEmailPassword({ email, password })\` - Email/password login
- \`signupMemberEmailPassword({ email, password, customFields?, metaData?, plans? })\` - Create account
- \`logout()\` - Sign out current member
- \`getCurrentMember()\` - Get logged-in member data
- \`onAuthChange(callback)\` - Listen for auth state changes
- \`sendMemberResetPasswordEmail({ email })\` - Send password reset
- \`loginWithProvider({ provider })\` - Social login (google/facebook)
- \`sendMemberLoginPasswordlessEmail({ email })\` - Passwordless login
### Member Management
- \`updateMember({ customFields?, metaData? })\` - Update member data
- \`updateMemberAuth({ email?, oldPassword?, newPassword? })\` - Update credentials
- \`getMemberJSON()\` - Get member data as JSON
- \`updateMemberJSON(json)\` - Update member JSON data
- \`updateMemberProfileImage({ image })\` - Update profile picture
- \`deleteMember()\` - Delete member account
- \`sendMemberVerificationEmail()\` - Send email verification
### Plans & Billing
- \`getPlans()\` - Get all available plans
- \`getPlan({ planId })\` - Get specific plan details
- \`purchasePlansWithCheckout({ priceIds, successUrl?, cancelUrl? })\` - Stripe checkout
- \`launchStripeCustomerPortal({ returnUrl? })\` - Open billing portal
- \`addPlan({ planId })\` - Add free plan to member
- \`removePlan({ planId })\` - Remove plan from member
### UI Components
- \`openModal({ type: 'LOGIN' | 'SIGNUP' | 'PROFILE' })\` - Open pre-built modal
- \`hideModal()\` - Close current modal
## Finding All Methods (49 total)
1. **Search index**: \`.memberstack/index.json\` - Searchable method index
2. **Quick reference**: \`.memberstack/quickref.md\` - 30 common methods with examples
3. **Full reference**: \`.memberstack/complete.md\` - Complete documentation
### Search Examples
\`\`\`bash
# Find login methods
grep "login" .memberstack/index.json
# Find method signature
grep -A 5 "loginMemberEmailPassword" .memberstack/complete.md
# Find all authentication methods
grep '"category": "authentication"' .memberstack/index.json
\`\`\`
## AI Instructions
When implementing Memberstack features:
1. ALWAYS check \`.memberstack/index.json\` for available methods
2. Use exact method signatures from documentation
3. Include error handling in all examples using try/catch blocks
4. Reference \`.memberstack/complete.md\` for detailed parameters and return types
5. Check error codes and types in the documentation
## Error Handling Pattern
\`\`\`javascript
try {
const { data: member } = await memberstack.getCurrentMember();
// Handle success
} catch (error) {
if (error.code === 'INVALID_CREDENTIALS') {
// Handle invalid login
} else if (error.code === 'NETWORK_ERROR') {
// Handle network issues
} else {
// Handle other errors
console.error('Memberstack error:', error.message);
}
}
\`\`\`
## Common Patterns
### Check if logged in
\`\`\`javascript
const { data: member } = await memberstack.getCurrentMember();
if (member) {
// User is logged in
console.log('Welcome', member.auth.email);
} else {
// User is not logged in
await memberstack.openModal({ type: 'LOGIN' });
}
\`\`\`
### React/Vue auth listener
\`\`\`javascript
useEffect(() => {
const unsubscribe = memberstack.onAuthChange((member) => {
setCurrentMember(member);
});
return unsubscribe;
}, []);
\`\`\`
## Documentation Version: 2.0.0
Last Updated: 2025-01-11
Total Methods: 49
${CLAUDE_MARKER_END}`;
}
getCodexTemplate() {
return `${CODEX_MARKER_START}
# Memberstack DOM Package - AI Documentation (Codex)
This AGENTS.md section equips Codex with Memberstack's API context. The docs live in the `.memberstack/` directory for fast lookup.
## Quick Start
\`\`\`javascript
import memberstack from '@memberstack/dom';
const ms = memberstack.init({ publicKey: 'pk_...' });
\`\`\`
## Most Common Methods
For complete implementation examples, see \`.memberstack/quickref.md\`
### Authentication
- \`loginMemberEmailPassword({ email, password })\` - Email/password login
- \`signupMemberEmailPassword({ email, password, customFields?, metaData?, plans? })\` - Create account
- \`logout()\` - Sign out current member
- \`getCurrentMember()\` - Get logged-in member data
- \`onAuthChange(callback)\` - Listen for auth state changes
- \`sendMemberResetPasswordEmail({ email })\` - Send password reset
- \`loginWithProvider({ provider })\` - Social login (google/facebook)
- \`sendMemberLoginPasswordlessEmail({ email })\` - Passwordless login
### Member Management
- \`updateMember({ customFields?, metaData? })\` - Update member data
- \`updateMemberAuth({ email?, oldPassword?, newPassword? })\` - Update credentials
- \`getMemberJSON()\` - Get member data as JSON
- \`updateMemberJSON(json)\` - Update member JSON data
- \`updateMemberProfileImage({ image })\` - Update profile picture
- \`deleteMember()\` - Delete member account
- \`sendMemberVerificationEmail()\` - Send email verification
### Plans & Billing
- \`getPlans()\` - Get all available plans
- \`getPlan({ planId })\` - Get specific plan details
- \`purchasePlansWithCheckout({ priceIds, successUrl?, cancelUrl? })\` - Stripe checkout
- \`launchStripeCustomerPortal({ returnUrl? })\` - Open billing portal
- \`addPlan({ planId })\` - Add free plan to member
- \`removePlan({ planId })\` - Remove plan from member
### UI Components
- \`openModal({ type: 'LOGIN' | 'SIGNUP' | 'PROFILE' })\` - Open pre-built modal
- \`hideModal()\` - Close current modal
## Finding All Methods (49 total)
1. **Search index**: \`.memberstack/index.json\` - Searchable method index
2. **Quick reference**: \`.memberstack/quickref.md\` - 30 common methods with examples
3. **Full reference**: \`.memberstack/complete.md\` - Complete documentation
### Search Examples
\`\`\`bash
# Find login methods
grep "login" .memberstack/index.json
# Find method signature
grep -A 5 "loginMemberEmailPassword" .memberstack/complete.md
# Find all authentication methods
grep '"category": "authentication"' .memberstack/index.json
\`\`\`
## AI Instructions
When implementing Memberstack features:
1. ALWAYS check \`.memberstack/index.json\` for available methods
2. Use exact method signatures from documentation
3. Include error handling in all examples using try/catch blocks
4. Reference \`.memberstack/complete.md\` for detailed parameters and return types
5. Check error codes and types in the documentation
## Error Handling Pattern
\`\`\`javascript
try {
const { data: member } = await memberstack.getCurrentMember();
// Handle success
} catch (error) {
if (error.code === 'INVALID_CREDENTIALS') {
// Handle invalid login
} else if (error.code === 'NETWORK_ERROR') {
// Handle network issues
} else {
// Handle other errors
console.error('Memberstack error:', error.message);
}
}
\`\`\`
## Common Patterns
### Check if logged in
\`\`\`javascript
const { data: member } = await memberstack.getCurrentMember();
if (member) {
// User is logged in
console.log('Welcome', member.auth.email);
} else {
// User is not logged in
await memberstack.openModal({ type: 'LOGIN' });
}
\`\`\`
### React/Vue auth listener
\`\`\`javascript
useEffect(() => {
const unsubscribe = memberstack.onAuthChange((member) => {
setCurrentMember(member);
});
return unsubscribe;
}, []);
\`\`\`
## Documentation Version: 2.0.0
Last Updated: 2025-01-11
Total Methods: 49
${CODEX_MARKER_END}`;
}
getCursorTemplate() {
return `${CURSOR_MARKER_START}
# Memberstack DOM Package - AI Documentation
## Quick Start
\`\`\`javascript
import memberstack from '@memberstack/dom';
const ms = memberstack.init({ publicKey: 'pk_...' });
\`\`\`
## Most Common Methods
For complete implementation examples, see \`.memberstack/quickref.md\`
### Authentication
- \`loginMemberEmailPassword({ email, password })\` - Email/password login
- \`signupMemberEmailPassword({ email, password, customFields?, metaData?, plans? })\` - Create account
- \`logout()\` - Sign out current member
- \`getCurrentMember()\` - Get logged-in member data
- \`onAuthChange(callback)\` - Listen for auth state changes
- \`sendMemberResetPasswordEmail({ email })\` - Send password reset
- \`loginWithProvider({ provider })\` - Social login (google/facebook)
- \`sendMemberLoginPasswordlessEmail({ email })\` - Passwordless login
### Member Management
- \`updateMember({ customFields?, metaData? })\` - Update member data
- \`updateMemberAuth({ email?, oldPassword?, newPassword? })\` - Update credentials
- \`getMemberJSON()\` - Get member data as JSON
- \`updateMemberJSON(json)\` - Update member JSON data
- \`updateMemberProfileImage({ image })\` - Update profile picture
- \`deleteMember()\` - Delete member account
- \`sendMemberVerificationEmail()\` - Send email verification
### Plans & Billing
- \`getPlans()\` - Get all available plans
- \`getPlan({ planId })\` - Get specific plan details
- \`purchasePlansWithCheckout({ priceIds, successUrl?, cancelUrl? })\` - Stripe checkout
- \`launchStripeCustomerPortal({ returnUrl? })\` - Open billing portal
- \`addPlan({ planId })\` - Add free plan to member
- \`removePlan({ planId })\` - Remove plan from member
### UI Components
- \`openModal({ type: 'LOGIN' | 'SIGNUP' | 'PROFILE' })\` - Open pre-built modal
- \`hideModal()\` - Close current modal
## Finding All Methods (49 total)
1. **Search index**: \`.memberstack/index.json\` - Searchable method index
2. **Quick reference**: \`.memberstack/quickref.md\` - 30 common methods with examples
3. **Full reference**: \`.memberstack/complete.md\` - Complete documentation
## AI Instructions
When implementing Memberstack features:
1. ALWAYS check \`.memberstack/index.json\` for available methods
2. Use exact method signatures from documentation
3. Include error handling in all examples using try/catch blocks
4. Reference \`.memberstack/complete.md\` for detailed parameters and return types
## Documentation Version: 2.0.0
Last Updated: 2025-01-11
Total Methods: 49
${CURSOR_MARKER_END}`;
}
}
module.exports = new MemberstackInstaller();