claude-code-templates 1.19.1 ā 1.20.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.
- package/README.md +46 -0
- package/bin/create-claude-config.js +5 -1
- package/package.json +1 -1
- package/src/index.js +25 -0
- package/src/sdk/global-agent-manager.js +646 -0
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ npx claude-code-templates@latest --health-check
|
|
|
28
28
|
- **š Real-time Analytics** - Monitor Claude Code sessions with live state detection and performance metrics
|
|
29
29
|
- **š Health Check** - Comprehensive system validation with actionable recommendations
|
|
30
30
|
- **š§© Individual Components** - Install specialized agents, commands, and MCPs individually
|
|
31
|
+
- **š Global Agents** - Create AI agents accessible from anywhere using Claude Code SDK
|
|
31
32
|
|
|
32
33
|
## šÆ What You Get
|
|
33
34
|
|
|
@@ -49,6 +50,51 @@ npx claude-code-templates@latest --health-check
|
|
|
49
50
|
| **Go** | Gin, Echo, Fiber | š§ Coming Soon |
|
|
50
51
|
| **Rust** | Axum, Warp, Actix | š§ Coming Soon |
|
|
51
52
|
|
|
53
|
+
## š Global Agents (Claude Code SDK Integration)
|
|
54
|
+
|
|
55
|
+
Create AI agents that can be executed from anywhere using the Claude Code SDK:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Create a global agent (one-time setup)
|
|
59
|
+
npx claude-code-templates@latest --create-agent customer-support
|
|
60
|
+
|
|
61
|
+
# Use the agent from anywhere
|
|
62
|
+
customer-support "Help me with ticket #12345"
|
|
63
|
+
sre-logs "Analyze error patterns in app.log"
|
|
64
|
+
code-reviewer "Review this PR for security issues"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Available Global Agents
|
|
68
|
+
|
|
69
|
+
| Agent | Usage | Description |
|
|
70
|
+
|-------|-------|-------------|
|
|
71
|
+
| `customer-support` | `customer-support "query"` | AI customer support specialist |
|
|
72
|
+
| `api-security-audit` | `api-security-audit "analyze endpoints"` | Security auditing for APIs |
|
|
73
|
+
| `react-performance-optimization` | `react-performance-optimization "optimize components"` | React performance expert |
|
|
74
|
+
| `database-optimization` | `database-optimization "improve queries"` | Database performance tuning |
|
|
75
|
+
|
|
76
|
+
### Global Agent Management
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# List installed global agents
|
|
80
|
+
npx claude-code-templates@latest --list-agents
|
|
81
|
+
|
|
82
|
+
# Update an agent to latest version
|
|
83
|
+
npx claude-code-templates@latest --update-agent customer-support
|
|
84
|
+
|
|
85
|
+
# Remove an agent
|
|
86
|
+
npx claude-code-templates@latest --remove-agent customer-support
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### How It Works
|
|
90
|
+
|
|
91
|
+
1. **Download Agent**: Fetches the latest agent from GitHub
|
|
92
|
+
2. **Generate Executable**: Creates a Node.js script that calls Claude Code SDK
|
|
93
|
+
3. **Add to PATH**: Makes the agent available globally in your shell
|
|
94
|
+
4. **Ready to Use**: Execute `agent-name "your prompt"` from any directory
|
|
95
|
+
|
|
96
|
+
The agents use the Claude Code SDK internally to provide specialized AI assistance with domain-specific knowledge and best practices.
|
|
97
|
+
|
|
52
98
|
## š Documentation
|
|
53
99
|
|
|
54
100
|
**[š Complete Documentation](https://docs.aitmpl.com/)** - Comprehensive guides, examples, and API reference
|
|
@@ -41,7 +41,7 @@ console.log(
|
|
|
41
41
|
|
|
42
42
|
program
|
|
43
43
|
.name('create-claude-config')
|
|
44
|
-
.description('Setup Claude Code configurations
|
|
44
|
+
.description('Setup Claude Code configurations and create global AI agents powered by Claude Code SDK')
|
|
45
45
|
.version(require('../package.json').version)
|
|
46
46
|
.option('-l, --language <language>', 'specify programming language (deprecated, use --template)')
|
|
47
47
|
.option('-f, --framework <framework>', 'specify framework (deprecated, use --template)')
|
|
@@ -66,6 +66,10 @@ program
|
|
|
66
66
|
.option('--hook <hook>', 'install specific hook component (supports comma-separated values)')
|
|
67
67
|
.option('--workflow <workflow>', 'install workflow from hash (#hash) OR workflow YAML (base64 encoded) when used with --agent/--command/--mcp')
|
|
68
68
|
.option('--prompt <prompt>', 'execute the provided prompt in Claude Code after installation')
|
|
69
|
+
.option('--create-agent <agent>', 'create a global agent accessible from anywhere (e.g., customer-support)')
|
|
70
|
+
.option('--list-agents', 'list all installed global agents')
|
|
71
|
+
.option('--remove-agent <agent>', 'remove a global agent')
|
|
72
|
+
.option('--update-agent <agent>', 'update a global agent to the latest version')
|
|
69
73
|
.action(async (options) => {
|
|
70
74
|
try {
|
|
71
75
|
await createClaudeConfig(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.1",
|
|
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/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const { runAnalytics } = require('./analytics');
|
|
|
16
16
|
const { startChatsMobile } = require('./chats-mobile');
|
|
17
17
|
const { runHealthCheck } = require('./health-check');
|
|
18
18
|
const { trackingService } = require('./tracking-service');
|
|
19
|
+
const { createGlobalAgent, listGlobalAgents, removeGlobalAgent, updateGlobalAgent } = require('./sdk/global-agent-manager');
|
|
19
20
|
|
|
20
21
|
async function showMainMenu() {
|
|
21
22
|
console.log('');
|
|
@@ -130,6 +131,30 @@ async function createClaudeConfig(options = {}) {
|
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
// Handle global agent creation
|
|
135
|
+
if (options.createAgent) {
|
|
136
|
+
await createGlobalAgent(options.createAgent, options);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Handle global agent listing
|
|
141
|
+
if (options.listAgents) {
|
|
142
|
+
await listGlobalAgents(options);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Handle global agent removal
|
|
147
|
+
if (options.removeAgent) {
|
|
148
|
+
await removeGlobalAgent(options.removeAgent, options);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle global agent update
|
|
153
|
+
if (options.updateAgent) {
|
|
154
|
+
await updateGlobalAgent(options.updateAgent, options);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
133
158
|
// Handle command stats analysis (both singular and plural)
|
|
134
159
|
if (options.commandStats || options.commandsStats) {
|
|
135
160
|
await runCommandStats(options);
|
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
|
|
7
|
+
// Global agents directory
|
|
8
|
+
const GLOBAL_AGENTS_DIR = path.join(os.homedir(), '.claude-code-templates');
|
|
9
|
+
const AGENTS_DIR = path.join(GLOBAL_AGENTS_DIR, 'agents');
|
|
10
|
+
const LOCAL_BIN_DIR = path.join(GLOBAL_AGENTS_DIR, 'bin');
|
|
11
|
+
|
|
12
|
+
// Try to use system bin directory for immediate availability
|
|
13
|
+
const SYSTEM_BIN_DIR = '/usr/local/bin';
|
|
14
|
+
const isSystemWritable = () => {
|
|
15
|
+
try {
|
|
16
|
+
const testFile = path.join(SYSTEM_BIN_DIR, '.test-write');
|
|
17
|
+
require('fs').writeFileSync(testFile, 'test', 'utf8');
|
|
18
|
+
require('fs').unlinkSync(testFile);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Choose the best bin directory
|
|
26
|
+
const BIN_DIR = isSystemWritable() ? SYSTEM_BIN_DIR : LOCAL_BIN_DIR;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a global agent that can be executed from anywhere
|
|
30
|
+
*/
|
|
31
|
+
async function createGlobalAgent(agentName, options = {}) {
|
|
32
|
+
console.log(chalk.blue(`š¤ Creating global agent: ${agentName}`));
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Ensure directories exist
|
|
36
|
+
await fs.ensureDir(AGENTS_DIR);
|
|
37
|
+
await fs.ensureDir(LOCAL_BIN_DIR); // Always ensure local bin exists for backups
|
|
38
|
+
|
|
39
|
+
if (BIN_DIR === SYSTEM_BIN_DIR) {
|
|
40
|
+
console.log(chalk.green('š Installing to system directory (immediately available)'));
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.yellow('ā ļø Installing to user directory (requires PATH setup)'));
|
|
43
|
+
await fs.ensureDir(BIN_DIR);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Download agent from GitHub
|
|
47
|
+
const spinner = ora('Downloading agent from GitHub...').start();
|
|
48
|
+
|
|
49
|
+
let githubUrl;
|
|
50
|
+
if (agentName.includes('/')) {
|
|
51
|
+
// Category/agent format
|
|
52
|
+
githubUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/agents/${agentName}.md`;
|
|
53
|
+
} else {
|
|
54
|
+
// Direct agent format - try to find it in any category
|
|
55
|
+
githubUrl = await findAgentUrl(agentName);
|
|
56
|
+
if (!githubUrl) {
|
|
57
|
+
spinner.fail(`Agent "${agentName}" not found`);
|
|
58
|
+
await showAvailableAgents();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const response = await fetch(githubUrl);
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
spinner.fail(`Failed to download agent: HTTP ${response.status}`);
|
|
66
|
+
if (response.status === 404) {
|
|
67
|
+
await showAvailableAgents();
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const agentContent = await response.text();
|
|
73
|
+
spinner.succeed('Agent downloaded successfully');
|
|
74
|
+
|
|
75
|
+
// Extract agent name for file/executable naming
|
|
76
|
+
const executableName = agentName.includes('/') ?
|
|
77
|
+
agentName.split('/')[1] : agentName;
|
|
78
|
+
|
|
79
|
+
// Save agent content
|
|
80
|
+
const agentFile = path.join(AGENTS_DIR, `${executableName}.md`);
|
|
81
|
+
await fs.writeFile(agentFile, agentContent, 'utf8');
|
|
82
|
+
|
|
83
|
+
// Generate executable script
|
|
84
|
+
await generateExecutableScript(executableName, agentFile);
|
|
85
|
+
|
|
86
|
+
console.log(chalk.green(`ā
Global agent '${executableName}' created successfully!`));
|
|
87
|
+
console.log(chalk.cyan('š¦ Usage:'));
|
|
88
|
+
console.log(chalk.white(` ${executableName} "your prompt here"`));
|
|
89
|
+
|
|
90
|
+
if (BIN_DIR === SYSTEM_BIN_DIR) {
|
|
91
|
+
console.log(chalk.green('š Ready to use immediately! No setup required.'));
|
|
92
|
+
console.log(chalk.gray('š” Works in scripts, npm tasks, CI/CD, etc.'));
|
|
93
|
+
} else {
|
|
94
|
+
// Add to PATH (first time setup) only for user directory
|
|
95
|
+
await addToPath();
|
|
96
|
+
console.log(chalk.yellow('š Restart your terminal or run:'));
|
|
97
|
+
console.log(chalk.gray(' source ~/.bashrc # for bash'));
|
|
98
|
+
console.log(chalk.gray(' source ~/.zshrc # for zsh'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log(chalk.red(`ā Error creating global agent: ${error.message}`));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* List installed global agents
|
|
108
|
+
*/
|
|
109
|
+
async function listGlobalAgents(options = {}) {
|
|
110
|
+
console.log(chalk.blue('š Installed Global Agents:'));
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Check both system and local bin directories
|
|
114
|
+
let systemAgents = [];
|
|
115
|
+
if (await fs.pathExists(SYSTEM_BIN_DIR)) {
|
|
116
|
+
const systemFiles = await fs.readdir(SYSTEM_BIN_DIR);
|
|
117
|
+
for (const file of systemFiles) {
|
|
118
|
+
if (!file.startsWith('.') && await fs.pathExists(path.join(AGENTS_DIR, `${file}.md`))) {
|
|
119
|
+
systemAgents.push(file);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const localAgents = await fs.pathExists(LOCAL_BIN_DIR) ?
|
|
125
|
+
(await fs.readdir(LOCAL_BIN_DIR)).filter(file => !file.startsWith('.')) : [];
|
|
126
|
+
|
|
127
|
+
const allAgents = [...new Set([...systemAgents, ...localAgents])];
|
|
128
|
+
|
|
129
|
+
if (allAgents.length === 0) {
|
|
130
|
+
console.log(chalk.yellow('ā ļø No global agents installed yet.'));
|
|
131
|
+
console.log(chalk.gray('š” Create one with: npx claude-code-templates@latest --create-agent <agent-name>'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(chalk.green(`\nā
Found ${allAgents.length} global agent(s):\n`));
|
|
136
|
+
|
|
137
|
+
for (const agent of allAgents) {
|
|
138
|
+
// Check which directory has the agent
|
|
139
|
+
const systemPath = path.join(SYSTEM_BIN_DIR, agent);
|
|
140
|
+
const localPath = path.join(LOCAL_BIN_DIR, agent);
|
|
141
|
+
|
|
142
|
+
let agentPath, location;
|
|
143
|
+
if (await fs.pathExists(systemPath)) {
|
|
144
|
+
agentPath = systemPath;
|
|
145
|
+
location = 'š system';
|
|
146
|
+
} else {
|
|
147
|
+
agentPath = localPath;
|
|
148
|
+
location = 'š¤ user';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const stats = await fs.stat(agentPath);
|
|
152
|
+
const isExecutable = (stats.mode & parseInt('111', 8)) !== 0;
|
|
153
|
+
|
|
154
|
+
console.log(chalk.cyan(` ${isExecutable ? 'ā
' : 'ā'} ${agent} (${location})`));
|
|
155
|
+
console.log(chalk.gray(` Usage: ${agent} "your prompt"`));
|
|
156
|
+
console.log(chalk.gray(` Created: ${stats.birthtime.toLocaleDateString()}`));
|
|
157
|
+
console.log('');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(chalk.blue('š Global Usage:'));
|
|
161
|
+
console.log(chalk.gray(' ⢠Run from any directory: <agent-name> "prompt"'));
|
|
162
|
+
console.log(chalk.gray(' ⢠List agents: npx claude-code-templates@latest --list-agents'));
|
|
163
|
+
console.log(chalk.gray(' ⢠Remove agent: npx claude-code-templates@latest --remove-agent <name>'));
|
|
164
|
+
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.log(chalk.red(`ā Error listing agents: ${error.message}`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Remove a global agent
|
|
172
|
+
*/
|
|
173
|
+
async function removeGlobalAgent(agentName, options = {}) {
|
|
174
|
+
console.log(chalk.blue(`šļø Removing global agent: ${agentName}`));
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const systemExecutablePath = path.join(SYSTEM_BIN_DIR, agentName);
|
|
178
|
+
const localExecutablePath = path.join(LOCAL_BIN_DIR, agentName);
|
|
179
|
+
const agentPath = path.join(AGENTS_DIR, `${agentName}.md`);
|
|
180
|
+
|
|
181
|
+
let removed = false;
|
|
182
|
+
|
|
183
|
+
// Remove from system directory
|
|
184
|
+
if (await fs.pathExists(systemExecutablePath)) {
|
|
185
|
+
await fs.remove(systemExecutablePath);
|
|
186
|
+
console.log(chalk.green(`ā
Removed system executable: ${agentName}`));
|
|
187
|
+
removed = true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Remove from local directory
|
|
191
|
+
if (await fs.pathExists(localExecutablePath)) {
|
|
192
|
+
await fs.remove(localExecutablePath);
|
|
193
|
+
console.log(chalk.green(`ā
Removed local executable: ${agentName}`));
|
|
194
|
+
removed = true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Remove agent file
|
|
198
|
+
if (await fs.pathExists(agentPath)) {
|
|
199
|
+
await fs.remove(agentPath);
|
|
200
|
+
console.log(chalk.green(`ā
Removed agent file: ${agentName}.md`));
|
|
201
|
+
removed = true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!removed) {
|
|
205
|
+
console.log(chalk.yellow(`ā ļø Agent '${agentName}' not found.`));
|
|
206
|
+
console.log(chalk.gray('š” List available agents with: --list-agents'));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(chalk.green(`š Global agent '${agentName}' removed successfully!`));
|
|
211
|
+
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.log(chalk.red(`ā Error removing agent: ${error.message}`));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Update a global agent
|
|
219
|
+
*/
|
|
220
|
+
async function updateGlobalAgent(agentName, options = {}) {
|
|
221
|
+
console.log(chalk.blue(`š Updating global agent: ${agentName}`));
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const executablePath = path.join(BIN_DIR, agentName);
|
|
225
|
+
|
|
226
|
+
if (!await fs.pathExists(executablePath)) {
|
|
227
|
+
console.log(chalk.yellow(`ā ļø Agent '${agentName}' not found.`));
|
|
228
|
+
console.log(chalk.gray('š” Create it with: --create-agent <agent-name>'));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Re-download and recreate
|
|
233
|
+
console.log(chalk.gray('š Re-downloading latest version...'));
|
|
234
|
+
await createGlobalAgent(agentName, { ...options, update: true });
|
|
235
|
+
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.log(chalk.red(`ā Error updating agent: ${error.message}`));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Generate executable script for an agent
|
|
243
|
+
*/
|
|
244
|
+
async function generateExecutableScript(agentName, agentFile) {
|
|
245
|
+
const scriptContent = `#!/usr/bin/env node
|
|
246
|
+
|
|
247
|
+
const { execSync } = require('child_process');
|
|
248
|
+
const path = require('path');
|
|
249
|
+
const fs = require('fs');
|
|
250
|
+
|
|
251
|
+
// Check if Claude CLI is available
|
|
252
|
+
function checkClaudeCLI() {
|
|
253
|
+
try {
|
|
254
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
255
|
+
return true;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Read agent system prompt
|
|
262
|
+
const agentPath = '${agentFile}';
|
|
263
|
+
if (!fs.existsSync(agentPath)) {
|
|
264
|
+
console.error('ā Agent file not found:', agentPath);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const rawSystemPrompt = fs.readFileSync(agentPath, 'utf8');
|
|
269
|
+
|
|
270
|
+
// Remove YAML front matter if present to get clean system prompt
|
|
271
|
+
const systemPrompt = rawSystemPrompt.replace(/^---[\\s\\S]*?---\\n/, '').trim();
|
|
272
|
+
|
|
273
|
+
// Parse arguments and detect context
|
|
274
|
+
const args = process.argv.slice(2);
|
|
275
|
+
let userInput = '';
|
|
276
|
+
let explicitFiles = [];
|
|
277
|
+
let explicitDirs = [];
|
|
278
|
+
let autoDetect = true;
|
|
279
|
+
|
|
280
|
+
// Parse command line arguments
|
|
281
|
+
let verbose = false;
|
|
282
|
+
|
|
283
|
+
for (let i = 0; i < args.length; i++) {
|
|
284
|
+
const arg = args[i];
|
|
285
|
+
|
|
286
|
+
if (arg === '--file' && i + 1 < args.length) {
|
|
287
|
+
explicitFiles.push(args[++i]);
|
|
288
|
+
autoDetect = false; // Disable auto-detect when explicit files provided
|
|
289
|
+
} else if (arg === '--dir' && i + 1 < args.length) {
|
|
290
|
+
explicitDirs.push(args[++i]);
|
|
291
|
+
autoDetect = false;
|
|
292
|
+
} else if (arg === '--no-auto') {
|
|
293
|
+
autoDetect = false;
|
|
294
|
+
} else if (arg === '--verbose' || arg === '-v') {
|
|
295
|
+
verbose = true;
|
|
296
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
297
|
+
console.log('Usage: ${agentName} [options] "your prompt"');
|
|
298
|
+
console.log('');
|
|
299
|
+
console.log('Context Options:');
|
|
300
|
+
console.log(' [default] Auto-detect project files (smart context)');
|
|
301
|
+
console.log(' --file <path> Include specific file');
|
|
302
|
+
console.log(' --dir <path> Include specific directory');
|
|
303
|
+
console.log(' --no-auto Disable auto-detection');
|
|
304
|
+
console.log(' --verbose, -v Enable verbose debugging output');
|
|
305
|
+
console.log('');
|
|
306
|
+
console.log('Examples:');
|
|
307
|
+
console.log(' ${agentName} "review for security issues" # Auto-detect');
|
|
308
|
+
console.log(' ${agentName} --file auth.js "check this file" # Specific file');
|
|
309
|
+
console.log(' ${agentName} --no-auto "general advice" # No context');
|
|
310
|
+
process.exit(0);
|
|
311
|
+
} else if (!arg.startsWith('--')) {
|
|
312
|
+
userInput += arg + ' ';
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
userInput = userInput.trim();
|
|
317
|
+
|
|
318
|
+
if (!userInput) {
|
|
319
|
+
console.error('ā Please provide a prompt');
|
|
320
|
+
console.error('Usage: ${agentName} [options] "your prompt"');
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Auto-detect project context if enabled
|
|
325
|
+
let contextPrompt = '';
|
|
326
|
+
if (autoDetect && explicitFiles.length === 0 && explicitDirs.length === 0) {
|
|
327
|
+
const fs = require('fs');
|
|
328
|
+
const path = require('path');
|
|
329
|
+
const cwd = process.cwd();
|
|
330
|
+
|
|
331
|
+
// Detect project type and relevant files
|
|
332
|
+
let projectType = 'unknown';
|
|
333
|
+
let relevantFiles = [];
|
|
334
|
+
|
|
335
|
+
// Check for common project indicators
|
|
336
|
+
if (fs.existsSync('package.json')) {
|
|
337
|
+
projectType = 'javascript/node';
|
|
338
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
339
|
+
|
|
340
|
+
// Add framework detection
|
|
341
|
+
if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
|
|
342
|
+
projectType = 'react';
|
|
343
|
+
} else if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
|
|
344
|
+
projectType = 'vue';
|
|
345
|
+
} else if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {
|
|
346
|
+
projectType = 'nextjs';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Common files to include for JS projects
|
|
350
|
+
const jsFiles = ['src/', 'lib/', 'components/', 'pages/', 'api/', 'routes/'];
|
|
351
|
+
jsFiles.forEach(dir => {
|
|
352
|
+
if (fs.existsSync(dir)) relevantFiles.push(dir);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
} else if (fs.existsSync('requirements.txt') || fs.existsSync('pyproject.toml')) {
|
|
356
|
+
projectType = 'python';
|
|
357
|
+
relevantFiles = ['*.py', 'src/', 'app/', 'api/'];
|
|
358
|
+
|
|
359
|
+
} else if (fs.existsSync('Cargo.toml')) {
|
|
360
|
+
projectType = 'rust';
|
|
361
|
+
relevantFiles = ['src/', 'Cargo.toml'];
|
|
362
|
+
|
|
363
|
+
} else if (fs.existsSync('go.mod')) {
|
|
364
|
+
projectType = 'go';
|
|
365
|
+
relevantFiles = ['*.go', 'cmd/', 'internal/', 'pkg/'];
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Build context prompt
|
|
369
|
+
if (projectType !== 'unknown') {
|
|
370
|
+
contextPrompt = \`
|
|
371
|
+
|
|
372
|
+
š PROJECT CONTEXT:
|
|
373
|
+
- Project type: \${projectType}
|
|
374
|
+
- Working directory: \${path.basename(cwd)}
|
|
375
|
+
- Auto-detected relevant files/folders: \${relevantFiles.join(', ')}
|
|
376
|
+
|
|
377
|
+
Please analyze the \${userInput} in the context of this \${projectType} project. You have access to read any files in the current directory using the Read tool.\`;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Check Claude CLI availability
|
|
382
|
+
if (!checkClaudeCLI()) {
|
|
383
|
+
console.error('ā Claude CLI not found in PATH');
|
|
384
|
+
console.error('š” Install Claude CLI: https://claude.ai/code');
|
|
385
|
+
console.error('š” Or install via npm: npm install -g @anthropic-ai/claude-code');
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Escape quotes in system prompt for shell execution
|
|
390
|
+
const escapedSystemPrompt = systemPrompt.replace(/"/g, '\\\\"').replace(/\`/g, '\\\\\`');
|
|
391
|
+
|
|
392
|
+
// Build final prompt with context
|
|
393
|
+
const finalPrompt = userInput + contextPrompt;
|
|
394
|
+
const escapedFinalPrompt = finalPrompt.replace(/"/g, '\\\\"').replace(/\`/g, '\\\\\`');
|
|
395
|
+
|
|
396
|
+
// Build Claude command with SDK - use --system-prompt instead of --append-system-prompt for better control
|
|
397
|
+
const claudeCmd = \`claude -p "\${escapedFinalPrompt}" --system-prompt "\${escapedSystemPrompt}"\${verbose ? ' --verbose' : ''}\`;
|
|
398
|
+
|
|
399
|
+
// Debug output if verbose
|
|
400
|
+
if (verbose) {
|
|
401
|
+
console.log('\\nš DEBUG MODE - Command Details:');
|
|
402
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
403
|
+
console.log('š User Input:', userInput);
|
|
404
|
+
console.log('š Project Context:', contextPrompt ? 'Auto-detected' : 'None');
|
|
405
|
+
console.log('šÆ Final Prompt Length:', finalPrompt.length, 'characters');
|
|
406
|
+
console.log('š¤ System Prompt Preview:', systemPrompt.substring(0, 150) + '...');
|
|
407
|
+
console.log('ā” Claude Command:', claudeCmd);
|
|
408
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\\n');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Show loading indicator
|
|
412
|
+
const frames = ['ā ', 'ā ', 'ā ¹', 'ā ø', 'ā ¼', 'ā “', 'ā ¦', 'ā §', 'ā ', 'ā '];
|
|
413
|
+
let currentFrame = 0;
|
|
414
|
+
const agentDisplayName = '${agentName}'.replace(/-/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase());
|
|
415
|
+
|
|
416
|
+
console.error(\`\\nš¤ \${agentDisplayName} is thinking...\`);
|
|
417
|
+
const loader = setInterval(() => {
|
|
418
|
+
process.stderr.write(\`\\r\${frames[currentFrame]} Claude Code is working... \`);
|
|
419
|
+
currentFrame = (currentFrame + 1) % frames.length;
|
|
420
|
+
}, 100);
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
// Execute Claude with the agent's system prompt
|
|
424
|
+
execSync(claudeCmd, {
|
|
425
|
+
stdio: ['inherit', 'inherit', 'pipe'],
|
|
426
|
+
cwd: process.cwd()
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Clear loader
|
|
430
|
+
clearInterval(loader);
|
|
431
|
+
process.stderr.write('\\rā
Response ready!\\n\\n');
|
|
432
|
+
|
|
433
|
+
} catch (error) {
|
|
434
|
+
clearInterval(loader);
|
|
435
|
+
process.stderr.write('\\r');
|
|
436
|
+
console.error('ā Error executing Claude:', error.message);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
`;
|
|
440
|
+
|
|
441
|
+
const scriptPath = path.join(BIN_DIR, agentName);
|
|
442
|
+
await fs.writeFile(scriptPath, scriptContent, 'utf8');
|
|
443
|
+
|
|
444
|
+
// Make executable (Unix/Linux/macOS)
|
|
445
|
+
if (process.platform !== 'win32') {
|
|
446
|
+
await fs.chmod(scriptPath, 0o755);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Add global agents bin directory to PATH
|
|
452
|
+
*/
|
|
453
|
+
async function addToPath() {
|
|
454
|
+
const shell = process.env.SHELL || '';
|
|
455
|
+
const isWindows = process.platform === 'win32';
|
|
456
|
+
|
|
457
|
+
if (isWindows) {
|
|
458
|
+
// Windows PATH management
|
|
459
|
+
console.log(chalk.yellow('šŖ Windows detected:'));
|
|
460
|
+
console.log(chalk.gray(`Add this to your PATH: ${BIN_DIR}`));
|
|
461
|
+
console.log(chalk.gray('Or run this in PowerShell as Administrator:'));
|
|
462
|
+
console.log(chalk.white(`[Environment]::SetEnvironmentVariable("Path", $env:Path + ";${BIN_DIR}", "User")`));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Unix-like systems
|
|
467
|
+
const pathExport = `export PATH="${BIN_DIR}:$PATH"`;
|
|
468
|
+
|
|
469
|
+
// Determine shell config files to update
|
|
470
|
+
const configFiles = [];
|
|
471
|
+
|
|
472
|
+
if (shell.includes('bash') || !shell) {
|
|
473
|
+
configFiles.push(path.join(os.homedir(), '.bashrc'));
|
|
474
|
+
configFiles.push(path.join(os.homedir(), '.bash_profile'));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (shell.includes('zsh')) {
|
|
478
|
+
configFiles.push(path.join(os.homedir(), '.zshrc'));
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (shell.includes('fish')) {
|
|
482
|
+
const fishConfigDir = path.join(os.homedir(), '.config', 'fish');
|
|
483
|
+
await fs.ensureDir(fishConfigDir);
|
|
484
|
+
configFiles.push(path.join(fishConfigDir, 'config.fish'));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Add default files if shell not detected
|
|
488
|
+
if (configFiles.length === 0) {
|
|
489
|
+
configFiles.push(path.join(os.homedir(), '.bashrc'));
|
|
490
|
+
configFiles.push(path.join(os.homedir(), '.zshrc'));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Check if PATH is already added
|
|
494
|
+
let alreadyInPath = false;
|
|
495
|
+
|
|
496
|
+
for (const configFile of configFiles) {
|
|
497
|
+
if (await fs.pathExists(configFile)) {
|
|
498
|
+
const content = await fs.readFile(configFile, 'utf8');
|
|
499
|
+
if (content.includes(BIN_DIR)) {
|
|
500
|
+
alreadyInPath = true;
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (alreadyInPath) {
|
|
507
|
+
console.log(chalk.green('ā
PATH already configured'));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Add to PATH in config files
|
|
512
|
+
console.log(chalk.blue('š§ Adding to PATH...'));
|
|
513
|
+
|
|
514
|
+
for (const configFile of configFiles) {
|
|
515
|
+
try {
|
|
516
|
+
let content = '';
|
|
517
|
+
if (await fs.pathExists(configFile)) {
|
|
518
|
+
content = await fs.readFile(configFile, 'utf8');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Add PATH export if not already present
|
|
522
|
+
if (!content.includes(BIN_DIR)) {
|
|
523
|
+
const newContent = content + `\n# Claude Code Templates - Global Agents\n${pathExport}\n`;
|
|
524
|
+
await fs.writeFile(configFile, newContent, 'utf8');
|
|
525
|
+
console.log(chalk.green(`ā
Updated ${path.basename(configFile)}`));
|
|
526
|
+
}
|
|
527
|
+
} catch (error) {
|
|
528
|
+
console.log(chalk.yellow(`ā ļø Could not update ${configFile}: ${error.message}`));
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Find agent URL by searching in all categories
|
|
535
|
+
*/
|
|
536
|
+
async function findAgentUrl(agentName) {
|
|
537
|
+
try {
|
|
538
|
+
// First try root level
|
|
539
|
+
const rootUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/agents/${agentName}.md`;
|
|
540
|
+
const rootResponse = await fetch(rootUrl);
|
|
541
|
+
if (rootResponse.ok) {
|
|
542
|
+
return rootUrl;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Search in categories
|
|
546
|
+
const categoriesResponse = await fetch('https://api.github.com/repos/davila7/claude-code-templates/contents/cli-tool/components/agents');
|
|
547
|
+
if (!categoriesResponse.ok) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const contents = await categoriesResponse.json();
|
|
552
|
+
|
|
553
|
+
for (const item of contents) {
|
|
554
|
+
if (item.type === 'dir') {
|
|
555
|
+
const categoryUrl = `https://raw.githubusercontent.com/davila7/claude-code-templates/main/cli-tool/components/agents/${item.name}/${agentName}.md`;
|
|
556
|
+
try {
|
|
557
|
+
const categoryResponse = await fetch(categoryUrl);
|
|
558
|
+
if (categoryResponse.ok) {
|
|
559
|
+
return categoryUrl;
|
|
560
|
+
}
|
|
561
|
+
} catch (error) {
|
|
562
|
+
// Continue searching
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return null;
|
|
568
|
+
} catch (error) {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Show available agents for user selection
|
|
575
|
+
*/
|
|
576
|
+
async function showAvailableAgents() {
|
|
577
|
+
console.log(chalk.yellow('\nš Available Agents:'));
|
|
578
|
+
console.log(chalk.gray('Use format: category/agent-name or just agent-name\n'));
|
|
579
|
+
|
|
580
|
+
try {
|
|
581
|
+
const response = await fetch('https://api.github.com/repos/davila7/claude-code-templates/contents/cli-tool/components/agents');
|
|
582
|
+
if (!response.ok) {
|
|
583
|
+
console.log(chalk.red('ā Could not fetch available agents from GitHub'));
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const contents = await response.json();
|
|
588
|
+
const agents = [];
|
|
589
|
+
|
|
590
|
+
for (const item of contents) {
|
|
591
|
+
if (item.type === 'file' && item.name.endsWith('.md')) {
|
|
592
|
+
agents.push({ name: item.name.replace('.md', ''), category: 'root' });
|
|
593
|
+
} else if (item.type === 'dir') {
|
|
594
|
+
try {
|
|
595
|
+
const categoryResponse = await fetch(`https://api.github.com/repos/davila7/claude-code-templates/contents/cli-tool/components/agents/${item.name}`);
|
|
596
|
+
if (categoryResponse.ok) {
|
|
597
|
+
const categoryContents = await categoryResponse.json();
|
|
598
|
+
for (const categoryItem of categoryContents) {
|
|
599
|
+
if (categoryItem.type === 'file' && categoryItem.name.endsWith('.md')) {
|
|
600
|
+
agents.push({
|
|
601
|
+
name: categoryItem.name.replace('.md', ''),
|
|
602
|
+
category: item.name,
|
|
603
|
+
path: `${item.name}/${categoryItem.name.replace('.md', '')}`
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
} catch (error) {
|
|
609
|
+
// Skip category on error
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Group by category
|
|
615
|
+
const grouped = agents.reduce((acc, agent) => {
|
|
616
|
+
const category = agent.category === 'root' ? 'š¤ General' : `š ${agent.category}`;
|
|
617
|
+
if (!acc[category]) acc[category] = [];
|
|
618
|
+
acc[category].push(agent);
|
|
619
|
+
return acc;
|
|
620
|
+
}, {});
|
|
621
|
+
|
|
622
|
+
Object.entries(grouped).forEach(([category, categoryAgents]) => {
|
|
623
|
+
console.log(chalk.cyan(category));
|
|
624
|
+
categoryAgents.forEach(agent => {
|
|
625
|
+
const displayName = agent.path || agent.name;
|
|
626
|
+
console.log(chalk.gray(` ⢠${displayName}`));
|
|
627
|
+
});
|
|
628
|
+
console.log('');
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
console.log(chalk.blue('Examples:'));
|
|
632
|
+
console.log(chalk.gray(' npx claude-code-templates@latest --create-agent api-security-audit'));
|
|
633
|
+
console.log(chalk.gray(' npx claude-code-templates@latest --create-agent deep-research-team/academic-researcher'));
|
|
634
|
+
|
|
635
|
+
} catch (error) {
|
|
636
|
+
console.log(chalk.red('ā Error fetching agents:', error.message));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
module.exports = {
|
|
641
|
+
createGlobalAgent,
|
|
642
|
+
listGlobalAgents,
|
|
643
|
+
removeGlobalAgent,
|
|
644
|
+
updateGlobalAgent,
|
|
645
|
+
showAvailableAgents
|
|
646
|
+
};
|