ccsetup 1.1.1 → 1.2.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.
Files changed (88) hide show
  1. package/README.md +96 -363
  2. package/bin/create-project.js +1616 -60
  3. package/bin/lib/claudeInterface.js +209 -0
  4. package/bin/lib/contextGenerator.js +287 -0
  5. package/bin/lib/scanner/index.js +28 -0
  6. package/bin/scan.js +367 -0
  7. package/lib/aiAgentSelector.js +155 -0
  8. package/lib/aiMergeHelper.js +112 -0
  9. package/lib/contextGenerator.js +574 -0
  10. package/lib/contextMerger.js +812 -0
  11. package/lib/progressReporter.js +88 -0
  12. package/lib/scanConfig.js +200 -0
  13. package/lib/scanner/fileAnalyzer.js +605 -0
  14. package/lib/scanner/index.js +164 -0
  15. package/lib/scanner/patterns.js +277 -0
  16. package/lib/scanner/projectDetector.js +147 -0
  17. package/lib/templates/README.md +176 -0
  18. package/lib/templates/catalog.js +230 -0
  19. package/lib/templates/filter.js +257 -0
  20. package/lib/templates/index.js +45 -0
  21. package/lib/templates/metadata/agents.json +413 -0
  22. package/lib/templates/metadata-extractor.js +329 -0
  23. package/lib/templates/search.js +356 -0
  24. package/package.json +11 -4
  25. package/template/{agents → .claude/agents}/checker.md +29 -0
  26. package/template/.claude/settings.json +15 -0
  27. package/template/.claude/skills/prd/SKILL.md +343 -0
  28. package/template/.claude/skills/ralph/SKILL.md +339 -0
  29. package/template/CLAUDE.md +39 -21
  30. package/template/CONTRIBUTING.md +37 -0
  31. package/template/agents/README.md +15 -171
  32. package/template/docs/ROADMAP.md +0 -36
  33. package/template/docs/agent-orchestration.md +24 -141
  34. package/template/hooks/workflow-selector/index.js +398 -0
  35. package/template/scripts/ralph/CLAUDE.md +174 -0
  36. package/template/scripts/ralph/ralph.sh +127 -0
  37. package/template/tickets/ticket-list.md +17 -68
  38. package/template/agents/ai-engineer.md +0 -31
  39. package/template/agents/api-documenter.md +0 -31
  40. package/template/agents/architect-review.md +0 -42
  41. package/template/agents/backend-architect.md +0 -29
  42. package/template/agents/business-analyst.md +0 -34
  43. package/template/agents/c-pro.md +0 -34
  44. package/template/agents/cloud-architect.md +0 -31
  45. package/template/agents/code-reviewer.md +0 -28
  46. package/template/agents/content-marketer.md +0 -34
  47. package/template/agents/context-manager.md +0 -63
  48. package/template/agents/cpp-pro.md +0 -37
  49. package/template/agents/customer-support.md +0 -34
  50. package/template/agents/data-engineer.md +0 -31
  51. package/template/agents/data-scientist.md +0 -28
  52. package/template/agents/database-admin.md +0 -31
  53. package/template/agents/database-optimizer.md +0 -31
  54. package/template/agents/debugger.md +0 -29
  55. package/template/agents/deployment-engineer.md +0 -31
  56. package/template/agents/devops-troubleshooter.md +0 -31
  57. package/template/agents/dx-optimizer.md +0 -62
  58. package/template/agents/error-detective.md +0 -31
  59. package/template/agents/frontend-developer.md +0 -30
  60. package/template/agents/golang-pro.md +0 -31
  61. package/template/agents/graphql-architect.md +0 -31
  62. package/template/agents/incident-responder.md +0 -73
  63. package/template/agents/javascript-pro.md +0 -34
  64. package/template/agents/legacy-modernizer.md +0 -31
  65. package/template/agents/ml-engineer.md +0 -31
  66. package/template/agents/mlops-engineer.md +0 -56
  67. package/template/agents/mobile-developer.md +0 -31
  68. package/template/agents/network-engineer.md +0 -31
  69. package/template/agents/payment-integration.md +0 -31
  70. package/template/agents/performance-engineer.md +0 -31
  71. package/template/agents/prompt-engineer.md +0 -58
  72. package/template/agents/python-pro.md +0 -31
  73. package/template/agents/quant-analyst.md +0 -31
  74. package/template/agents/risk-manager.md +0 -40
  75. package/template/agents/rust-pro.md +0 -34
  76. package/template/agents/sales-automator.md +0 -34
  77. package/template/agents/search-specialist.md +0 -58
  78. package/template/agents/security-auditor.md +0 -31
  79. package/template/agents/sql-pro.md +0 -34
  80. package/template/agents/terraform-specialist.md +0 -34
  81. package/template/agents/test-automator.md +0 -31
  82. /package/template/{agents → .claude/agents}/backend.md +0 -0
  83. /package/template/{agents → .claude/agents}/blockchain.md +0 -0
  84. /package/template/{agents → .claude/agents}/coder.md +0 -0
  85. /package/template/{agents → .claude/agents}/frontend.md +0 -0
  86. /package/template/{agents → .claude/agents}/planner.md +0 -0
  87. /package/template/{agents → .claude/agents}/researcher.md +0 -0
  88. /package/template/{agents → .claude/agents}/shadcn.md +0 -0
@@ -0,0 +1,176 @@
1
+ # Template Catalog System
2
+
3
+ The template catalog system provides a powerful foundation for browsing, filtering, and selecting from ccsetup's collection of 50+ agent templates. This system implements Phase 1 of the template selection feature as outlined in PLAN-009.
4
+
5
+ ## Overview
6
+
7
+ The template catalog system consists of:
8
+ - **Metadata Extraction**: Automatically extracts metadata from agent frontmatter
9
+ - **Template Catalog**: Provides filtering, search, and discovery capabilities
10
+ - **Category Management**: Organizes templates into logical categories
11
+ - **Caching**: Efficient caching for fast template discovery
12
+
13
+ ## Components
14
+
15
+ ### MetadataExtractor (`metadata-extractor.js`)
16
+ - Parses agent frontmatter to extract metadata
17
+ - Automatically categorizes agents based on content analysis
18
+ - Generates tags from descriptions and tools
19
+ - Creates structured metadata JSON files
20
+
21
+ ### TemplateCatalog (`catalog.js`)
22
+ - Template discovery and loading
23
+ - Filtering by category, tags, and search queries
24
+ - Template validation
25
+ - Caching for performance
26
+
27
+ ### Generated Metadata (`metadata/agents.json`)
28
+ - Contains metadata for all 52 agents
29
+ - Organized by categories: Development, Backend, Planning, AI/ML, etc.
30
+ - Includes tags, tools, examples, and workflows for each agent
31
+
32
+ ## Usage
33
+
34
+ ### Basic Catalog Operations
35
+ ```javascript
36
+ const { TemplateCatalog } = require('./lib/templates');
37
+
38
+ const catalog = new TemplateCatalog();
39
+
40
+ // Load all templates
41
+ const data = await catalog.load();
42
+
43
+ // Get templates by category
44
+ const planningAgents = await catalog.getTemplates('planning');
45
+ const frontendAgents = await catalog.getTemplates('frontend');
46
+
47
+ // Filter by tags
48
+ const testingAgents = await catalog.getTemplates(null, ['testing']);
49
+
50
+ // Search templates
51
+ const reactAgents = await catalog.searchTemplates('react');
52
+
53
+ // Get categories
54
+ const categories = await catalog.getCategories();
55
+ ```
56
+
57
+ ### Template Filtering
58
+ ```javascript
59
+ // Multiple filters can be combined
60
+ const templates = await catalog.getTemplates(
61
+ 'development', // category
62
+ ['frontend', 'ui'], // tags
63
+ 'react' // search query
64
+ );
65
+
66
+ // Advanced search with options
67
+ const results = await catalog.searchTemplates('API', {
68
+ category: 'backend',
69
+ tags: ['api'],
70
+ limit: 10,
71
+ sortBy: 'name'
72
+ });
73
+ ```
74
+
75
+ ### Template Validation
76
+ ```javascript
77
+ // Validate a template
78
+ const validation = await catalog.validateTemplate(template);
79
+ if (!validation.valid) {
80
+ console.log('Errors:', validation.errors);
81
+ console.log('Warnings:', validation.warnings);
82
+ }
83
+ ```
84
+
85
+ ## Categories
86
+
87
+ The system automatically categorizes agents into:
88
+
89
+ - **Planning & Architecture** (8 agents) - Strategic planning and system design
90
+ - **Development & Implementation** (26 agents) - Code implementation and development
91
+ - **Frontend Development** (13 agents) - UI/UX and frontend frameworks
92
+ - **Backend Development** (2 agents) - API design and server-side development
93
+ - **Quality Assurance & Testing** (9 agents) - Testing and code review
94
+ - **Security & Auditing** (5 agents) - Security analysis and auditing
95
+ - **DevOps & Infrastructure** (2 agents) - Deployment and infrastructure
96
+ - **Database & Data** (3 agents) - Database management and data analysis
97
+ - **AI & Machine Learning** (5 agents) - AI/ML development and operations
98
+ - **Mobile Development** (1 agent) - Mobile app development
99
+ - **General Purpose** (4 agents) - Utility and general-purpose agents
100
+
101
+ ## Popular Tags
102
+
103
+ Most common tags across all templates:
104
+ - `development` (35 agents)
105
+ - `implementation` (35 agents)
106
+ - `ml`, `ai` (19 agents each)
107
+ - `performance` (19 agents)
108
+ - `frontend`, `ui` (13 agents each)
109
+ - `backend`, `api` (9 agents each)
110
+ - `testing` (9 agents)
111
+
112
+ ## Scripts
113
+
114
+ ### Generate Metadata
115
+ ```bash
116
+ npm run metadata:generate
117
+ ```
118
+ Scans all agent templates and generates fresh metadata.
119
+
120
+ ### Test Catalog
121
+ ```bash
122
+ npm run catalog:test
123
+ ```
124
+ Runs comprehensive tests of catalog functionality.
125
+
126
+ ### Demo System
127
+ ```bash
128
+ node scripts/demo-template-selection.js
129
+ ```
130
+ Demonstrates browsing, filtering, and search capabilities.
131
+
132
+ ## Data Structure
133
+
134
+ Each template has the following metadata:
135
+ ```json
136
+ {
137
+ "id": "agent-planner",
138
+ "name": "Strategic Planner Agent",
139
+ "category": "agents",
140
+ "subcategory": "planning",
141
+ "description": "Strategic planning specialist...",
142
+ "tags": ["planning", "architecture", "roadmap"],
143
+ "tools": ["Read", "Grep", "Glob", "TodoWrite"],
144
+ "version": "1.0.0",
145
+ "dependencies": [],
146
+ "files": ["planner.md"],
147
+ "author": "ccsetup",
148
+ "examples": ["Breaking down feature implementations"],
149
+ "workflows": ["Feature Development", "Refactoring"]
150
+ }
151
+ ```
152
+
153
+ ## Performance
154
+
155
+ - **Caching**: 5-minute cache validity for fast repeated access
156
+ - **Lazy Loading**: Metadata loaded only when needed
157
+ - **Efficient Filtering**: Fast category and tag-based filtering
158
+ - **Search Optimization**: Text search across name, description, tags, and examples
159
+
160
+ ## Future Enhancements
161
+
162
+ Phase 1 provides the foundation for:
163
+ - Interactive CLI template selection (Phase 2)
164
+ - Browse command implementation (Phase 3)
165
+ - Additional template categories (Phase 4)
166
+ - Template marketplace features (Future)
167
+
168
+ ## Files
169
+
170
+ - `metadata-extractor.js` - Extracts metadata from agent templates
171
+ - `catalog.js` - Main catalog class with filtering and search
172
+ - `index.js` - Public API exports
173
+ - `metadata/agents.json` - Generated metadata for all agents
174
+ - `README.md` - This documentation
175
+
176
+ The template catalog system provides a robust foundation for the enhanced template selection system outlined in PLAN-009.
@@ -0,0 +1,230 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const MetadataExtractor = require('./metadata-extractor');
4
+
5
+ class TemplateCatalog {
6
+ constructor() {
7
+ this.cache = null;
8
+ this.cacheTimestamp = null;
9
+ this.cacheValidityMs = 5 * 60 * 1000; // 5 minutes
10
+ }
11
+
12
+ async load(forceRefresh = false) {
13
+ if (!forceRefresh && this.isCacheValid()) {
14
+ return this.cache;
15
+ }
16
+
17
+ try {
18
+ const metadataPath = path.join(__dirname, 'metadata/agents.json');
19
+
20
+ if (fs.existsSync(metadataPath) && !forceRefresh) {
21
+ const content = fs.readFileSync(metadataPath, 'utf8');
22
+ this.cache = JSON.parse(content);
23
+ this.cacheTimestamp = Date.now();
24
+ return this.cache;
25
+ }
26
+
27
+ console.log('Generating fresh catalog from agent templates...');
28
+ const catalog = MetadataExtractor.generateCatalogFromAgents();
29
+
30
+ MetadataExtractor.saveCatalogToFile(catalog, metadataPath);
31
+
32
+ this.cache = catalog;
33
+ this.cacheTimestamp = Date.now();
34
+
35
+ return this.cache;
36
+ } catch (error) {
37
+ console.error('Error loading template catalog:', error.message);
38
+
39
+ return {
40
+ agents: [],
41
+ categories: {},
42
+ stats: {
43
+ totalAgents: 0,
44
+ categories: 0,
45
+ lastUpdated: new Date().toISOString(),
46
+ error: error.message
47
+ }
48
+ };
49
+ }
50
+ }
51
+
52
+ isCacheValid() {
53
+ return this.cache &&
54
+ this.cacheTimestamp &&
55
+ (Date.now() - this.cacheTimestamp) < this.cacheValidityMs;
56
+ }
57
+
58
+ async getTemplates(category = null, tags = [], searchQuery = null) {
59
+ const catalog = await this.load();
60
+ let templates = catalog.agents || [];
61
+
62
+ if (category && category !== 'all') {
63
+ if (category === 'agents') {
64
+ // Already filtered to agents
65
+ } else {
66
+ templates = templates.filter(t => t.subcategory === category);
67
+ }
68
+ }
69
+
70
+ if (tags && tags.length > 0) {
71
+ templates = templates.filter(t =>
72
+ tags.some(tag => t.tags.includes(tag.toLowerCase()))
73
+ );
74
+ }
75
+
76
+ if (searchQuery && searchQuery.trim()) {
77
+ const query = searchQuery.toLowerCase().trim();
78
+ templates = templates.filter(t => {
79
+ const searchableText = [
80
+ t.name,
81
+ t.description,
82
+ ...t.tags,
83
+ ...t.examples
84
+ ].join(' ').toLowerCase();
85
+
86
+ return searchableText.includes(query);
87
+ });
88
+ }
89
+
90
+ return templates;
91
+ }
92
+
93
+ async getTemplate(id) {
94
+ const catalog = await this.load();
95
+ return catalog.agents.find(agent => agent.id === id);
96
+ }
97
+
98
+ async getCategories() {
99
+ const catalog = await this.load();
100
+ return catalog.categories || {};
101
+ }
102
+
103
+ async getStats() {
104
+ const catalog = await this.load();
105
+ return catalog.stats || {};
106
+ }
107
+
108
+ async searchTemplates(query, options = {}) {
109
+ const {
110
+ category = null,
111
+ tags = [],
112
+ limit = 50,
113
+ sortBy = 'name'
114
+ } = options;
115
+
116
+ let templates = await this.getTemplates(category, tags, query);
117
+
118
+ if (sortBy === 'name') {
119
+ templates.sort((a, b) => a.name.localeCompare(b.name));
120
+ } else if (sortBy === 'category') {
121
+ templates.sort((a, b) => {
122
+ const categoryCompare = a.subcategory.localeCompare(b.subcategory);
123
+ return categoryCompare !== 0 ? categoryCompare : a.name.localeCompare(b.name);
124
+ });
125
+ }
126
+
127
+ return templates.slice(0, limit);
128
+ }
129
+
130
+ async validateTemplate(template) {
131
+ const requiredFields = ['id', 'name', 'category', 'description'];
132
+ const validation = {
133
+ valid: true,
134
+ errors: [],
135
+ warnings: []
136
+ };
137
+
138
+ for (const field of requiredFields) {
139
+ if (!template[field]) {
140
+ validation.valid = false;
141
+ validation.errors.push(`Missing required field: ${field}`);
142
+ }
143
+ }
144
+
145
+ if (template.category !== 'agents') {
146
+ validation.warnings.push(`Unexpected category: ${template.category}`);
147
+ }
148
+
149
+ if (!template.files || template.files.length === 0) {
150
+ validation.valid = false;
151
+ validation.errors.push('Template must specify at least one file');
152
+ }
153
+
154
+ if (template.files) {
155
+ const templateDir = path.join(__dirname, '../../template/.claude/agents');
156
+ for (const file of template.files) {
157
+ const filePath = path.join(templateDir, file);
158
+ if (!fs.existsSync(filePath)) {
159
+ validation.valid = false;
160
+ validation.errors.push(`Template file not found: ${file}`);
161
+ }
162
+ }
163
+ }
164
+
165
+ if (!template.tags || template.tags.length === 0) {
166
+ validation.warnings.push('Template has no tags - may be hard to discover');
167
+ }
168
+
169
+ return validation;
170
+ }
171
+
172
+ async refreshCatalog() {
173
+ console.log('Refreshing template catalog...');
174
+
175
+ const catalog = MetadataExtractor.generateCatalogFromAgents();
176
+ const metadataPath = path.join(__dirname, 'metadata/agents.json');
177
+
178
+ const saved = MetadataExtractor.saveCatalogToFile(catalog, metadataPath);
179
+
180
+ if (saved) {
181
+ this.cache = catalog;
182
+ this.cacheTimestamp = Date.now();
183
+ console.log(`Catalog refreshed: ${catalog.stats.totalAgents} agents processed`);
184
+ return catalog;
185
+ } else {
186
+ throw new Error('Failed to save refreshed catalog');
187
+ }
188
+ }
189
+
190
+ async getCatalogBySubcategory() {
191
+ const catalog = await this.load();
192
+ const result = {};
193
+
194
+ for (const agent of catalog.agents) {
195
+ if (!result[agent.subcategory]) {
196
+ result[agent.subcategory] = [];
197
+ }
198
+ result[agent.subcategory].push(agent);
199
+ }
200
+
201
+ Object.keys(result).forEach(category => {
202
+ result[category].sort((a, b) => a.name.localeCompare(b.name));
203
+ });
204
+
205
+ return result;
206
+ }
207
+
208
+ async getTagCloud() {
209
+ const catalog = await this.load();
210
+ const tagCounts = {};
211
+
212
+ for (const agent of catalog.agents) {
213
+ for (const tag of agent.tags) {
214
+ tagCounts[tag] = (tagCounts[tag] || 0) + 1;
215
+ }
216
+ }
217
+
218
+ const sortedTags = Object.entries(tagCounts)
219
+ .sort((a, b) => b[1] - a[1])
220
+ .map(([tag, count]) => ({ tag, count }));
221
+
222
+ return sortedTags;
223
+ }
224
+
225
+ static createInstance() {
226
+ return new TemplateCatalog();
227
+ }
228
+ }
229
+
230
+ module.exports = TemplateCatalog;
@@ -0,0 +1,257 @@
1
+ class TemplateFilter {
2
+ constructor(templates = []) {
3
+ this.templates = templates;
4
+ }
5
+
6
+ setTemplates(templates) {
7
+ this.templates = templates;
8
+ return this;
9
+ }
10
+
11
+ byCategory(category) {
12
+ if (!category || category === 'all') {
13
+ return new TemplateFilter(this.templates);
14
+ }
15
+
16
+ const filtered = this.templates.filter(template => {
17
+ if (category === 'agents') {
18
+ return template.category === 'agents';
19
+ }
20
+ return template.subcategory === category;
21
+ });
22
+
23
+ return new TemplateFilter(filtered);
24
+ }
25
+
26
+ byTags(tags) {
27
+ if (!tags || tags.length === 0) {
28
+ return new TemplateFilter(this.templates);
29
+ }
30
+
31
+ const normalizedTags = tags.map(tag => tag.toLowerCase());
32
+ const filtered = this.templates.filter(template => {
33
+ if (!template.tags || template.tags.length === 0) {
34
+ return false;
35
+ }
36
+
37
+ return normalizedTags.some(tag =>
38
+ template.tags.some(templateTag =>
39
+ templateTag.toLowerCase().includes(tag)
40
+ )
41
+ );
42
+ });
43
+
44
+ return new TemplateFilter(filtered);
45
+ }
46
+
47
+ byExactTags(tags) {
48
+ if (!tags || tags.length === 0) {
49
+ return new TemplateFilter(this.templates);
50
+ }
51
+
52
+ const normalizedTags = tags.map(tag => tag.toLowerCase());
53
+ const filtered = this.templates.filter(template => {
54
+ if (!template.tags || template.tags.length === 0) {
55
+ return false;
56
+ }
57
+
58
+ return normalizedTags.every(tag =>
59
+ template.tags.some(templateTag =>
60
+ templateTag.toLowerCase() === tag
61
+ )
62
+ );
63
+ });
64
+
65
+ return new TemplateFilter(filtered);
66
+ }
67
+
68
+ byAnyTag(tags) {
69
+ if (!tags || tags.length === 0) {
70
+ return new TemplateFilter(this.templates);
71
+ }
72
+
73
+ const normalizedTags = tags.map(tag => tag.toLowerCase());
74
+ const filtered = this.templates.filter(template => {
75
+ if (!template.tags || template.tags.length === 0) {
76
+ return false;
77
+ }
78
+
79
+ return normalizedTags.some(tag =>
80
+ template.tags.some(templateTag =>
81
+ templateTag.toLowerCase() === tag
82
+ )
83
+ );
84
+ });
85
+
86
+ return new TemplateFilter(filtered);
87
+ }
88
+
89
+ byAllTags(tags) {
90
+ if (!tags || tags.length === 0) {
91
+ return new TemplateFilter(this.templates);
92
+ }
93
+
94
+ const normalizedTags = tags.map(tag => tag.toLowerCase());
95
+ const filtered = this.templates.filter(template => {
96
+ if (!template.tags || template.tags.length === 0) {
97
+ return false;
98
+ }
99
+
100
+ return normalizedTags.every(tag =>
101
+ template.tags.some(templateTag =>
102
+ templateTag.toLowerCase() === tag
103
+ )
104
+ );
105
+ });
106
+
107
+ return new TemplateFilter(filtered);
108
+ }
109
+
110
+ combine(filters = {}) {
111
+ let result = this.templates;
112
+
113
+ if (filters.category) {
114
+ result = new TemplateFilter(result).byCategory(filters.category).getResults();
115
+ }
116
+
117
+ if (filters.tags && filters.tags.length > 0) {
118
+ const tagMode = filters.tagMode || 'any';
119
+ if (tagMode === 'all') {
120
+ result = new TemplateFilter(result).byAllTags(filters.tags).getResults();
121
+ } else if (tagMode === 'exact') {
122
+ result = new TemplateFilter(result).byExactTags(filters.tags).getResults();
123
+ } else {
124
+ result = new TemplateFilter(result).byAnyTag(filters.tags).getResults();
125
+ }
126
+ }
127
+
128
+ if (filters.subcategories && filters.subcategories.length > 0) {
129
+ result = result.filter(template =>
130
+ filters.subcategories.includes(template.subcategory)
131
+ );
132
+ }
133
+
134
+ return new TemplateFilter(result);
135
+ }
136
+
137
+ getResults() {
138
+ return [...this.templates];
139
+ }
140
+
141
+ getCount() {
142
+ return this.templates.length;
143
+ }
144
+
145
+ getCategories() {
146
+ const categories = new Set();
147
+ this.templates.forEach(template => {
148
+ if (template.category) {
149
+ categories.add(template.category);
150
+ }
151
+ });
152
+ return Array.from(categories).sort();
153
+ }
154
+
155
+ getSubcategories() {
156
+ const subcategories = new Set();
157
+ this.templates.forEach(template => {
158
+ if (template.subcategory) {
159
+ subcategories.add(template.subcategory);
160
+ }
161
+ });
162
+ return Array.from(subcategories).sort();
163
+ }
164
+
165
+ getAllTags() {
166
+ const tags = new Set();
167
+ this.templates.forEach(template => {
168
+ if (template.tags && Array.isArray(template.tags)) {
169
+ template.tags.forEach(tag => tags.add(tag.toLowerCase()));
170
+ }
171
+ });
172
+ return Array.from(tags).sort();
173
+ }
174
+
175
+ getTagsByFrequency() {
176
+ const tagCounts = {};
177
+ this.templates.forEach(template => {
178
+ if (template.tags && Array.isArray(template.tags)) {
179
+ template.tags.forEach(tag => {
180
+ const normalizedTag = tag.toLowerCase();
181
+ tagCounts[normalizedTag] = (tagCounts[normalizedTag] || 0) + 1;
182
+ });
183
+ }
184
+ });
185
+
186
+ return Object.entries(tagCounts)
187
+ .sort((a, b) => b[1] - a[1])
188
+ .map(([tag, count]) => ({ tag, count }));
189
+ }
190
+
191
+ sortBy(field, direction = 'asc') {
192
+ const sorted = [...this.templates].sort((a, b) => {
193
+ let valueA = a[field];
194
+ let valueB = b[field];
195
+
196
+ if (typeof valueA === 'string' && typeof valueB === 'string') {
197
+ valueA = valueA.toLowerCase();
198
+ valueB = valueB.toLowerCase();
199
+ }
200
+
201
+ if (valueA < valueB) {
202
+ return direction === 'asc' ? -1 : 1;
203
+ }
204
+ if (valueA > valueB) {
205
+ return direction === 'asc' ? 1 : -1;
206
+ }
207
+ return 0;
208
+ });
209
+
210
+ return new TemplateFilter(sorted);
211
+ }
212
+
213
+ limit(count) {
214
+ return new TemplateFilter(this.templates.slice(0, count));
215
+ }
216
+
217
+ offset(count) {
218
+ return new TemplateFilter(this.templates.slice(count));
219
+ }
220
+
221
+ paginate(page, pageSize) {
222
+ const start = (page - 1) * pageSize;
223
+ const end = start + pageSize;
224
+ return new TemplateFilter(this.templates.slice(start, end));
225
+ }
226
+
227
+ isEmpty() {
228
+ return this.templates.length === 0;
229
+ }
230
+
231
+ first() {
232
+ return this.templates.length > 0 ? this.templates[0] : null;
233
+ }
234
+
235
+ last() {
236
+ return this.templates.length > 0 ? this.templates[this.templates.length - 1] : null;
237
+ }
238
+
239
+ find(predicate) {
240
+ return this.templates.find(predicate);
241
+ }
242
+
243
+ some(predicate) {
244
+ return this.templates.some(predicate);
245
+ }
246
+
247
+ every(predicate) {
248
+ return this.templates.every(predicate);
249
+ }
250
+
251
+ static createFromCatalog(catalog) {
252
+ const templates = catalog?.agents || [];
253
+ return new TemplateFilter(templates);
254
+ }
255
+ }
256
+
257
+ module.exports = TemplateFilter;
@@ -0,0 +1,45 @@
1
+ const TemplateCatalog = require('./catalog');
2
+ const MetadataExtractor = require('./metadata-extractor');
3
+ const TemplateFilter = require('./filter');
4
+ const TemplateSearch = require('./search');
5
+
6
+ module.exports = {
7
+ TemplateCatalog,
8
+ MetadataExtractor,
9
+ TemplateFilter,
10
+ TemplateSearch,
11
+
12
+ async createCatalog() {
13
+ return TemplateCatalog.createInstance();
14
+ },
15
+
16
+ async refreshCatalog() {
17
+ const catalog = TemplateCatalog.createInstance();
18
+ return await catalog.refreshCatalog();
19
+ },
20
+
21
+ async getCatalogStats() {
22
+ const catalog = TemplateCatalog.createInstance();
23
+ return await catalog.getStats();
24
+ },
25
+
26
+ createFilter(templates) {
27
+ return new TemplateFilter(templates);
28
+ },
29
+
30
+ createSearch(templates) {
31
+ return new TemplateSearch(templates);
32
+ },
33
+
34
+ async searchTemplates(query, options = {}) {
35
+ const catalog = TemplateCatalog.createInstance();
36
+ const templates = await catalog.load();
37
+ return new TemplateSearch(templates.agents || []).search(query, options);
38
+ },
39
+
40
+ async filterTemplates(filters = {}) {
41
+ const catalog = TemplateCatalog.createInstance();
42
+ const templates = await catalog.load();
43
+ return new TemplateFilter(templates.agents || []).combine(filters);
44
+ }
45
+ };