claude-flow-novice 2.3.7 ā 2.3.9
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/package.json +3 -2
- package/scripts/categorize-agents.js +255 -0
- package/scripts/subcategorize-agents.js +427 -0
- package/scripts/sync-from-package.js +180 -0
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow-novice",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.9",
|
|
4
4
|
"description": "AI Agent Orchestration CLI",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
7
|
-
"claude-flow-novice": "./src/cli/index.ts"
|
|
7
|
+
"claude-flow-novice": "./src/cli/index.ts",
|
|
8
|
+
"claude-flow-sync": "./scripts/sync-from-package.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"src",
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Categorize imported agents into subdirectories
|
|
5
|
+
* Analyzes YAML frontmatter and filenames to determine appropriate category
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const AGENTS_DIR = path.join(__dirname, '../agents');
|
|
12
|
+
|
|
13
|
+
// Category definitions with keyword patterns
|
|
14
|
+
const CATEGORIES = {
|
|
15
|
+
'development-engineering': {
|
|
16
|
+
keywords: ['backend', 'frontend', 'api', 'rest', 'graphql', 'react', 'angular', 'vue', 'typescript',
|
|
17
|
+
'javascript', 'python', 'java', 'rust', 'go', 'node', 'developer', 'engineer', 'code',
|
|
18
|
+
'programming', 'devops', 'kubernetes', 'docker', 'ci/cd', 'mobile', 'ios', 'android',
|
|
19
|
+
'web', 'fullstack', 'microservices', 'architecture-analyst', 'database', 'sql', 'nosql',
|
|
20
|
+
'git', 'version-control', 'deployment', 'container', 'orchestration', 'testing-specialist',
|
|
21
|
+
'acceptance-test', 'integration', 'unit-test', 'qa', 'quality-assurance', 'debugging'],
|
|
22
|
+
description: 'Backend, frontend, mobile, DevOps, API development, testing'
|
|
23
|
+
},
|
|
24
|
+
'ai-ml-automation': {
|
|
25
|
+
keywords: ['machine-learning', 'deep-learning', 'neural', 'tensorflow', 'pytorch', 'ai', 'ml',
|
|
26
|
+
'model', 'training', 'inference', 'nlp', 'computer-vision', 'data-science', 'mlops',
|
|
27
|
+
'automation', 'intelligent', 'prediction-engine', 'anomaly-detection', 'algorithm',
|
|
28
|
+
'optimization', 'reinforcement-learning', 'supervised', 'unsupervised'],
|
|
29
|
+
description: 'Machine learning, deep learning, neural networks, MLOps, automation'
|
|
30
|
+
},
|
|
31
|
+
'business-operations': {
|
|
32
|
+
keywords: ['business', 'strategy', 'growth', 'revenue', 'sales', 'marketing', 'customer',
|
|
33
|
+
'operations', 'management', 'planning', 'leadership', 'executive', 'roi', 'kpi',
|
|
34
|
+
'metrics', 'analytics-insights', 'competitive', 'market', 'venture', 'startup',
|
|
35
|
+
'scaling', 'transformation', 'change-management', 'stakeholder', 'organizational'],
|
|
36
|
+
description: 'Business strategy, growth, revenue, customer experience, operations'
|
|
37
|
+
},
|
|
38
|
+
'security-compliance': {
|
|
39
|
+
keywords: ['security', 'cybersecurity', 'compliance', 'audit', 'gdpr', 'privacy', 'threat',
|
|
40
|
+
'vulnerability', 'penetration', 'encryption', 'authentication', 'authorization',
|
|
41
|
+
'zero-trust', 'firewall', 'monitoring', 'incident', 'forensic', 'risk', 'pci',
|
|
42
|
+
'hipaa', 'sox', 'iso', 'regulatory', 'data-privacy', 'infosec'],
|
|
43
|
+
description: 'Cybersecurity, compliance, privacy, threat analysis, auditing'
|
|
44
|
+
},
|
|
45
|
+
'data-analytics': {
|
|
46
|
+
keywords: ['data', 'analytics', 'bi', 'business-intelligence', 'etl', 'elt', 'pipeline',
|
|
47
|
+
'warehouse', 'lake', 'visualization', 'dashboard', 'reporting', 'insights',
|
|
48
|
+
'forecasting', 'time-series', 'statistical', 'metrics', 'kpi-dashboard',
|
|
49
|
+
'real-time-analytics', 'stream-processing', 'batch-processing'],
|
|
50
|
+
description: 'Business intelligence, analytics, ETL, forecasting, data engineering'
|
|
51
|
+
},
|
|
52
|
+
'personal-professional': {
|
|
53
|
+
keywords: ['career', 'personal', 'professional', 'development', 'coaching', 'mentoring',
|
|
54
|
+
'leadership-development', 'emotional-intelligence', 'productivity', 'wellness',
|
|
55
|
+
'work-life', 'communication', 'networking', 'resume', 'interview', 'skill',
|
|
56
|
+
'learning', 'education', 'training', 'mindfulness', 'motivation', 'goal-setting',
|
|
57
|
+
'time-management', 'stress', 'wellness', 'active-listening'],
|
|
58
|
+
description: 'Career, leadership, emotional intelligence, productivity, personal growth'
|
|
59
|
+
},
|
|
60
|
+
'payment-financial': {
|
|
61
|
+
keywords: ['payment', 'stripe', 'paypal', 'square', 'braintree', 'checkout', 'transaction',
|
|
62
|
+
'financial', 'billing', 'invoice', 'subscription', 'bnpl', 'afterpay', 'klarna',
|
|
63
|
+
'affirm', 'gateway', 'merchant', 'pos', 'e-commerce-payment', 'apple-pay', 'google-pay',
|
|
64
|
+
'alipay', 'wechat-pay', 'cryptocurrency', 'blockchain-payment'],
|
|
65
|
+
description: 'Payment gateways, BNPL, financial transactions, billing systems'
|
|
66
|
+
},
|
|
67
|
+
'industry-specific': {
|
|
68
|
+
keywords: ['healthcare', 'medical', 'clinical', 'patient', 'diagnosis', 'telemedicine',
|
|
69
|
+
'finance-sector', 'banking', 'insurance', 'legal', 'law', 'compliance-legal',
|
|
70
|
+
'education', 'e-learning', 'academic', 'retail', 'manufacturing', 'logistics',
|
|
71
|
+
'supply-chain', 'real-estate', 'hospitality', 'tourism', 'energy', 'utilities',
|
|
72
|
+
'telecommunications', 'media', 'entertainment', 'sports', 'gaming'],
|
|
73
|
+
description: 'Healthcare, finance, legal, education, retail, manufacturing, etc.'
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Files to keep in root
|
|
78
|
+
const META_FILES = ['CLAUDE.md', 'README.md', 'IMPORTED_AGENTS_README.md', 'MIGRATION_SUMMARY.md', '.gitkeep'];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extract YAML frontmatter from markdown file
|
|
82
|
+
*/
|
|
83
|
+
function extractFrontmatter(content) {
|
|
84
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
85
|
+
if (!match) return null;
|
|
86
|
+
|
|
87
|
+
const frontmatter = {};
|
|
88
|
+
const lines = match[1].split('\n');
|
|
89
|
+
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const colonIndex = line.indexOf(':');
|
|
92
|
+
if (colonIndex === -1) continue;
|
|
93
|
+
|
|
94
|
+
const key = line.substring(0, colonIndex).trim();
|
|
95
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
96
|
+
frontmatter[key] = value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return frontmatter;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Categorize agent based on name and description
|
|
104
|
+
*/
|
|
105
|
+
function categorizeAgent(filename, content) {
|
|
106
|
+
const frontmatter = extractFrontmatter(content);
|
|
107
|
+
const searchText = (filename + ' ' + (frontmatter?.name || '') + ' ' + (frontmatter?.description || '')).toLowerCase();
|
|
108
|
+
|
|
109
|
+
let bestCategory = null;
|
|
110
|
+
let bestScore = 0;
|
|
111
|
+
|
|
112
|
+
for (const [category, config] of Object.entries(CATEGORIES)) {
|
|
113
|
+
let score = 0;
|
|
114
|
+
for (const keyword of config.keywords) {
|
|
115
|
+
if (searchText.includes(keyword.toLowerCase())) {
|
|
116
|
+
score++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (score > bestScore) {
|
|
121
|
+
bestScore = score;
|
|
122
|
+
bestCategory = category;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Default to industry-specific if no clear match
|
|
127
|
+
return bestCategory || 'industry-specific';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Main categorization process
|
|
132
|
+
*/
|
|
133
|
+
async function categorizeAgents() {
|
|
134
|
+
console.log('š Analyzing agent files...\n');
|
|
135
|
+
|
|
136
|
+
// Read all markdown files
|
|
137
|
+
const files = fs.readdirSync(AGENTS_DIR)
|
|
138
|
+
.filter(f => f.endsWith('.md') && !META_FILES.includes(f));
|
|
139
|
+
|
|
140
|
+
console.log(`Found ${files.length} agent files to categorize\n`);
|
|
141
|
+
|
|
142
|
+
// Categorize each file
|
|
143
|
+
const categorization = {};
|
|
144
|
+
for (const category of Object.keys(CATEGORIES)) {
|
|
145
|
+
categorization[category] = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
const filePath = path.join(AGENTS_DIR, file);
|
|
150
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
151
|
+
const category = categorizeAgent(file, content);
|
|
152
|
+
categorization[category].push(file);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Display categorization summary
|
|
156
|
+
console.log('š Categorization Summary:\n');
|
|
157
|
+
for (const [category, files] of Object.entries(categorization)) {
|
|
158
|
+
console.log(`${category}: ${files.length} agents`);
|
|
159
|
+
}
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
// Create directories and move files
|
|
163
|
+
console.log('š Creating category directories...\n');
|
|
164
|
+
|
|
165
|
+
for (const [category, config] of Object.entries(CATEGORIES)) {
|
|
166
|
+
const categoryDir = path.join(AGENTS_DIR, category);
|
|
167
|
+
|
|
168
|
+
// Create directory
|
|
169
|
+
if (!fs.existsSync(categoryDir)) {
|
|
170
|
+
fs.mkdirSync(categoryDir, { recursive: true });
|
|
171
|
+
console.log(`ā Created ${category}/`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Move files
|
|
175
|
+
const filesToMove = categorization[category];
|
|
176
|
+
for (const file of filesToMove) {
|
|
177
|
+
const src = path.join(AGENTS_DIR, file);
|
|
178
|
+
const dest = path.join(categoryDir, file);
|
|
179
|
+
fs.renameSync(src, dest);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Create index file
|
|
183
|
+
const indexContent = `# ${category.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
184
|
+
|
|
185
|
+
**Description:** ${config.description}
|
|
186
|
+
**Agent Count:** ${filesToMove.length}
|
|
187
|
+
|
|
188
|
+
## Available Agents
|
|
189
|
+
|
|
190
|
+
${filesToMove.sort().map(f => `- [${f.replace('.md', '')}](./${f})`).join('\n')}
|
|
191
|
+
`;
|
|
192
|
+
|
|
193
|
+
fs.writeFileSync(path.join(categoryDir, 'INDEX.md'), indexContent);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
console.log('\nā
Categorization complete!\n');
|
|
197
|
+
|
|
198
|
+
// Generate summary report
|
|
199
|
+
const summaryContent = `# Agent Categorization Summary
|
|
200
|
+
|
|
201
|
+
**Total Agents:** ${files.length}
|
|
202
|
+
**Categories:** ${Object.keys(CATEGORIES).length}
|
|
203
|
+
**Generated:** ${new Date().toISOString()}
|
|
204
|
+
|
|
205
|
+
## Category Breakdown
|
|
206
|
+
|
|
207
|
+
${Object.entries(categorization).map(([cat, files]) => {
|
|
208
|
+
const config = CATEGORIES[cat];
|
|
209
|
+
return `### ${cat.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')} (${files.length} agents)
|
|
210
|
+
|
|
211
|
+
**Description:** ${config.description}
|
|
212
|
+
**Location:** \`agents/${cat}/\`
|
|
213
|
+
|
|
214
|
+
<details>
|
|
215
|
+
<summary>View agents in this category</summary>
|
|
216
|
+
|
|
217
|
+
${files.sort().map(f => `- ${f.replace('.md', '')}`).join('\n')}
|
|
218
|
+
|
|
219
|
+
</details>
|
|
220
|
+
`;
|
|
221
|
+
}).join('\n')}
|
|
222
|
+
|
|
223
|
+
## Quick Navigation
|
|
224
|
+
|
|
225
|
+
${Object.entries(categorization).map(([cat, files]) =>
|
|
226
|
+
`- [${cat}](${cat}/INDEX.md) - ${files.length} agents`
|
|
227
|
+
).join('\n')}
|
|
228
|
+
|
|
229
|
+
## Usage
|
|
230
|
+
|
|
231
|
+
\`\`\`bash
|
|
232
|
+
# List agents in a category
|
|
233
|
+
ls agents/development-engineering/
|
|
234
|
+
|
|
235
|
+
# Find specific agent
|
|
236
|
+
find agents -name "*react*"
|
|
237
|
+
|
|
238
|
+
# Search across all agents
|
|
239
|
+
grep -r "keyword" agents/
|
|
240
|
+
\`\`\`
|
|
241
|
+
`;
|
|
242
|
+
|
|
243
|
+
fs.writeFileSync(path.join(AGENTS_DIR, 'CATEGORIZATION_SUMMARY.md'), summaryContent);
|
|
244
|
+
console.log('š Generated CATEGORIZATION_SUMMARY.md\n');
|
|
245
|
+
|
|
246
|
+
// Final statistics
|
|
247
|
+
console.log('š Final Statistics:\n');
|
|
248
|
+
console.log(`Total agents categorized: ${files.length}`);
|
|
249
|
+
console.log(`Categories created: ${Object.keys(CATEGORIES).length}`);
|
|
250
|
+
console.log(`Meta files preserved: ${META_FILES.filter(f => fs.existsSync(path.join(AGENTS_DIR, f))).length}`);
|
|
251
|
+
console.log('\n⨠Done!\n');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Run
|
|
255
|
+
categorizeAgents().catch(console.error);
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create subcategories within each main agent category
|
|
5
|
+
* Provides finer-grained organization for easier browsing
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const AGENTS_DIR = path.join(__dirname, '../agents');
|
|
12
|
+
|
|
13
|
+
// Subcategory definitions for each main category
|
|
14
|
+
const SUBCATEGORIES = {
|
|
15
|
+
'development-engineering': {
|
|
16
|
+
'backend': {
|
|
17
|
+
keywords: ['backend', 'api', 'rest', 'graphql', 'server', 'node', 'django', 'flask', 'express', 'fastapi'],
|
|
18
|
+
description: 'Backend development, APIs, server-side programming'
|
|
19
|
+
},
|
|
20
|
+
'frontend': {
|
|
21
|
+
keywords: ['frontend', 'react', 'angular', 'vue', 'ui', 'javascript', 'typescript', 'css', 'html', 'web-ui'],
|
|
22
|
+
description: 'Frontend frameworks, UI development, client-side'
|
|
23
|
+
},
|
|
24
|
+
'mobile': {
|
|
25
|
+
keywords: ['mobile', 'ios', 'android', 'react-native', 'flutter', 'swift', 'kotlin'],
|
|
26
|
+
description: 'Mobile app development (iOS, Android)'
|
|
27
|
+
},
|
|
28
|
+
'devops': {
|
|
29
|
+
keywords: ['devops', 'docker', 'kubernetes', 'ci-cd', 'cicd', 'deployment', 'container', 'gitops', 'argocd', 'flux', 'jenkins'],
|
|
30
|
+
description: 'DevOps, CI/CD, containerization, orchestration'
|
|
31
|
+
},
|
|
32
|
+
'testing': {
|
|
33
|
+
keywords: ['test', 'qa', 'quality', 'acceptance', 'cypress', 'jest', 'playwright', 'e2e', 'integration-test', 'unit-test', 'fuzz'],
|
|
34
|
+
description: 'Testing, QA, quality assurance, test automation'
|
|
35
|
+
},
|
|
36
|
+
'database': {
|
|
37
|
+
keywords: ['database', 'sql', 'nosql', 'postgres', 'mysql', 'mongodb', 'redis', 'data-model'],
|
|
38
|
+
description: 'Database design, SQL, NoSQL, data modeling'
|
|
39
|
+
},
|
|
40
|
+
'architecture': {
|
|
41
|
+
keywords: ['architect', 'architecture', 'design-pattern', 'microservices', 'system-design', 'scalability'],
|
|
42
|
+
description: 'Software architecture, system design, patterns'
|
|
43
|
+
},
|
|
44
|
+
'integration': {
|
|
45
|
+
keywords: ['integration', 'api-integration', 'third-party', 'webhook', 'payment-integration', 'oauth'],
|
|
46
|
+
description: 'API integration, third-party services, webhooks'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
'industry-specific': {
|
|
51
|
+
'healthcare': {
|
|
52
|
+
keywords: ['health', 'medical', 'clinical', 'patient', 'doctor', 'hospital', 'telemedicine', 'pharmaceutical'],
|
|
53
|
+
description: 'Healthcare, medical, clinical systems'
|
|
54
|
+
},
|
|
55
|
+
'finance': {
|
|
56
|
+
keywords: ['finance', 'banking', 'fintech', 'trading', 'investment', 'accounting', 'ledger', 'financial'],
|
|
57
|
+
description: 'Finance, banking, fintech, trading'
|
|
58
|
+
},
|
|
59
|
+
'legal': {
|
|
60
|
+
keywords: ['legal', 'law', 'attorney', 'court', 'litigation', 'contract', 'compliance-legal'],
|
|
61
|
+
description: 'Legal services, law firms, compliance'
|
|
62
|
+
},
|
|
63
|
+
'education': {
|
|
64
|
+
keywords: ['education', 'learning', 'student', 'teacher', 'academic', 'e-learning', 'training', 'course'],
|
|
65
|
+
description: 'Education, e-learning, academic systems'
|
|
66
|
+
},
|
|
67
|
+
'retail': {
|
|
68
|
+
keywords: ['retail', 'e-commerce', 'shop', 'store', 'pos', 'inventory', 'product', 'catalog'],
|
|
69
|
+
description: 'Retail, e-commerce, point-of-sale'
|
|
70
|
+
},
|
|
71
|
+
'manufacturing': {
|
|
72
|
+
keywords: ['manufactur', 'production', 'factory', 'supply-chain', 'logistics', 'warehouse', 'industrial'],
|
|
73
|
+
description: 'Manufacturing, production, supply chain'
|
|
74
|
+
},
|
|
75
|
+
'entertainment': {
|
|
76
|
+
keywords: ['entertainment', 'media', 'gaming', 'sports', 'music', 'video', 'streaming', 'content'],
|
|
77
|
+
description: 'Entertainment, media, gaming, sports'
|
|
78
|
+
},
|
|
79
|
+
'other': {
|
|
80
|
+
keywords: ['real-estate', 'hospitality', 'tourism', 'energy', 'utilities', 'telecom', 'insurance', 'nonprofit'],
|
|
81
|
+
description: 'Other industries (real estate, hospitality, etc.)'
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
'ai-ml-automation': {
|
|
86
|
+
'machine-learning': {
|
|
87
|
+
keywords: ['machine-learning', 'ml', 'supervised', 'unsupervised', 'classification', 'regression', 'scikit'],
|
|
88
|
+
description: 'Traditional machine learning, classification, regression'
|
|
89
|
+
},
|
|
90
|
+
'deep-learning': {
|
|
91
|
+
keywords: ['deep-learning', 'neural', 'tensorflow', 'pytorch', 'keras', 'cnn', 'rnn', 'transformer'],
|
|
92
|
+
description: 'Deep learning, neural networks, frameworks'
|
|
93
|
+
},
|
|
94
|
+
'nlp': {
|
|
95
|
+
keywords: ['nlp', 'natural-language', 'text', 'sentiment', 'chatbot', 'language-model', 'tokeniz'],
|
|
96
|
+
description: 'Natural language processing, text analysis'
|
|
97
|
+
},
|
|
98
|
+
'computer-vision': {
|
|
99
|
+
keywords: ['vision', 'image', 'video', 'object-detection', 'facial', 'ocr', 'recognition'],
|
|
100
|
+
description: 'Computer vision, image processing, object detection'
|
|
101
|
+
},
|
|
102
|
+
'mlops': {
|
|
103
|
+
keywords: ['mlops', 'model-deployment', 'training-pipeline', 'experiment-tracking', 'model-serving'],
|
|
104
|
+
description: 'MLOps, model deployment, training pipelines'
|
|
105
|
+
},
|
|
106
|
+
'automation': {
|
|
107
|
+
keywords: ['automation', 'intelligent-automation', 'workflow', 'orchestration', 'agent-based'],
|
|
108
|
+
description: 'Intelligent automation, workflow automation'
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
'business-operations': {
|
|
113
|
+
'strategy': {
|
|
114
|
+
keywords: ['strategy', 'strategic', 'planning', 'competitive', 'positioning', 'vision'],
|
|
115
|
+
description: 'Business strategy, strategic planning'
|
|
116
|
+
},
|
|
117
|
+
'growth': {
|
|
118
|
+
keywords: ['growth', 'scaling', 'expansion', 'market-expansion', 'growth-hacking'],
|
|
119
|
+
description: 'Business growth, scaling, expansion'
|
|
120
|
+
},
|
|
121
|
+
'revenue': {
|
|
122
|
+
keywords: ['revenue', 'sales', 'monetization', 'pricing', 'billing', 'subscription'],
|
|
123
|
+
description: 'Revenue optimization, sales, monetization'
|
|
124
|
+
},
|
|
125
|
+
'customer': {
|
|
126
|
+
keywords: ['customer', 'client', 'user-experience', 'crm', 'support', 'journey', 'satisfaction'],
|
|
127
|
+
description: 'Customer experience, CRM, support'
|
|
128
|
+
},
|
|
129
|
+
'operations': {
|
|
130
|
+
keywords: ['operations', 'operational', 'process', 'workflow', 'efficiency', 'optimization'],
|
|
131
|
+
description: 'Operations management, process optimization'
|
|
132
|
+
},
|
|
133
|
+
'leadership': {
|
|
134
|
+
keywords: ['leadership', 'management', 'executive', 'team-building', 'organizational'],
|
|
135
|
+
description: 'Leadership, management, organizational development'
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
'data-analytics': {
|
|
140
|
+
'business-intelligence': {
|
|
141
|
+
keywords: ['business-intelligence', 'bi', 'dashboard', 'reporting', 'kpi', 'metrics'],
|
|
142
|
+
description: 'Business intelligence, dashboards, reporting'
|
|
143
|
+
},
|
|
144
|
+
'data-engineering': {
|
|
145
|
+
keywords: ['etl', 'elt', 'pipeline', 'data-pipeline', 'warehouse', 'lake', 'ingestion'],
|
|
146
|
+
description: 'Data engineering, ETL/ELT, data pipelines'
|
|
147
|
+
},
|
|
148
|
+
'analytics': {
|
|
149
|
+
keywords: ['analytics', 'analysis', 'insights', 'statistical', 'predictive', 'prescriptive'],
|
|
150
|
+
description: 'Data analytics, statistical analysis, insights'
|
|
151
|
+
},
|
|
152
|
+
'forecasting': {
|
|
153
|
+
keywords: ['forecast', 'prediction', 'time-series', 'trend', 'anomaly-detection'],
|
|
154
|
+
description: 'Forecasting, time-series analysis, predictions'
|
|
155
|
+
},
|
|
156
|
+
'visualization': {
|
|
157
|
+
keywords: ['visualization', 'chart', 'graph', 'tableau', 'power-bi', 'data-viz'],
|
|
158
|
+
description: 'Data visualization, charts, dashboards'
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
'personal-professional': {
|
|
163
|
+
'career': {
|
|
164
|
+
keywords: ['career', 'job', 'interview', 'resume', 'job-search', 'promotion', 'transition'],
|
|
165
|
+
description: 'Career development, job search, interviews'
|
|
166
|
+
},
|
|
167
|
+
'leadership': {
|
|
168
|
+
keywords: ['leadership', 'leader', 'management', 'executive', 'mentoring', 'coaching'],
|
|
169
|
+
description: 'Leadership development, coaching, mentoring'
|
|
170
|
+
},
|
|
171
|
+
'productivity': {
|
|
172
|
+
keywords: ['productivity', 'time-management', 'efficiency', 'goal', 'planning', 'organization'],
|
|
173
|
+
description: 'Productivity, time management, goal setting'
|
|
174
|
+
},
|
|
175
|
+
'communication': {
|
|
176
|
+
keywords: ['communication', 'presentation', 'public-speaking', 'writing', 'listening', 'feedback'],
|
|
177
|
+
description: 'Communication skills, presentations, feedback'
|
|
178
|
+
},
|
|
179
|
+
'wellness': {
|
|
180
|
+
keywords: ['wellness', 'stress', 'work-life', 'balance', 'mindfulness', 'mental-health', 'emotional'],
|
|
181
|
+
description: 'Wellness, work-life balance, mental health'
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
'security-compliance': {
|
|
186
|
+
'cybersecurity': {
|
|
187
|
+
keywords: ['cybersecurity', 'security', 'threat', 'vulnerability', 'penetration', 'firewall', 'encryption'],
|
|
188
|
+
description: 'Cybersecurity, threat analysis, penetration testing'
|
|
189
|
+
},
|
|
190
|
+
'compliance': {
|
|
191
|
+
keywords: ['compliance', 'regulatory', 'audit', 'gdpr', 'hipaa', 'sox', 'pci', 'iso'],
|
|
192
|
+
description: 'Compliance, regulatory requirements, auditing'
|
|
193
|
+
},
|
|
194
|
+
'privacy': {
|
|
195
|
+
keywords: ['privacy', 'data-privacy', 'gdpr', 'personal-data', 'consent', 'anonymization'],
|
|
196
|
+
description: 'Data privacy, GDPR, personal data protection'
|
|
197
|
+
},
|
|
198
|
+
'access-control': {
|
|
199
|
+
keywords: ['access-control', 'authentication', 'authorization', 'identity', 'zero-trust', 'iam'],
|
|
200
|
+
description: 'Access control, authentication, identity management'
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
'payment-financial': {
|
|
205
|
+
'payment-gateways': {
|
|
206
|
+
keywords: ['stripe', 'paypal', 'square', 'braintree', 'checkout', 'payment-gateway', 'merchant'],
|
|
207
|
+
description: 'Payment gateways (Stripe, PayPal, Square, etc.)'
|
|
208
|
+
},
|
|
209
|
+
'bnpl': {
|
|
210
|
+
keywords: ['bnpl', 'afterpay', 'klarna', 'affirm', 'installment', 'buy-now-pay-later'],
|
|
211
|
+
description: 'Buy-now-pay-later, installment payments'
|
|
212
|
+
},
|
|
213
|
+
'cryptocurrency': {
|
|
214
|
+
keywords: ['crypto', 'bitcoin', 'ethereum', 'blockchain', 'wallet', 'web3'],
|
|
215
|
+
description: 'Cryptocurrency, blockchain, Web3 payments'
|
|
216
|
+
},
|
|
217
|
+
'billing': {
|
|
218
|
+
keywords: ['billing', 'invoice', 'subscription', 'recurring', 'pricing'],
|
|
219
|
+
description: 'Billing systems, invoicing, subscriptions'
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Extract YAML frontmatter from markdown file
|
|
226
|
+
*/
|
|
227
|
+
function extractFrontmatter(content) {
|
|
228
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
229
|
+
if (!match) return null;
|
|
230
|
+
|
|
231
|
+
const frontmatter = {};
|
|
232
|
+
const lines = match[1].split('\n');
|
|
233
|
+
|
|
234
|
+
for (const line of lines) {
|
|
235
|
+
const colonIndex = line.indexOf(':');
|
|
236
|
+
if (colonIndex === -1) continue;
|
|
237
|
+
|
|
238
|
+
const key = line.substring(0, colonIndex).trim();
|
|
239
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
240
|
+
frontmatter[key] = value;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return frontmatter;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Categorize agent into subcategory
|
|
248
|
+
*/
|
|
249
|
+
function subcategorizeAgent(filename, content, mainCategory) {
|
|
250
|
+
const subcategories = SUBCATEGORIES[mainCategory];
|
|
251
|
+
if (!subcategories) return null;
|
|
252
|
+
|
|
253
|
+
const frontmatter = extractFrontmatter(content);
|
|
254
|
+
const searchText = (filename + ' ' + (frontmatter?.name || '') + ' ' + (frontmatter?.description || '')).toLowerCase();
|
|
255
|
+
|
|
256
|
+
let bestSubcategory = null;
|
|
257
|
+
let bestScore = 0;
|
|
258
|
+
|
|
259
|
+
for (const [subcategory, config] of Object.entries(subcategories)) {
|
|
260
|
+
let score = 0;
|
|
261
|
+
for (const keyword of config.keywords) {
|
|
262
|
+
if (searchText.includes(keyword.toLowerCase())) {
|
|
263
|
+
score++;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (score > bestScore) {
|
|
268
|
+
bestScore = score;
|
|
269
|
+
bestSubcategory = subcategory;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Default to first subcategory or 'other' if no match
|
|
274
|
+
if (!bestSubcategory) {
|
|
275
|
+
bestSubcategory = subcategories.other ? 'other' : Object.keys(subcategories)[0];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return bestSubcategory;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Process a single main category
|
|
283
|
+
*/
|
|
284
|
+
function processCategory(mainCategory) {
|
|
285
|
+
const categoryDir = path.join(AGENTS_DIR, mainCategory);
|
|
286
|
+
if (!fs.existsSync(categoryDir)) {
|
|
287
|
+
console.log(`āļø Skipping ${mainCategory} (not found)`);
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
console.log(`\nš Processing ${mainCategory}...`);
|
|
292
|
+
|
|
293
|
+
// Read agent files
|
|
294
|
+
const files = fs.readdirSync(categoryDir)
|
|
295
|
+
.filter(f => f.endsWith('.md') && f !== 'INDEX.md');
|
|
296
|
+
|
|
297
|
+
if (files.length === 0) {
|
|
298
|
+
console.log(` No agents found in ${mainCategory}`);
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log(` Found ${files.length} agents`);
|
|
303
|
+
|
|
304
|
+
// Categorize into subcategories
|
|
305
|
+
const subcategorization = {};
|
|
306
|
+
const subcategoryConfigs = SUBCATEGORIES[mainCategory];
|
|
307
|
+
|
|
308
|
+
for (const subcategory of Object.keys(subcategoryConfigs)) {
|
|
309
|
+
subcategorization[subcategory] = [];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
const filePath = path.join(categoryDir, file);
|
|
314
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
315
|
+
const subcategory = subcategorizeAgent(file, content, mainCategory);
|
|
316
|
+
|
|
317
|
+
if (subcategory && subcategorization[subcategory]) {
|
|
318
|
+
subcategorization[subcategory].push(file);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Display subcategorization
|
|
323
|
+
console.log(`\n Subcategories:`);
|
|
324
|
+
for (const [subcategory, files] of Object.entries(subcategorization)) {
|
|
325
|
+
if (files.length > 0) {
|
|
326
|
+
console.log(` ${subcategory}: ${files.length} agents`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Create subdirectories and move files
|
|
331
|
+
for (const [subcategory, config] of Object.entries(subcategoryConfigs)) {
|
|
332
|
+
const subcategoryDir = path.join(categoryDir, subcategory);
|
|
333
|
+
const filesToMove = subcategorization[subcategory];
|
|
334
|
+
|
|
335
|
+
if (filesToMove.length === 0) continue;
|
|
336
|
+
|
|
337
|
+
// Create directory
|
|
338
|
+
if (!fs.existsSync(subcategoryDir)) {
|
|
339
|
+
fs.mkdirSync(subcategoryDir, { recursive: true });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Move files
|
|
343
|
+
for (const file of filesToMove) {
|
|
344
|
+
const src = path.join(categoryDir, file);
|
|
345
|
+
const dest = path.join(subcategoryDir, file);
|
|
346
|
+
fs.renameSync(src, dest);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Create subcategory index
|
|
350
|
+
const indexContent = `# ${subcategory.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
351
|
+
|
|
352
|
+
**Description:** ${config.description}
|
|
353
|
+
**Agent Count:** ${filesToMove.length}
|
|
354
|
+
|
|
355
|
+
## Available Agents
|
|
356
|
+
|
|
357
|
+
${filesToMove.sort().map(f => `- [${f.replace('.md', '')}](./${f})`).join('\n')}
|
|
358
|
+
`;
|
|
359
|
+
|
|
360
|
+
fs.writeFileSync(path.join(subcategoryDir, 'INDEX.md'), indexContent);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Update main category index
|
|
364
|
+
const mainIndexContent = `# ${mainCategory.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
365
|
+
|
|
366
|
+
**Total Agents:** ${files.length}
|
|
367
|
+
|
|
368
|
+
## Subcategories
|
|
369
|
+
|
|
370
|
+
${Object.entries(subcategorization)
|
|
371
|
+
.filter(([_, files]) => files.length > 0)
|
|
372
|
+
.sort((a, b) => b[1].length - a[1].length)
|
|
373
|
+
.map(([subcat, files]) => {
|
|
374
|
+
const config = subcategoryConfigs[subcat];
|
|
375
|
+
return `### [${subcat}](${subcat}/INDEX.md) (${files.length} agents)
|
|
376
|
+
|
|
377
|
+
${config.description}
|
|
378
|
+
`;
|
|
379
|
+
}).join('\n')}
|
|
380
|
+
|
|
381
|
+
## Quick Navigation
|
|
382
|
+
|
|
383
|
+
${Object.entries(subcategorization)
|
|
384
|
+
.filter(([_, files]) => files.length > 0)
|
|
385
|
+
.map(([subcat, files]) => `- [${subcat}](${subcat}/INDEX.md) - ${files.length} agents`)
|
|
386
|
+
.join('\n')}
|
|
387
|
+
`;
|
|
388
|
+
|
|
389
|
+
fs.writeFileSync(path.join(categoryDir, 'INDEX.md'), mainIndexContent);
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
category: mainCategory,
|
|
393
|
+
totalAgents: files.length,
|
|
394
|
+
subcategories: Object.fromEntries(
|
|
395
|
+
Object.entries(subcategorization).filter(([_, files]) => files.length > 0)
|
|
396
|
+
)
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Main subcategorization process
|
|
402
|
+
*/
|
|
403
|
+
async function subcategorizeAgents() {
|
|
404
|
+
console.log('š Subcategorizing agents within each main category...\n');
|
|
405
|
+
|
|
406
|
+
const results = {};
|
|
407
|
+
|
|
408
|
+
for (const mainCategory of Object.keys(SUBCATEGORIES)) {
|
|
409
|
+
const result = processCategory(mainCategory);
|
|
410
|
+
if (result) {
|
|
411
|
+
results[mainCategory] = result;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
console.log('\n\nā
Subcategorization complete!\n');
|
|
416
|
+
|
|
417
|
+
// Generate summary
|
|
418
|
+
console.log('š Summary by Main Category:\n');
|
|
419
|
+
for (const [category, data] of Object.entries(results)) {
|
|
420
|
+
console.log(`${category}: ${data.totalAgents} agents in ${Object.keys(data.subcategories).length} subcategories`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.log('\n⨠Done!\n');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Run
|
|
427
|
+
subcategorizeAgents().catch(console.error);
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sync agents, commands, and hooks from npm package to local project
|
|
4
|
+
* Usage: npx claude-flow-novice sync [--force] [--backup] [--agents] [--commands] [--hooks]
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const flags = {
|
|
13
|
+
force: args.includes('--force'),
|
|
14
|
+
backup: args.includes('--backup'),
|
|
15
|
+
agents: args.includes('--agents') || args.length === 0,
|
|
16
|
+
commands: args.includes('--commands') || args.length === 0,
|
|
17
|
+
hooks: args.includes('--hooks') || args.length === 0,
|
|
18
|
+
help: args.includes('--help') || args.includes('-h')
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (flags.help) {
|
|
22
|
+
console.log(`
|
|
23
|
+
Claude Flow Novice - Sync Script
|
|
24
|
+
|
|
25
|
+
Syncs agents, commands, and hooks from npm package to your local project.
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
npx claude-flow-novice sync [options]
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--agents Sync .claude/agents/ only
|
|
32
|
+
--commands Sync .claude/commands/ only
|
|
33
|
+
--hooks Sync config/hooks/ only
|
|
34
|
+
--force Overwrite existing files without prompting
|
|
35
|
+
--backup Create backup before syncing (recommended)
|
|
36
|
+
--help, -h Show this help message
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
npx claude-flow-novice sync # Sync everything
|
|
40
|
+
npx claude-flow-novice sync --agents --backup # Sync agents with backup
|
|
41
|
+
npx claude-flow-novice sync --force --backup # Force sync all with backup
|
|
42
|
+
`);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Find package location
|
|
47
|
+
const packagePath = path.join(__dirname, '..');
|
|
48
|
+
const projectRoot = process.cwd();
|
|
49
|
+
|
|
50
|
+
console.log('š Claude Flow Novice - Sync Script\n');
|
|
51
|
+
console.log(`š¦ Package: ${packagePath}`);
|
|
52
|
+
console.log(`š Project: ${projectRoot}\n`);
|
|
53
|
+
|
|
54
|
+
const syncItems = [
|
|
55
|
+
{
|
|
56
|
+
name: 'agents',
|
|
57
|
+
enabled: flags.agents,
|
|
58
|
+
source: path.join(packagePath, '.claude', 'agents'),
|
|
59
|
+
dest: path.join(projectRoot, '.claude', 'agents'),
|
|
60
|
+
description: 'Agent definitions'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'commands',
|
|
64
|
+
enabled: flags.commands,
|
|
65
|
+
source: path.join(packagePath, '.claude', 'commands'),
|
|
66
|
+
dest: path.join(projectRoot, '.claude', 'commands'),
|
|
67
|
+
description: 'Slash commands'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'hooks',
|
|
71
|
+
enabled: flags.hooks,
|
|
72
|
+
source: path.join(packagePath, 'config', 'hooks'),
|
|
73
|
+
dest: path.join(projectRoot, 'config', 'hooks'),
|
|
74
|
+
description: 'Validation hooks'
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// Backup function
|
|
79
|
+
function createBackup(destPath) {
|
|
80
|
+
if (!fs.existsSync(destPath)) return null;
|
|
81
|
+
|
|
82
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
|
83
|
+
const backupPath = `${destPath}.backup-${timestamp}`;
|
|
84
|
+
|
|
85
|
+
console.log(` š Creating backup: ${path.basename(backupPath)}`);
|
|
86
|
+
execSync(`cp -r "${destPath}" "${backupPath}"`);
|
|
87
|
+
return backupPath;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Copy directory recursively
|
|
91
|
+
function copyDir(src, dest) {
|
|
92
|
+
if (!fs.existsSync(src)) {
|
|
93
|
+
console.error(` ā Source not found: ${src}`);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Create destination directory
|
|
98
|
+
if (!fs.existsSync(dest)) {
|
|
99
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
103
|
+
let fileCount = 0;
|
|
104
|
+
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
const srcPath = path.join(src, entry.name);
|
|
107
|
+
const destPath = path.join(dest, entry.name);
|
|
108
|
+
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
copyDir(srcPath, destPath);
|
|
111
|
+
} else {
|
|
112
|
+
fs.copyFileSync(srcPath, destPath);
|
|
113
|
+
fileCount++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return fileCount;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Count files in directory
|
|
121
|
+
function countFiles(dir) {
|
|
122
|
+
if (!fs.existsSync(dir)) return 0;
|
|
123
|
+
|
|
124
|
+
let count = 0;
|
|
125
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
126
|
+
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
if (entry.isDirectory()) {
|
|
129
|
+
count += countFiles(path.join(dir, entry.name));
|
|
130
|
+
} else {
|
|
131
|
+
count++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return count;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Main sync process
|
|
139
|
+
let totalSynced = 0;
|
|
140
|
+
|
|
141
|
+
for (const item of syncItems) {
|
|
142
|
+
if (!item.enabled) continue;
|
|
143
|
+
|
|
144
|
+
console.log(`\nš Syncing ${item.description}...`);
|
|
145
|
+
console.log(` Source: ${item.source}`);
|
|
146
|
+
console.log(` Dest: ${item.dest}`);
|
|
147
|
+
|
|
148
|
+
// Check if source exists
|
|
149
|
+
if (!fs.existsSync(item.source)) {
|
|
150
|
+
console.log(` ā ļø Source directory not found, skipping`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Backup if requested
|
|
155
|
+
if (flags.backup) {
|
|
156
|
+
createBackup(item.dest);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if destination exists and not forcing
|
|
160
|
+
if (fs.existsSync(item.dest) && !flags.force) {
|
|
161
|
+
const beforeCount = countFiles(item.dest);
|
|
162
|
+
console.log(` ā ļø Destination exists with ${beforeCount} files`);
|
|
163
|
+
console.log(` š” Use --force to overwrite or --backup to create backup first`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Perform sync
|
|
168
|
+
const fileCount = copyDir(item.source, item.dest);
|
|
169
|
+
console.log(` ā
Synced ${fileCount} files`);
|
|
170
|
+
totalSynced += fileCount;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(`\n⨠Sync complete! ${totalSynced} files synced.\n`);
|
|
174
|
+
|
|
175
|
+
if (totalSynced > 0) {
|
|
176
|
+
console.log('Next steps:');
|
|
177
|
+
console.log(' 1. Review synced files');
|
|
178
|
+
console.log(' 2. Customize as needed for your project');
|
|
179
|
+
console.log(' 3. Commit changes to version control\n');
|
|
180
|
+
}
|