claude-code-templates 1.14.14 ā 1.14.16
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/README.md +7 -7
- package/bin/create-claude-config.js +5 -4
- package/package.json +1 -1
- package/src/health-check.js +310 -0
- package/src/index.js +451 -63
package/README.md
CHANGED
|
@@ -51,14 +51,14 @@ npx claude-code-templates@latest --health-check
|
|
|
51
51
|
|
|
52
52
|
## š Documentation
|
|
53
53
|
|
|
54
|
-
**[š Complete Documentation](https://aitmpl.com/
|
|
54
|
+
**[š Complete Documentation](https://docs.aitmpl.com/)** - Comprehensive guides, examples, and API reference
|
|
55
55
|
|
|
56
56
|
Quick links:
|
|
57
|
-
- [Getting Started](https://aitmpl.com/
|
|
58
|
-
- [Project Setup](https://aitmpl.com/
|
|
59
|
-
- [Analytics Dashboard](https://aitmpl.com/
|
|
60
|
-
- [Individual Components](https://aitmpl.com/
|
|
61
|
-
- [CLI Options](https://aitmpl.com/
|
|
57
|
+
- [Getting Started](https://docs.aitmpl.com/docs/intro) - Installation and first steps
|
|
58
|
+
- [Project Setup](https://docs.aitmpl.com/docs/project-setup/interactive-setup) - Configure your projects
|
|
59
|
+
- [Analytics Dashboard](https://docs.aitmpl.com/docs/analytics/overview) - Real-time monitoring
|
|
60
|
+
- [Individual Components](https://docs.aitmpl.com/docs/components/overview) - Agents, Commands, MCPs
|
|
61
|
+
- [CLI Options](https://docs.aitmpl.com/docs/cli-options) - All available commands
|
|
62
62
|
|
|
63
63
|
## š¤ Contributing
|
|
64
64
|
|
|
@@ -71,7 +71,7 @@ MIT License - see the [LICENSE](LICENSE) file for details.
|
|
|
71
71
|
## š Links
|
|
72
72
|
|
|
73
73
|
- **š Browse Components**: [aitmpl.com](https://aitmpl.com)
|
|
74
|
-
- **š Documentation**: [aitmpl.com
|
|
74
|
+
- **š Documentation**: [docs.aitmpl.com](https://docs.aitmpl.com)
|
|
75
75
|
- **š Issues**: [GitHub Issues](https://github.com/davila7/claude-code-templates/issues)
|
|
76
76
|
- **š¬ Discussions**: [GitHub Discussions](https://github.com/davila7/claude-code-templates/discussions)
|
|
77
77
|
|
|
@@ -55,10 +55,11 @@ program
|
|
|
55
55
|
.option('--analytics', 'launch real-time Claude Code analytics dashboard')
|
|
56
56
|
.option('--chats, --agents', 'launch Claude Code chats/agents dashboard (opens directly to conversations)')
|
|
57
57
|
.option('--health-check, --health, --check, --verify', 'run comprehensive health check to verify Claude Code setup')
|
|
58
|
-
.option('--agent <agent>', 'install specific agent component')
|
|
59
|
-
.option('--command <command>', 'install specific command component')
|
|
60
|
-
.option('--mcp <mcp>', 'install specific MCP component')
|
|
61
|
-
.option('--workflow <workflow>', 'install workflow from hash (
|
|
58
|
+
.option('--agent <agent>', 'install specific agent component (supports comma-separated values)')
|
|
59
|
+
.option('--command <command>', 'install specific command component (supports comma-separated values)')
|
|
60
|
+
.option('--mcp <mcp>', 'install specific MCP component (supports comma-separated values)')
|
|
61
|
+
.option('--workflow <workflow>', 'install workflow from hash (#hash) OR workflow YAML (base64 encoded) when used with --agent/--command/--mcp')
|
|
62
|
+
.option('--prompt <prompt>', 'execute the provided prompt in Claude Code after installation')
|
|
62
63
|
.action(async (options) => {
|
|
63
64
|
try {
|
|
64
65
|
await createClaudeConfig(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.16",
|
|
4
4
|
"description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/health-check.js
CHANGED
|
@@ -16,7 +16,9 @@ class HealthChecker {
|
|
|
16
16
|
system: [],
|
|
17
17
|
claudeCode: [],
|
|
18
18
|
project: [],
|
|
19
|
+
agents: [],
|
|
19
20
|
commands: [],
|
|
21
|
+
mcps: [],
|
|
20
22
|
hooks: []
|
|
21
23
|
};
|
|
22
24
|
this.totalChecks = 0;
|
|
@@ -42,6 +44,14 @@ class HealthChecker {
|
|
|
42
44
|
await this.checkProjectSetupWithSpinner();
|
|
43
45
|
await this.sleep(3000);
|
|
44
46
|
|
|
47
|
+
// Agents check
|
|
48
|
+
await this.checkAgentsWithSpinner();
|
|
49
|
+
await this.sleep(3000);
|
|
50
|
+
|
|
51
|
+
// MCP servers check
|
|
52
|
+
await this.checkMCPServersWithSpinner();
|
|
53
|
+
await this.sleep(3000);
|
|
54
|
+
|
|
45
55
|
// Custom commands check
|
|
46
56
|
await this.checkCustomCommandsWithSpinner();
|
|
47
57
|
await this.sleep(3000);
|
|
@@ -165,6 +175,54 @@ class HealthChecker {
|
|
|
165
175
|
localSettingsSpinner.succeed(`${this.getStatusIcon(localSettingsInfo.status)} Local Settings ā ${localSettingsInfo.message}`);
|
|
166
176
|
}
|
|
167
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Check agents with spinner and immediate results
|
|
180
|
+
*/
|
|
181
|
+
async checkAgentsWithSpinner() {
|
|
182
|
+
console.log(chalk.cyan('\nāāāāāāāāāāāā'));
|
|
183
|
+
console.log(chalk.cyan('ā AGENTS ā'));
|
|
184
|
+
console.log(chalk.cyan('āāāāāāāāāāāā'));
|
|
185
|
+
|
|
186
|
+
// Project agents
|
|
187
|
+
const projectSpinner = ora('Scanning Project Agents...').start();
|
|
188
|
+
const projectAgents = this.checkProjectAgents();
|
|
189
|
+
this.addResult('agents', 'Project Agents', projectAgents.status, projectAgents.message);
|
|
190
|
+
projectSpinner.succeed(`${this.getStatusIcon(projectAgents.status)} Project Agents ā ${projectAgents.message}`);
|
|
191
|
+
|
|
192
|
+
// Personal agents
|
|
193
|
+
const personalSpinner = ora('Scanning Personal Agents...').start();
|
|
194
|
+
const personalAgents = this.checkPersonalAgents();
|
|
195
|
+
this.addResult('agents', 'Personal Agents', personalAgents.status, personalAgents.message);
|
|
196
|
+
personalSpinner.succeed(`${this.getStatusIcon(personalAgents.status)} Personal Agents ā ${personalAgents.message}`);
|
|
197
|
+
|
|
198
|
+
// Agent syntax validation
|
|
199
|
+
const syntaxSpinner = ora('Validating Agent Syntax...').start();
|
|
200
|
+
const syntaxInfo = this.checkAgentSyntax();
|
|
201
|
+
this.addResult('agents', 'Agent Syntax', syntaxInfo.status, syntaxInfo.message);
|
|
202
|
+
syntaxSpinner.succeed(`${this.getStatusIcon(syntaxInfo.status)} Agent Syntax ā ${syntaxInfo.message}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check MCP servers with spinner and immediate results
|
|
207
|
+
*/
|
|
208
|
+
async checkMCPServersWithSpinner() {
|
|
209
|
+
console.log(chalk.cyan('\nāāāāāāāāāāāāāāāā'));
|
|
210
|
+
console.log(chalk.cyan('ā MCP SERVERS ā'));
|
|
211
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāā'));
|
|
212
|
+
|
|
213
|
+
// Project MCP configuration
|
|
214
|
+
const projectMCPSpinner = ora('Scanning Project MCP Configuration...').start();
|
|
215
|
+
const projectMCP = this.checkProjectMCPConfiguration();
|
|
216
|
+
this.addResult('mcps', 'Project MCP Config', projectMCP.status, projectMCP.message);
|
|
217
|
+
projectMCPSpinner.succeed(`${this.getStatusIcon(projectMCP.status)} Project MCP Config ā ${projectMCP.message}`);
|
|
218
|
+
|
|
219
|
+
// MCP configuration validation
|
|
220
|
+
const mcpValidationSpinner = ora('Validating MCP Configuration...').start();
|
|
221
|
+
const mcpValidation = this.checkMCPConfigurationSyntax();
|
|
222
|
+
this.addResult('mcps', 'MCP Config Syntax', mcpValidation.status, mcpValidation.message);
|
|
223
|
+
mcpValidationSpinner.succeed(`${this.getStatusIcon(mcpValidation.status)} MCP Config Syntax ā ${mcpValidation.message}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
168
226
|
/**
|
|
169
227
|
* Check custom commands with spinner and immediate results
|
|
170
228
|
*/
|
|
@@ -552,6 +610,123 @@ class HealthChecker {
|
|
|
552
610
|
}
|
|
553
611
|
}
|
|
554
612
|
|
|
613
|
+
checkProjectMCPConfiguration() {
|
|
614
|
+
const currentDir = process.cwd();
|
|
615
|
+
const mcpConfigPath = path.join(currentDir, '.mcp.json');
|
|
616
|
+
|
|
617
|
+
if (fs.existsSync(mcpConfigPath)) {
|
|
618
|
+
try {
|
|
619
|
+
const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
|
|
620
|
+
|
|
621
|
+
if (mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object') {
|
|
622
|
+
const serverCount = Object.keys(mcpConfig.mcpServers).length;
|
|
623
|
+
return {
|
|
624
|
+
status: 'pass',
|
|
625
|
+
message: `${serverCount} MCP servers configured in .mcp.json`
|
|
626
|
+
};
|
|
627
|
+
} else {
|
|
628
|
+
return {
|
|
629
|
+
status: 'warn',
|
|
630
|
+
message: 'No mcpServers found in .mcp.json'
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
} catch (error) {
|
|
634
|
+
return {
|
|
635
|
+
status: 'fail',
|
|
636
|
+
message: 'Invalid JSON syntax in .mcp.json'
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
return {
|
|
641
|
+
status: 'warn',
|
|
642
|
+
message: 'No project MCP configuration found (.mcp.json)'
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
checkMCPConfigurationSyntax() {
|
|
648
|
+
const configPaths = [
|
|
649
|
+
path.join(process.cwd(), '.mcp.json')
|
|
650
|
+
];
|
|
651
|
+
|
|
652
|
+
let totalServers = 0;
|
|
653
|
+
let validServers = 0;
|
|
654
|
+
let invalidServers = 0;
|
|
655
|
+
const issues = [];
|
|
656
|
+
|
|
657
|
+
for (const configPath of configPaths) {
|
|
658
|
+
if (fs.existsSync(configPath)) {
|
|
659
|
+
try {
|
|
660
|
+
const mcpConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
661
|
+
|
|
662
|
+
if (mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object') {
|
|
663
|
+
const servers = mcpConfig.mcpServers;
|
|
664
|
+
|
|
665
|
+
for (const [serverName, serverConfig] of Object.entries(servers)) {
|
|
666
|
+
totalServers++;
|
|
667
|
+
|
|
668
|
+
// Validate server configuration structure
|
|
669
|
+
if (!serverConfig || typeof serverConfig !== 'object') {
|
|
670
|
+
invalidServers++;
|
|
671
|
+
issues.push(`Invalid server config for ${serverName} in ${path.basename(configPath)}`);
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Check required fields
|
|
676
|
+
if (!serverConfig.command) {
|
|
677
|
+
invalidServers++;
|
|
678
|
+
issues.push(`Missing command for ${serverName} in ${path.basename(configPath)}`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Optional: Check if args is array when present
|
|
683
|
+
if (serverConfig.args && !Array.isArray(serverConfig.args)) {
|
|
684
|
+
invalidServers++;
|
|
685
|
+
issues.push(`Invalid args format for ${serverName} in ${path.basename(configPath)} (should be array)`);
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Optional: Check if env is object when present
|
|
690
|
+
if (serverConfig.env && typeof serverConfig.env !== 'object') {
|
|
691
|
+
invalidServers++;
|
|
692
|
+
issues.push(`Invalid env format for ${serverName} in ${path.basename(configPath)} (should be object)`);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
validServers++;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
} catch (error) {
|
|
700
|
+
// JSON parsing error already handled in other checks
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (totalServers === 0) {
|
|
706
|
+
return {
|
|
707
|
+
status: 'warn',
|
|
708
|
+
message: 'No MCP servers configured'
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (invalidServers === 0) {
|
|
713
|
+
return {
|
|
714
|
+
status: 'pass',
|
|
715
|
+
message: `All ${totalServers} MCP server configurations are valid`
|
|
716
|
+
};
|
|
717
|
+
} else if (validServers > 0) {
|
|
718
|
+
return {
|
|
719
|
+
status: 'warn',
|
|
720
|
+
message: `${validServers}/${totalServers} MCP servers valid, ${invalidServers} issues found`
|
|
721
|
+
};
|
|
722
|
+
} else {
|
|
723
|
+
return {
|
|
724
|
+
status: 'fail',
|
|
725
|
+
message: `All ${totalServers} MCP server configurations have issues`
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
555
730
|
checkProjectCommands() {
|
|
556
731
|
const currentDir = process.cwd();
|
|
557
732
|
const commandsDir = path.join(currentDir, '.claude', 'commands');
|
|
@@ -625,6 +800,129 @@ class HealthChecker {
|
|
|
625
800
|
}
|
|
626
801
|
}
|
|
627
802
|
|
|
803
|
+
checkProjectAgents() {
|
|
804
|
+
const currentDir = process.cwd();
|
|
805
|
+
const agentsDir = path.join(currentDir, '.claude', 'agents');
|
|
806
|
+
|
|
807
|
+
if (fs.existsSync(agentsDir)) {
|
|
808
|
+
const agents = this.countAgentsRecursively(agentsDir);
|
|
809
|
+
return {
|
|
810
|
+
status: 'pass',
|
|
811
|
+
message: `${agents} agents found in .claude/agents/`
|
|
812
|
+
};
|
|
813
|
+
} else {
|
|
814
|
+
return {
|
|
815
|
+
status: 'warn',
|
|
816
|
+
message: 'No project agents directory found'
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
checkPersonalAgents() {
|
|
822
|
+
const homeDir = os.homedir();
|
|
823
|
+
const agentsDir = path.join(homeDir, '.claude', 'agents');
|
|
824
|
+
|
|
825
|
+
if (fs.existsSync(agentsDir)) {
|
|
826
|
+
const agents = this.countAgentsRecursively(agentsDir);
|
|
827
|
+
return {
|
|
828
|
+
status: 'pass',
|
|
829
|
+
message: `${agents} agents found in ~/.claude/agents/`
|
|
830
|
+
};
|
|
831
|
+
} else {
|
|
832
|
+
return {
|
|
833
|
+
status: 'warn',
|
|
834
|
+
message: 'No personal agents directory found'
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
countAgentsRecursively(dir) {
|
|
840
|
+
let count = 0;
|
|
841
|
+
try {
|
|
842
|
+
const items = fs.readdirSync(dir);
|
|
843
|
+
for (const item of items) {
|
|
844
|
+
const itemPath = path.join(dir, item);
|
|
845
|
+
const stat = fs.statSync(itemPath);
|
|
846
|
+
if (stat.isDirectory()) {
|
|
847
|
+
count += this.countAgentsRecursively(itemPath);
|
|
848
|
+
} else if (item.endsWith('.md')) {
|
|
849
|
+
count++;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
} catch (error) {
|
|
853
|
+
// Handle permission or access errors
|
|
854
|
+
}
|
|
855
|
+
return count;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
checkAgentSyntax() {
|
|
859
|
+
const currentDir = process.cwd();
|
|
860
|
+
const agentsDir = path.join(currentDir, '.claude', 'agents');
|
|
861
|
+
|
|
862
|
+
if (!fs.existsSync(agentsDir)) {
|
|
863
|
+
return {
|
|
864
|
+
status: 'warn',
|
|
865
|
+
message: 'No agents to validate'
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const agents = this.getAgentFilesRecursively(agentsDir);
|
|
870
|
+
let issuesFound = 0;
|
|
871
|
+
let agentsChecked = 0;
|
|
872
|
+
|
|
873
|
+
for (const agentPath of agents) {
|
|
874
|
+
try {
|
|
875
|
+
const content = fs.readFileSync(agentPath, 'utf8');
|
|
876
|
+
agentsChecked++;
|
|
877
|
+
|
|
878
|
+
// Check for frontmatter (agent metadata)
|
|
879
|
+
if (!content.includes('---') || !content.includes('name:') || !content.includes('description:')) {
|
|
880
|
+
issuesFound++;
|
|
881
|
+
}
|
|
882
|
+
} catch (error) {
|
|
883
|
+
issuesFound++;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (agentsChecked === 0) {
|
|
888
|
+
return {
|
|
889
|
+
status: 'warn',
|
|
890
|
+
message: 'No agents to validate'
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (issuesFound === 0) {
|
|
895
|
+
return {
|
|
896
|
+
status: 'pass',
|
|
897
|
+
message: `All ${agentsChecked} agents have proper syntax`
|
|
898
|
+
};
|
|
899
|
+
} else {
|
|
900
|
+
return {
|
|
901
|
+
status: 'warn',
|
|
902
|
+
message: `${issuesFound}/${agentsChecked} agents missing proper frontmatter`
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
getAgentFilesRecursively(dir) {
|
|
908
|
+
let files = [];
|
|
909
|
+
try {
|
|
910
|
+
const items = fs.readdirSync(dir);
|
|
911
|
+
for (const item of items) {
|
|
912
|
+
const itemPath = path.join(dir, item);
|
|
913
|
+
const stat = fs.statSync(itemPath);
|
|
914
|
+
if (stat.isDirectory()) {
|
|
915
|
+
files = files.concat(this.getAgentFilesRecursively(itemPath));
|
|
916
|
+
} else if (item.endsWith('.md')) {
|
|
917
|
+
files.push(itemPath);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
} catch (error) {
|
|
921
|
+
// Handle permission or access errors
|
|
922
|
+
}
|
|
923
|
+
return files;
|
|
924
|
+
}
|
|
925
|
+
|
|
628
926
|
checkUserHooks() {
|
|
629
927
|
const homeDir = os.homedir();
|
|
630
928
|
const settingsPath = path.join(homeDir, '.claude', 'settings.json');
|
|
@@ -1041,7 +1339,9 @@ class HealthChecker {
|
|
|
1041
1339
|
...this.results.system,
|
|
1042
1340
|
...this.results.claudeCode,
|
|
1043
1341
|
...this.results.project,
|
|
1342
|
+
...this.results.agents,
|
|
1044
1343
|
...this.results.commands,
|
|
1344
|
+
...this.results.mcps,
|
|
1045
1345
|
...this.results.hooks
|
|
1046
1346
|
];
|
|
1047
1347
|
|
|
@@ -1052,6 +1352,16 @@ class HealthChecker {
|
|
|
1052
1352
|
recommendations.push('Consider switching to Zsh for better autocompletion and features');
|
|
1053
1353
|
} else if (result.check === 'Command Syntax' && result.message.includes('$ARGUMENTS')) {
|
|
1054
1354
|
recommendations.push('Add $ARGUMENTS placeholder to command files for proper parameter handling');
|
|
1355
|
+
} else if (result.check === 'Agent Syntax' && result.message.includes('frontmatter')) {
|
|
1356
|
+
recommendations.push('Add proper frontmatter (name, description) to agent files');
|
|
1357
|
+
} else if (result.check === 'Project Agents' && result.message.includes('No project agents directory')) {
|
|
1358
|
+
recommendations.push('Create .claude/agents/ directory to organize your custom agents');
|
|
1359
|
+
} else if (result.check === 'Project MCP Config' && result.message.includes('No project MCP configuration')) {
|
|
1360
|
+
recommendations.push('Create .mcp.json file to configure MCP servers for your project');
|
|
1361
|
+
} else if (result.check === 'MCP Config Syntax' && result.message.includes('Invalid JSON')) {
|
|
1362
|
+
recommendations.push('Fix JSON syntax errors in MCP configuration files');
|
|
1363
|
+
} else if (result.check === 'MCP Config Syntax' && result.message.includes('Missing command')) {
|
|
1364
|
+
recommendations.push('Add missing command fields to MCP server configurations');
|
|
1055
1365
|
} else if (result.check === 'Local Hooks' && result.message.includes('Invalid JSON')) {
|
|
1056
1366
|
recommendations.push('Fix JSON syntax error in .claude/settings.local.json');
|
|
1057
1367
|
}
|
package/src/index.js
CHANGED
|
@@ -90,23 +90,17 @@ async function showMainMenu() {
|
|
|
90
90
|
async function createClaudeConfig(options = {}) {
|
|
91
91
|
const targetDir = options.directory || process.cwd();
|
|
92
92
|
|
|
93
|
-
// Handle
|
|
94
|
-
if (options.agent) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
await installIndividualCommand(options.command, targetDir, options);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (options.mcp) {
|
|
105
|
-
await installIndividualMCP(options.mcp, targetDir, options);
|
|
93
|
+
// Handle multiple components installation (new approach)
|
|
94
|
+
if (options.agent || options.command || options.mcp) {
|
|
95
|
+
// If --workflow is used with components, treat it as YAML
|
|
96
|
+
if (options.workflow) {
|
|
97
|
+
options.yaml = options.workflow;
|
|
98
|
+
}
|
|
99
|
+
await installMultipleComponents(options, targetDir);
|
|
106
100
|
return;
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
// Handle workflow installation
|
|
103
|
+
// Handle workflow installation (hash-based)
|
|
110
104
|
if (options.workflow) {
|
|
111
105
|
await installWorkflow(options.workflow, targetDir, options);
|
|
112
106
|
return;
|
|
@@ -271,7 +265,7 @@ async function createClaudeConfig(options = {}) {
|
|
|
271
265
|
console.log(chalk.white(' 3. Start using Claude Code with: claude'));
|
|
272
266
|
console.log('');
|
|
273
267
|
console.log(chalk.blue('š View all available templates at: https://aitmpl.com/'));
|
|
274
|
-
console.log(chalk.blue('š Read the complete documentation at: https://aitmpl.com/
|
|
268
|
+
console.log(chalk.blue('š Read the complete documentation at: https://docs.aitmpl.com/'));
|
|
275
269
|
|
|
276
270
|
if (config.language !== 'common') {
|
|
277
271
|
console.log(chalk.yellow(`š” Language-specific features for ${config.language} have been configured`));
|
|
@@ -304,6 +298,11 @@ async function createClaudeConfig(options = {}) {
|
|
|
304
298
|
if (!options.dryRun) {
|
|
305
299
|
await runPostInstallationValidation(targetDir, templateConfig);
|
|
306
300
|
}
|
|
301
|
+
|
|
302
|
+
// Handle prompt execution if provided
|
|
303
|
+
if (options.prompt) {
|
|
304
|
+
await handlePromptExecution(options.prompt, targetDir);
|
|
305
|
+
}
|
|
307
306
|
}
|
|
308
307
|
|
|
309
308
|
// Individual component installation functions
|
|
@@ -597,6 +596,122 @@ async function getAvailableAgentsFromGitHub() {
|
|
|
597
596
|
}
|
|
598
597
|
}
|
|
599
598
|
|
|
599
|
+
/**
|
|
600
|
+
* Install multiple components with optional YAML workflow
|
|
601
|
+
*/
|
|
602
|
+
async function installMultipleComponents(options, targetDir) {
|
|
603
|
+
console.log(chalk.blue('š§ Installing multiple components...'));
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const components = {
|
|
607
|
+
agents: [],
|
|
608
|
+
commands: [],
|
|
609
|
+
mcps: []
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// Parse comma-separated values for each component type
|
|
613
|
+
if (options.agent) {
|
|
614
|
+
const agentsInput = Array.isArray(options.agent) ? options.agent.join(',') : options.agent;
|
|
615
|
+
components.agents = agentsInput.split(',').map(a => a.trim()).filter(a => a);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (options.command) {
|
|
619
|
+
const commandsInput = Array.isArray(options.command) ? options.command.join(',') : options.command;
|
|
620
|
+
components.commands = commandsInput.split(',').map(c => c.trim()).filter(c => c);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (options.mcp) {
|
|
624
|
+
const mcpsInput = Array.isArray(options.mcp) ? options.mcp.join(',') : options.mcp;
|
|
625
|
+
components.mcps = mcpsInput.split(',').map(m => m.trim()).filter(m => m);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const totalComponents = components.agents.length + components.commands.length + components.mcps.length;
|
|
629
|
+
|
|
630
|
+
if (totalComponents === 0) {
|
|
631
|
+
console.log(chalk.yellow('ā ļø No components specified to install.'));
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
console.log(chalk.cyan(`š¦ Installing ${totalComponents} components:`));
|
|
636
|
+
console.log(chalk.gray(` Agents: ${components.agents.length}`));
|
|
637
|
+
console.log(chalk.gray(` Commands: ${components.commands.length}`));
|
|
638
|
+
console.log(chalk.gray(` MCPs: ${components.mcps.length}`));
|
|
639
|
+
|
|
640
|
+
// Install agents
|
|
641
|
+
for (const agent of components.agents) {
|
|
642
|
+
console.log(chalk.gray(` Installing agent: ${agent}`));
|
|
643
|
+
await installIndividualAgent(agent, targetDir, { ...options, silent: true });
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Install commands
|
|
647
|
+
for (const command of components.commands) {
|
|
648
|
+
console.log(chalk.gray(` Installing command: ${command}`));
|
|
649
|
+
await installIndividualCommand(command, targetDir, { ...options, silent: true });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Install MCPs
|
|
653
|
+
for (const mcp of components.mcps) {
|
|
654
|
+
console.log(chalk.gray(` Installing MCP: ${mcp}`));
|
|
655
|
+
await installIndividualMCP(mcp, targetDir, { ...options, silent: true });
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Handle YAML workflow if provided
|
|
659
|
+
if (options.yaml) {
|
|
660
|
+
console.log(chalk.blue('\nš Processing workflow YAML...'));
|
|
661
|
+
|
|
662
|
+
try {
|
|
663
|
+
// Decode the YAML from base64
|
|
664
|
+
const yamlContent = Buffer.from(options.yaml, 'base64').toString('utf8');
|
|
665
|
+
|
|
666
|
+
// Parse workflow name from YAML (try to extract from name: field)
|
|
667
|
+
let workflowName = 'custom-workflow';
|
|
668
|
+
const nameMatch = yamlContent.match(/name:\s*["']?([^"'\n]+)["']?/);
|
|
669
|
+
if (nameMatch) {
|
|
670
|
+
workflowName = nameMatch[1].trim().replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Save YAML to workflows directory
|
|
674
|
+
const workflowsDir = path.join(targetDir, '.claude', 'workflows');
|
|
675
|
+
const workflowFile = path.join(workflowsDir, `${workflowName}.yaml`);
|
|
676
|
+
|
|
677
|
+
await fs.ensureDir(workflowsDir);
|
|
678
|
+
await fs.writeFile(workflowFile, yamlContent, 'utf8');
|
|
679
|
+
|
|
680
|
+
console.log(chalk.green(`ā
Workflow YAML saved: ${path.relative(targetDir, workflowFile)}`));
|
|
681
|
+
|
|
682
|
+
} catch (yamlError) {
|
|
683
|
+
console.log(chalk.red(`ā Error processing YAML: ${yamlError.message}`));
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
console.log(chalk.green(`\nā
Successfully installed ${totalComponents} components!`));
|
|
688
|
+
console.log(chalk.cyan(`š Components installed to: .claude/`));
|
|
689
|
+
|
|
690
|
+
if (options.yaml) {
|
|
691
|
+
console.log(chalk.cyan(`š Workflow file created in: .claude/workflows/`));
|
|
692
|
+
console.log(chalk.cyan(`š Use the workflow file with Claude Code to execute the complete setup`));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Track installation
|
|
696
|
+
trackingService.trackDownload('multi-component', 'batch', {
|
|
697
|
+
installation_type: 'multi-component',
|
|
698
|
+
agents_count: components.agents.length,
|
|
699
|
+
commands_count: components.commands.length,
|
|
700
|
+
mcps_count: components.mcps.length,
|
|
701
|
+
has_yaml: !!options.yaml,
|
|
702
|
+
target_directory: path.relative(process.cwd(), targetDir)
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// Handle prompt execution if provided
|
|
706
|
+
if (options.prompt) {
|
|
707
|
+
await handlePromptExecution(options.prompt, targetDir);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
} catch (error) {
|
|
711
|
+
console.log(chalk.red(`ā Error installing components: ${error.message}`));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
600
715
|
/**
|
|
601
716
|
* Show available agents organized by category
|
|
602
717
|
*/
|
|
@@ -673,16 +788,48 @@ async function installWorkflow(workflowHash, targetDir, options) {
|
|
|
673
788
|
console.log(chalk.gray(` Commands: ${commands.length}`));
|
|
674
789
|
console.log(chalk.gray(` MCPs: ${mcps.length}`));
|
|
675
790
|
|
|
676
|
-
// Install
|
|
677
|
-
|
|
678
|
-
console.log(chalk.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
791
|
+
// Install components from workflow data (not from GitHub)
|
|
792
|
+
if (workflowData.components) {
|
|
793
|
+
console.log(chalk.blue(`š¦ Installing components from workflow package...`));
|
|
794
|
+
|
|
795
|
+
// Install agents
|
|
796
|
+
if (workflowData.components.agent) {
|
|
797
|
+
for (const agent of workflowData.components.agent) {
|
|
798
|
+
console.log(chalk.gray(` Installing agent: ${agent.name}`));
|
|
799
|
+
await installComponentFromWorkflow(agent, 'agent', targetDir, options);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Install commands
|
|
804
|
+
if (workflowData.components.command) {
|
|
805
|
+
for (const command of workflowData.components.command) {
|
|
806
|
+
console.log(chalk.gray(` Installing command: ${command.name}`));
|
|
807
|
+
await installComponentFromWorkflow(command, 'command', targetDir, options);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Install MCPs
|
|
812
|
+
if (workflowData.components.mcp) {
|
|
813
|
+
for (const mcp of workflowData.components.mcp) {
|
|
814
|
+
console.log(chalk.gray(` Installing MCP: ${mcp.name}`));
|
|
815
|
+
await installComponentFromWorkflow(mcp, 'mcp', targetDir, options);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
} else {
|
|
819
|
+
// Fallback to old method for legacy workflows
|
|
820
|
+
console.log(chalk.yellow(`ā ļø Using legacy component installation method...`));
|
|
821
|
+
|
|
822
|
+
// Install agents
|
|
823
|
+
for (const agent of agents) {
|
|
824
|
+
console.log(chalk.gray(` Installing agent: ${agent.name}`));
|
|
825
|
+
await installIndividualAgent(agent.path, targetDir, { ...options, silent: true });
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Install commands
|
|
829
|
+
for (const command of commands) {
|
|
830
|
+
console.log(chalk.gray(` Installing command: ${command.name}`));
|
|
831
|
+
await installIndividualCommand(command.path, targetDir, { ...options, silent: true });
|
|
832
|
+
}
|
|
686
833
|
}
|
|
687
834
|
|
|
688
835
|
// Install MCPs
|
|
@@ -692,7 +839,15 @@ async function installWorkflow(workflowHash, targetDir, options) {
|
|
|
692
839
|
}
|
|
693
840
|
|
|
694
841
|
// Generate and save workflow YAML
|
|
695
|
-
|
|
842
|
+
let yamlContent;
|
|
843
|
+
if (workflowData.yaml) {
|
|
844
|
+
// Use YAML from workflow package
|
|
845
|
+
yamlContent = workflowData.yaml;
|
|
846
|
+
} else {
|
|
847
|
+
// Generate YAML (legacy)
|
|
848
|
+
yamlContent = generateWorkflowYAML(workflowData);
|
|
849
|
+
}
|
|
850
|
+
|
|
696
851
|
const workflowsDir = path.join(targetDir, '.claude', 'workflows');
|
|
697
852
|
const workflowFile = path.join(workflowsDir, `${workflowData.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.yaml`);
|
|
698
853
|
|
|
@@ -716,6 +871,11 @@ async function installWorkflow(workflowHash, targetDir, options) {
|
|
|
716
871
|
target_directory: path.relative(process.cwd(), targetDir)
|
|
717
872
|
});
|
|
718
873
|
|
|
874
|
+
// Handle prompt execution if provided
|
|
875
|
+
if (options.prompt) {
|
|
876
|
+
await handlePromptExecution(options.prompt, targetDir);
|
|
877
|
+
}
|
|
878
|
+
|
|
719
879
|
} catch (error) {
|
|
720
880
|
console.log(chalk.red(`ā Error installing workflow: ${error.message}`));
|
|
721
881
|
|
|
@@ -728,42 +888,107 @@ async function installWorkflow(workflowHash, targetDir, options) {
|
|
|
728
888
|
}
|
|
729
889
|
}
|
|
730
890
|
|
|
891
|
+
/**
|
|
892
|
+
* Decompress string with Unicode support
|
|
893
|
+
*/
|
|
894
|
+
function decompressString(compressed) {
|
|
895
|
+
try {
|
|
896
|
+
// Simple Base64 decoding with Unicode support
|
|
897
|
+
const decoded = Buffer.from(compressed, 'base64').toString('utf8');
|
|
898
|
+
// Convert URI encoded characters back
|
|
899
|
+
return decodeURIComponent(decoded.replace(/(.)/g, function(m, p) {
|
|
900
|
+
let code = p.charCodeAt(0).toString(16).toUpperCase();
|
|
901
|
+
if (code.length < 2) code = '0' + code;
|
|
902
|
+
return '%' + code;
|
|
903
|
+
}));
|
|
904
|
+
} catch (error) {
|
|
905
|
+
throw new Error(`Decompression failed: ${error.message}`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
731
909
|
/**
|
|
732
910
|
* Fetch workflow data from hash
|
|
733
911
|
* In production, this would fetch from a remote workflow registry
|
|
734
912
|
* For now, we'll simulate this functionality
|
|
735
913
|
*/
|
|
736
914
|
async function fetchWorkflowData(hash) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
915
|
+
try {
|
|
916
|
+
// Check if hash contains encoded data (new format: shortHash_encodedData)
|
|
917
|
+
if (hash.includes('_')) {
|
|
918
|
+
console.log(chalk.green('š Decoding workflow from hash...'));
|
|
919
|
+
|
|
920
|
+
const [shortHash, encodedData] = hash.split('_', 2);
|
|
921
|
+
|
|
922
|
+
if (!encodedData) {
|
|
923
|
+
throw new Error('Invalid hash format: missing encoded data');
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Decode compressed data
|
|
927
|
+
let decodedData;
|
|
928
|
+
try {
|
|
929
|
+
// First try to decompress the data (new compressed format)
|
|
930
|
+
const decompressedString = decompressString(encodedData);
|
|
931
|
+
decodedData = JSON.parse(decompressedString);
|
|
932
|
+
} catch (decompressError) {
|
|
933
|
+
// Fallback to old Base64 format for compatibility
|
|
934
|
+
try {
|
|
935
|
+
const decodedString = decodeURIComponent(escape(atob(encodedData)));
|
|
936
|
+
decodedData = JSON.parse(decodedString);
|
|
937
|
+
} catch (base64Error) {
|
|
938
|
+
throw new Error('Failed to decode workflow data from hash');
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Validate decoded data structure
|
|
943
|
+
if (!decodedData.metadata || !decodedData.steps || !decodedData.components) {
|
|
944
|
+
throw new Error('Invalid workflow data structure in hash');
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
console.log(chalk.green('ā
Workflow decoded successfully!'));
|
|
948
|
+
console.log(chalk.gray(` Short hash: ${shortHash}`));
|
|
949
|
+
console.log(chalk.gray(` Timestamp: ${decodedData.timestamp}`));
|
|
950
|
+
console.log(chalk.gray(` Version: ${decodedData.version}`));
|
|
951
|
+
|
|
952
|
+
// Convert to expected format
|
|
953
|
+
return {
|
|
954
|
+
name: decodedData.metadata.name,
|
|
955
|
+
description: decodedData.metadata.description,
|
|
956
|
+
tags: decodedData.metadata.tags || [],
|
|
957
|
+
version: decodedData.version,
|
|
958
|
+
hash: shortHash,
|
|
959
|
+
steps: decodedData.steps,
|
|
960
|
+
components: decodedData.components,
|
|
961
|
+
yaml: decodedData.yaml,
|
|
962
|
+
timestamp: decodedData.timestamp
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Legacy demo workflows for testing
|
|
967
|
+
if (hash === 'demo123' || hash === 'abc123test') {
|
|
968
|
+
console.log(chalk.green('šÆ Demo workflow found! Using sample configuration...'));
|
|
969
|
+
return {
|
|
970
|
+
name: 'Full Stack Development Workflow',
|
|
971
|
+
description: 'Complete workflow for setting up a full-stack development environment with React frontend, Node.js backend, and security auditing',
|
|
972
|
+
tags: ['development', 'fullstack', 'react', 'security'],
|
|
973
|
+
version: '1.0.0',
|
|
974
|
+
hash: hash,
|
|
975
|
+
steps: [
|
|
976
|
+
{
|
|
977
|
+
type: 'agent',
|
|
978
|
+
name: 'frontend-developer',
|
|
979
|
+
path: 'development-team/frontend-developer',
|
|
980
|
+
category: 'development-team',
|
|
981
|
+
description: 'Setup React frontend development environment'
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
type: 'agent',
|
|
985
|
+
name: 'backend-architect',
|
|
986
|
+
path: 'development-team/backend-architect',
|
|
987
|
+
category: 'development-team',
|
|
988
|
+
description: 'Configure Node.js backend architecture'
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
type: 'command',
|
|
767
992
|
name: 'generate-tests',
|
|
768
993
|
path: 'testing/generate-tests',
|
|
769
994
|
category: 'testing',
|
|
@@ -787,13 +1012,101 @@ async function fetchWorkflowData(hash) {
|
|
|
787
1012
|
};
|
|
788
1013
|
}
|
|
789
1014
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1015
|
+
// This is where we would integrate with a workflow registry API
|
|
1016
|
+
// For now, return null to indicate workflow not found for other hashes
|
|
1017
|
+
console.log(chalk.yellow('\nā ļø Workflow registry not yet implemented.'));
|
|
1018
|
+
console.log(chalk.gray('To test with demo workflow, use hash: demo123'));
|
|
1019
|
+
console.log(chalk.gray('Example: --workflow "#demo123"'));
|
|
1020
|
+
|
|
1021
|
+
return null;
|
|
1022
|
+
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
console.error(chalk.red(`ā Error fetching workflow data: ${error.message}`));
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Install component from workflow package data
|
|
1031
|
+
*/
|
|
1032
|
+
async function installComponentFromWorkflow(componentData, type, targetDir, options) {
|
|
1033
|
+
try {
|
|
1034
|
+
let targetPath;
|
|
1035
|
+
let fileName = componentData.name;
|
|
1036
|
+
|
|
1037
|
+
if (type === 'agent') {
|
|
1038
|
+
// Create .claude/agents directory if it doesn't exist
|
|
1039
|
+
const agentsDir = path.join(targetDir, '.claude', 'agents');
|
|
1040
|
+
await fs.ensureDir(agentsDir);
|
|
1041
|
+
|
|
1042
|
+
// For agents, handle category subdirectories
|
|
1043
|
+
if (componentData.category && componentData.category !== 'general') {
|
|
1044
|
+
const categoryDir = path.join(agentsDir, componentData.category);
|
|
1045
|
+
await fs.ensureDir(categoryDir);
|
|
1046
|
+
targetPath = path.join(categoryDir, `${fileName}.md`);
|
|
1047
|
+
} else {
|
|
1048
|
+
targetPath = path.join(agentsDir, `${fileName}.md`);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
} else if (type === 'command') {
|
|
1052
|
+
// Create .claude/commands directory if it doesn't exist
|
|
1053
|
+
const commandsDir = path.join(targetDir, '.claude', 'commands');
|
|
1054
|
+
await fs.ensureDir(commandsDir);
|
|
1055
|
+
targetPath = path.join(commandsDir, `${fileName}.md`);
|
|
1056
|
+
|
|
1057
|
+
} else if (type === 'mcp') {
|
|
1058
|
+
// For MCPs, merge with existing .mcp.json
|
|
1059
|
+
const targetMcpFile = path.join(targetDir, '.mcp.json');
|
|
1060
|
+
let existingConfig = {};
|
|
1061
|
+
|
|
1062
|
+
if (await fs.pathExists(targetMcpFile)) {
|
|
1063
|
+
existingConfig = await fs.readJson(targetMcpFile);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Parse MCP content and merge
|
|
1067
|
+
let mcpConfig;
|
|
1068
|
+
try {
|
|
1069
|
+
mcpConfig = JSON.parse(componentData.content);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
throw new Error(`Failed to parse MCP content for ${componentData.name}: ${error.message}`);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// Remove description field before merging (CLI processing)
|
|
1075
|
+
if (mcpConfig.mcpServers) {
|
|
1076
|
+
for (const serverName in mcpConfig.mcpServers) {
|
|
1077
|
+
if (mcpConfig.mcpServers[serverName] && typeof mcpConfig.mcpServers[serverName] === 'object') {
|
|
1078
|
+
delete mcpConfig.mcpServers[serverName].description;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// Merge configurations
|
|
1084
|
+
const mergedConfig = {
|
|
1085
|
+
...existingConfig,
|
|
1086
|
+
...mcpConfig
|
|
1087
|
+
};
|
|
1088
|
+
|
|
1089
|
+
// Deep merge mcpServers
|
|
1090
|
+
if (existingConfig.mcpServers && mcpConfig.mcpServers) {
|
|
1091
|
+
mergedConfig.mcpServers = {
|
|
1092
|
+
...existingConfig.mcpServers,
|
|
1093
|
+
...mcpConfig.mcpServers
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
await fs.writeJson(targetMcpFile, mergedConfig, { spaces: 2 });
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Write content for agents and commands
|
|
1102
|
+
if (targetPath) {
|
|
1103
|
+
await fs.writeFile(targetPath, componentData.content, 'utf8');
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
console.error(chalk.red(`ā Error installing ${type} "${componentData.name}": ${error.message}`));
|
|
1108
|
+
throw error;
|
|
1109
|
+
}
|
|
797
1110
|
}
|
|
798
1111
|
|
|
799
1112
|
/**
|
|
@@ -947,4 +1260,79 @@ ${workflowData.steps.map((step, index) => `# ${index + 1}. ${step.description} (
|
|
|
947
1260
|
return yaml;
|
|
948
1261
|
}
|
|
949
1262
|
|
|
1263
|
+
/**
|
|
1264
|
+
* Handle prompt execution in Claude Code
|
|
1265
|
+
*/
|
|
1266
|
+
async function handlePromptExecution(prompt, targetDir) {
|
|
1267
|
+
console.log(chalk.blue('\nšÆ Prompt execution requested...'));
|
|
1268
|
+
|
|
1269
|
+
// Ask user if they want to execute the prompt in Claude Code
|
|
1270
|
+
const { shouldExecute } = await inquirer.prompt([{
|
|
1271
|
+
type: 'confirm',
|
|
1272
|
+
name: 'shouldExecute',
|
|
1273
|
+
message: `Do you want to execute this prompt in Claude Code?\n${chalk.cyan(`"${prompt}"`)}`,
|
|
1274
|
+
default: true
|
|
1275
|
+
}]);
|
|
1276
|
+
|
|
1277
|
+
if (!shouldExecute) {
|
|
1278
|
+
console.log(chalk.yellow('ā¹ļø Prompt execution skipped by user.'));
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
console.log(chalk.blue('š Preparing to launch Claude Code with your prompt...'));
|
|
1283
|
+
|
|
1284
|
+
try {
|
|
1285
|
+
// Check if claude command is available in PATH
|
|
1286
|
+
const { spawn } = require('child_process');
|
|
1287
|
+
const open = require('open');
|
|
1288
|
+
|
|
1289
|
+
// First try to execute claude command directly
|
|
1290
|
+
const claudeProcess = spawn('claude', [prompt], {
|
|
1291
|
+
cwd: targetDir,
|
|
1292
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
1293
|
+
shell: true
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
claudeProcess.on('error', async (error) => {
|
|
1297
|
+
if (error.code === 'ENOENT') {
|
|
1298
|
+
// Claude command not found, try alternative approaches
|
|
1299
|
+
console.log(chalk.yellow('ā ļø Claude Code CLI not found in PATH.'));
|
|
1300
|
+
console.log(chalk.blue('š” Alternative ways to execute your prompt:'));
|
|
1301
|
+
console.log(chalk.gray(' 1. Install Claude Code CLI: https://claude.ai/code'));
|
|
1302
|
+
console.log(chalk.gray(' 2. Copy and paste this prompt in Claude Code interface:'));
|
|
1303
|
+
console.log(chalk.cyan(`\n "${prompt}"\n`));
|
|
1304
|
+
|
|
1305
|
+
// Ask if user wants to open Claude Code web interface
|
|
1306
|
+
const { openWeb } = await inquirer.prompt([{
|
|
1307
|
+
type: 'confirm',
|
|
1308
|
+
name: 'openWeb',
|
|
1309
|
+
message: 'Would you like to open Claude Code in your browser?',
|
|
1310
|
+
default: true
|
|
1311
|
+
}]);
|
|
1312
|
+
|
|
1313
|
+
if (openWeb) {
|
|
1314
|
+
await open('https://claude.ai/code');
|
|
1315
|
+
console.log(chalk.green('ā
Claude Code opened in your browser!'));
|
|
1316
|
+
console.log(chalk.cyan(`Don't forget to paste your prompt: "${prompt}"`));
|
|
1317
|
+
}
|
|
1318
|
+
} else {
|
|
1319
|
+
throw error;
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
claudeProcess.on('close', (code) => {
|
|
1324
|
+
if (code === 0) {
|
|
1325
|
+
console.log(chalk.green('ā
Claude Code executed successfully!'));
|
|
1326
|
+
} else if (code !== null) {
|
|
1327
|
+
console.log(chalk.yellow(`ā ļø Claude Code exited with code ${code}`));
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
console.log(chalk.red(`ā Error executing prompt: ${error.message}`));
|
|
1333
|
+
console.log(chalk.blue('š” You can manually execute this prompt in Claude Code:'));
|
|
1334
|
+
console.log(chalk.cyan(`"${prompt}"`));
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
950
1338
|
module.exports = { createClaudeConfig, showMainMenu };
|