ccsetup 1.1.1 → 1.2.1

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 (86) hide show
  1. package/README.md +144 -342
  2. package/bin/create-project.js +1246 -90
  3. package/bin/lib/claudeInterface.js +209 -0
  4. package/lib/aiAgentSelector.js +155 -0
  5. package/lib/templates/README.md +176 -0
  6. package/lib/templates/catalog.js +230 -0
  7. package/lib/templates/filter.js +257 -0
  8. package/lib/templates/index.js +45 -0
  9. package/lib/templates/metadata/agents.json +413 -0
  10. package/lib/templates/metadata-extractor.js +329 -0
  11. package/lib/templates/search.js +356 -0
  12. package/package.json +13 -5
  13. package/template/{agents → .claude/agents}/checker.md +29 -0
  14. package/template/.claude/settings.json +32 -0
  15. package/template/.claude/skills/codex-review/SKILL.md +139 -0
  16. package/template/.claude/skills/prd/SKILL.md +343 -0
  17. package/template/.claude/skills/ralph/SKILL.md +339 -0
  18. package/template/.claude/skills/secops/SKILL.md +259 -0
  19. package/template/.codex/skills/codex-review/SKILL.md +139 -0
  20. package/template/.codex/skills/prd/SKILL.md +343 -0
  21. package/template/.codex/skills/ralph/SKILL.md +339 -0
  22. package/template/AGENTS.md +43 -0
  23. package/template/CLAUDE.md +141 -21
  24. package/template/CONTRIBUTING.md +37 -0
  25. package/template/agents/README.md +15 -171
  26. package/template/docs/ROADMAP.md +0 -36
  27. package/template/docs/agent-orchestration.md +24 -141
  28. package/template/docs/codex-setup.md +32 -0
  29. package/template/hooks/codex-review/index.js +105 -0
  30. package/template/hooks/workflow-selector/index.js +398 -0
  31. package/template/scripts/codex-review/codex-review.sh +266 -0
  32. package/template/scripts/ralph/CLAUDE.md +174 -0
  33. package/template/scripts/ralph/CODEX.md +76 -0
  34. package/template/scripts/ralph/ralph.sh +150 -0
  35. package/template/tickets/ticket-list.md +17 -68
  36. package/template/agents/ai-engineer.md +0 -31
  37. package/template/agents/api-documenter.md +0 -31
  38. package/template/agents/architect-review.md +0 -42
  39. package/template/agents/backend-architect.md +0 -29
  40. package/template/agents/business-analyst.md +0 -34
  41. package/template/agents/c-pro.md +0 -34
  42. package/template/agents/cloud-architect.md +0 -31
  43. package/template/agents/code-reviewer.md +0 -28
  44. package/template/agents/content-marketer.md +0 -34
  45. package/template/agents/context-manager.md +0 -63
  46. package/template/agents/cpp-pro.md +0 -37
  47. package/template/agents/customer-support.md +0 -34
  48. package/template/agents/data-engineer.md +0 -31
  49. package/template/agents/data-scientist.md +0 -28
  50. package/template/agents/database-admin.md +0 -31
  51. package/template/agents/database-optimizer.md +0 -31
  52. package/template/agents/debugger.md +0 -29
  53. package/template/agents/deployment-engineer.md +0 -31
  54. package/template/agents/devops-troubleshooter.md +0 -31
  55. package/template/agents/dx-optimizer.md +0 -62
  56. package/template/agents/error-detective.md +0 -31
  57. package/template/agents/frontend-developer.md +0 -30
  58. package/template/agents/golang-pro.md +0 -31
  59. package/template/agents/graphql-architect.md +0 -31
  60. package/template/agents/incident-responder.md +0 -73
  61. package/template/agents/javascript-pro.md +0 -34
  62. package/template/agents/legacy-modernizer.md +0 -31
  63. package/template/agents/ml-engineer.md +0 -31
  64. package/template/agents/mlops-engineer.md +0 -56
  65. package/template/agents/mobile-developer.md +0 -31
  66. package/template/agents/network-engineer.md +0 -31
  67. package/template/agents/payment-integration.md +0 -31
  68. package/template/agents/performance-engineer.md +0 -31
  69. package/template/agents/prompt-engineer.md +0 -58
  70. package/template/agents/python-pro.md +0 -31
  71. package/template/agents/quant-analyst.md +0 -31
  72. package/template/agents/risk-manager.md +0 -40
  73. package/template/agents/rust-pro.md +0 -34
  74. package/template/agents/sales-automator.md +0 -34
  75. package/template/agents/search-specialist.md +0 -58
  76. package/template/agents/security-auditor.md +0 -31
  77. package/template/agents/sql-pro.md +0 -34
  78. package/template/agents/terraform-specialist.md +0 -34
  79. package/template/agents/test-automator.md +0 -31
  80. /package/template/{agents → .claude/agents}/backend.md +0 -0
  81. /package/template/{agents → .claude/agents}/blockchain.md +0 -0
  82. /package/template/{agents → .claude/agents}/coder.md +0 -0
  83. /package/template/{agents → .claude/agents}/frontend.md +0 -0
  84. /package/template/{agents → .claude/agents}/planner.md +0 -0
  85. /package/template/{agents → .claude/agents}/researcher.md +0 -0
  86. /package/template/{agents → .claude/agents}/shadcn.md +0 -0
@@ -0,0 +1,209 @@
1
+ const { exec } = require('child_process');
2
+ const { promisify } = require('util');
3
+ const execAsync = promisify(exec);
4
+
5
+ class ClaudeInterface {
6
+ constructor(projectPath) {
7
+ this.projectPath = projectPath;
8
+ }
9
+
10
+ /**
11
+ * Check if Claude Code CLI is available
12
+ */
13
+ async isAvailable() {
14
+ try {
15
+ await execAsync('claude --version');
16
+ return true;
17
+ } catch (error) {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Scan repository using Claude Code CLI
24
+ */
25
+ async scanRepository() {
26
+ const isAvailable = await this.isAvailable();
27
+ if (!isAvailable) {
28
+ throw new Error('Claude Code CLI is not available. Please install it first: npm install -g @anthropic-ai/claude-code');
29
+ }
30
+
31
+ console.log('🤖 Using Claude Code to analyze your repository...');
32
+
33
+ try {
34
+ // Use Claude Code to analyze the repository
35
+ const projectInfo = await this.analyzeProject();
36
+ const dependencies = await this.analyzeDependencies();
37
+ const structure = await this.analyzeStructure();
38
+ const commands = await this.analyzeCommands();
39
+ const patterns = await this.analyzePatterns();
40
+
41
+ return {
42
+ projectType: projectInfo.language || 'Unknown',
43
+ frameworks: projectInfo.frameworks || [],
44
+ purpose: projectInfo.description || '',
45
+ structure: structure.directories || [],
46
+ dependencies: dependencies,
47
+ commands: commands.scripts || [],
48
+ patterns: patterns,
49
+ scanDate: new Date().toISOString(),
50
+ scanMethod: 'claude-code'
51
+ };
52
+ } catch (error) {
53
+ throw new Error(`Claude Code analysis failed: ${error.message}`);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Analyze project type and frameworks
59
+ */
60
+ async analyzeProject() {
61
+ const prompt = `Analyze this project and tell me:
62
+ 1. The main programming language
63
+ 2. Key frameworks being used
64
+ 3. A one-sentence description of what this project does
65
+
66
+ Respond in this exact JSON format:
67
+ {
68
+ "language": "the main language",
69
+ "frameworks": ["framework1", "framework2"],
70
+ "description": "one sentence description"
71
+ }`;
72
+
73
+ const result = await this.executeCommand(prompt);
74
+ return this.parseJsonResponse(result);
75
+ }
76
+
77
+ /**
78
+ * Analyze project dependencies
79
+ */
80
+ async analyzeDependencies() {
81
+ const prompt = `List the key dependencies in this project. Look at package.json, requirements.txt, go.mod, etc.
82
+
83
+ Respond in this exact JSON format:
84
+ {
85
+ "runtime": ["dep1", "dep2"],
86
+ "dev": ["devDep1", "devDep2"]
87
+ }`;
88
+
89
+ const result = await this.executeCommand(prompt);
90
+ const parsed = this.parseJsonResponse(result);
91
+ return {
92
+ runtime: parsed.runtime || [],
93
+ dev: parsed.dev || []
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Analyze project structure
99
+ */
100
+ async analyzeStructure() {
101
+ const prompt = `List the most important directories in this project and their purpose.
102
+
103
+ Respond in this exact JSON format:
104
+ {
105
+ "directories": [
106
+ {"path": "/src", "purpose": "source code"},
107
+ {"path": "/tests", "purpose": "test files"}
108
+ ]
109
+ }`;
110
+
111
+ const result = await this.executeCommand(prompt);
112
+ return this.parseJsonResponse(result);
113
+ }
114
+
115
+ /**
116
+ * Analyze available commands
117
+ */
118
+ async analyzeCommands() {
119
+ const prompt = `List all available commands from package.json scripts, Makefile, or other sources.
120
+
121
+ Respond in this exact JSON format:
122
+ {
123
+ "scripts": [
124
+ {"command": "npm run dev", "description": "start development server"},
125
+ {"command": "npm test", "description": "run tests"}
126
+ ]
127
+ }`;
128
+
129
+ const result = await this.executeCommand(prompt);
130
+ return this.parseJsonResponse(result);
131
+ }
132
+
133
+ /**
134
+ * Analyze patterns and tools
135
+ */
136
+ async analyzePatterns() {
137
+ const prompt = `Identify architectural patterns, testing tools, and deployment methods used.
138
+
139
+ Respond in this exact JSON format:
140
+ {
141
+ "architecture": ["MVC", "REST API"],
142
+ "testing": ["Jest", "unit tests"],
143
+ "deployment": ["Docker", "CI/CD"]
144
+ }`;
145
+
146
+ const result = await this.executeCommand(prompt);
147
+ return this.parseJsonResponse(result);
148
+ }
149
+
150
+ /**
151
+ * Execute Claude Code command
152
+ */
153
+ async executeCommand(prompt) {
154
+ // Create a temporary file to store the prompt to avoid shell escaping issues
155
+ const fs = require('fs');
156
+ const path = require('path');
157
+ const os = require('os');
158
+
159
+ const tmpFile = path.join(os.tmpdir(), `claude-prompt-${Date.now()}.txt`);
160
+ fs.writeFileSync(tmpFile, prompt);
161
+
162
+ try {
163
+ const command = `cat "${tmpFile}" | claude --print`;
164
+
165
+ const { stdout, stderr } = await execAsync(command, {
166
+ cwd: this.projectPath,
167
+ maxBuffer: 1024 * 1024 * 10, // 10MB buffer
168
+ timeout: 60000 // 60 second timeout
169
+ });
170
+
171
+ // Clean up temp file
172
+ fs.unlinkSync(tmpFile);
173
+
174
+ if (stderr && !stderr.includes('Warning')) {
175
+ throw new Error(`Claude Code error: ${stderr}`);
176
+ }
177
+
178
+ return stdout;
179
+ } catch (error) {
180
+ // Clean up temp file on error
181
+ if (fs.existsSync(tmpFile)) {
182
+ fs.unlinkSync(tmpFile);
183
+ }
184
+ throw error;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Parse JSON from Claude's response
190
+ */
191
+ parseJsonResponse(response) {
192
+ try {
193
+ // Try to extract JSON from the response
194
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
195
+ if (jsonMatch) {
196
+ return JSON.parse(jsonMatch[0]);
197
+ }
198
+
199
+ // If no JSON found, return empty object
200
+ console.warn('Could not parse JSON from Claude response');
201
+ return {};
202
+ } catch (error) {
203
+ console.warn('JSON parsing failed:', error.message);
204
+ return {};
205
+ }
206
+ }
207
+ }
208
+
209
+ module.exports = ClaudeInterface;
@@ -0,0 +1,155 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ class AIAgentSelector {
6
+ constructor(scanResults, agentMetadata) {
7
+ this.scanResults = scanResults;
8
+ this.agentMetadata = agentMetadata;
9
+ this.hasClaudeCode = this.checkClaudeCode();
10
+ }
11
+
12
+ checkClaudeCode() {
13
+ try {
14
+ execSync('which claude', { stdio: 'ignore' });
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
21
+ async recommendAgents(maxRecommendations = 5) {
22
+ if (!this.hasClaudeCode || !this.scanResults) {
23
+ return null;
24
+ }
25
+
26
+ const projectContext = this.buildProjectContext();
27
+ const agentList = this.getAgentSummary();
28
+
29
+ const prompt = `Analyze this project and recommend the ${maxRecommendations} most relevant agents:
30
+
31
+ PROJECT DETAILS:
32
+ ${projectContext}
33
+
34
+ AVAILABLE AGENTS:
35
+ ${agentList}
36
+
37
+ Based on the project type, tech stack, and structure, recommend exactly ${maxRecommendations} agents that would be most valuable.
38
+ For each agent, provide:
39
+ 1. Agent name (exact match from list)
40
+ 2. Why it's recommended (one sentence)
41
+ 3. Priority: HIGH, MEDIUM, or LOW
42
+
43
+ Format as JSON array: [{"name": "agent-name", "reason": "why", "priority": "HIGH"}]`;
44
+
45
+ try {
46
+ const response = execSync(
47
+ `claude --print "${prompt.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`,
48
+ { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 1024 * 1024 }
49
+ ).trim();
50
+
51
+ return this.parseRecommendations(response);
52
+ } catch (error) {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ buildProjectContext() {
58
+ const context = [];
59
+
60
+ if (this.scanResults.projectType) {
61
+ context.push(`Type: ${this.scanResults.projectType}`);
62
+ }
63
+
64
+ if (this.scanResults.languages && this.scanResults.languages.length > 0) {
65
+ context.push(`Languages: ${this.scanResults.languages.join(', ')}`);
66
+ }
67
+
68
+ if (this.scanResults.frameworks && this.scanResults.frameworks.length > 0) {
69
+ context.push(`Frameworks: ${this.scanResults.frameworks.join(', ')}`);
70
+ }
71
+
72
+ if (this.scanResults.dependencies) {
73
+ const deps = Object.keys(this.scanResults.dependencies).slice(0, 10);
74
+ if (deps.length > 0) {
75
+ context.push(`Key dependencies: ${deps.join(', ')}`);
76
+ }
77
+ }
78
+
79
+ if (this.scanResults.structure) {
80
+ const dirs = Object.keys(this.scanResults.structure).filter(dir => !dir.startsWith('.'));
81
+ context.push(`Structure: ${dirs.join(', ')}`);
82
+ }
83
+
84
+ return context.join('\n');
85
+ }
86
+
87
+ getAgentSummary() {
88
+ return this.agentMetadata.agents
89
+ .map(agent => `- ${agent.name}: ${agent.purpose}`)
90
+ .join('\n');
91
+ }
92
+
93
+ parseRecommendations(response) {
94
+ try {
95
+ // Extract JSON from response
96
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
97
+ if (!jsonMatch) return null;
98
+
99
+ const recommendations = JSON.parse(jsonMatch[0]);
100
+
101
+ // Validate and enhance recommendations
102
+ return recommendations
103
+ .filter(rec => rec.name && rec.reason)
104
+ .map(rec => ({
105
+ name: rec.name,
106
+ reason: rec.reason,
107
+ priority: rec.priority || 'MEDIUM',
108
+ agent: this.agentMetadata.agents.find(a => a.name === rec.name)
109
+ }))
110
+ .filter(rec => rec.agent);
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+
116
+ async getWorkflowBasedRecommendations() {
117
+ if (!this.hasClaudeCode) return null;
118
+
119
+ const workflows = [
120
+ { name: 'api-development', agents: ['backend', 'api-documenter', 'database-optimizer'] },
121
+ { name: 'frontend-development', agents: ['frontend', 'shadcn', 'performance-engineer'] },
122
+ { name: 'full-stack', agents: ['backend', 'frontend', 'database-admin', 'deployment-engineer'] },
123
+ { name: 'data-science', agents: ['data-scientist', 'ml-engineer', 'python-pro'] },
124
+ { name: 'devops', agents: ['deployment-engineer', 'cloud-architect', 'terraform-specialist'] }
125
+ ];
126
+
127
+ const prompt = `Based on this project context, which development workflow best matches?
128
+
129
+ ${this.buildProjectContext()}
130
+
131
+ Choose ONE from: ${workflows.map(w => w.name).join(', ')}
132
+ Output only the workflow name.`;
133
+
134
+ try {
135
+ const workflow = execSync(
136
+ `claude --print "${prompt.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`,
137
+ { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] }
138
+ ).trim().toLowerCase();
139
+
140
+ const matched = workflows.find(w => w.name === workflow);
141
+ if (matched) {
142
+ return {
143
+ workflow: matched.name,
144
+ agents: matched.agents.map(name =>
145
+ this.agentMetadata.agents.find(a => a.name === name)
146
+ ).filter(Boolean)
147
+ };
148
+ }
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+ }
154
+
155
+ module.exports = AIAgentSelector;
@@ -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 8 core 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 8 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.