tmpa-cli 1.0.22 → 1.0.24
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/bin/index.js +223 -188
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -39,52 +39,42 @@ const PROVIDERS = {
|
|
|
39
39
|
'3': { name: 'NVIDIA NIM', endpoint: 'https://integrate.api.nvidia.com/v1', defaultModel: 'meta/llama-3.1-70b-instruct' },
|
|
40
40
|
'4': { name: 'OpenAI', endpoint: 'https://api.openai.com/v1', defaultModel: 'gpt-4o-mini' },
|
|
41
41
|
'5': { name: 'Anthropic', endpoint: 'https://api.anthropic.com/v1', defaultModel: 'claude-3-5-haiku-20241022' },
|
|
42
|
-
'6': { name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com/v1beta
|
|
42
|
+
'6': { name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com/v1beta', defaultModel: 'gemini-2.5-flash' },
|
|
43
43
|
'7': { name: 'Custom / Auto-Detect', endpoint: '', defaultModel: '' }
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
// 100+
|
|
46
|
+
// 100+ PRESETS LIST
|
|
47
47
|
const MCP_PRESETS = [
|
|
48
|
-
{ id: 'custom', name: '
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{ id: '
|
|
52
|
-
{ id: '
|
|
53
|
-
{ id: '
|
|
54
|
-
{ id: '
|
|
55
|
-
{ id: '
|
|
56
|
-
{ id: '
|
|
57
|
-
{ id: '
|
|
58
|
-
{ id: '
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
{ id: '
|
|
62
|
-
{ id: '
|
|
63
|
-
{ id: '
|
|
64
|
-
{ id: '
|
|
65
|
-
{ id: '
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{ id: '
|
|
69
|
-
{ id: '
|
|
70
|
-
{ id: '
|
|
71
|
-
{ id: '
|
|
72
|
-
{ id: '
|
|
73
|
-
{ id: '
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{ id: '
|
|
77
|
-
{ id: '
|
|
78
|
-
{ id: 'puppeteer', name: '🌐 Puppeteer Scraper', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-puppeteer', desc: 'Headless browser web automation & screenshot' },
|
|
79
|
-
{ id: 'firecrawl', name: '🔥 Firecrawl Scraper', category: 'Search & Scraping', type: 'direct-api', endpoint: 'https://api.firecrawl.dev/v1/mcp', reqAuth: 'FIRECRAWL_API_KEY', desc: 'Convert entire websites to clean Markdown' },
|
|
80
|
-
{ id: 'fetch', name: '📡 Fetch HTTP Utility', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-fetch', desc: 'HTTP request & web page parser' },
|
|
81
|
-
|
|
82
|
-
// Productivity & Communication
|
|
83
|
-
{ id: 'notion', name: '📄 Notion Workspace', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-notion', reqAuth: 'NOTION_API_TOKEN', desc: 'Read & edit Notion pages and databases' },
|
|
84
|
-
{ id: 'slack', name: '💬 Slack Channel', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-slack', reqAuth: 'SLACK_BOT_TOKEN', desc: 'Send messages & read channels' },
|
|
85
|
-
{ id: 'discord', name: '🎮 Discord Bot MCP', category: 'Productivity', type: 'stdio', command: 'npx -y discord-mcp-server', reqAuth: 'DISCORD_BOT_TOKEN', desc: 'Interact with Discord servers & channels' },
|
|
86
|
-
{ id: 'linear', name: '📐 Linear Issue Tracker', category: 'Productivity', type: 'stdio', command: 'npx -y @linear/mcp-server', reqAuth: 'LINEAR_API_KEY', desc: 'Manage project issues & sprint cycles' },
|
|
87
|
-
{ id: 'google-drive', name: '📁 Google Drive', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-gdrive', reqAuth: 'GDRIVE_CLIENT_ID', desc: 'Access Drive documents & sheets' }
|
|
48
|
+
{ id: 'custom', name: '[+] Custom / Manual Input', category: 'General', type: 'custom', desc: 'Manual Name, Target, & Key' },
|
|
49
|
+
{ id: 'google-stitch', name: '[+] Google Stitch', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://stitch.googleapis.com/v1', reqAuth: 'GOOGLE_STITCH_API_KEY', desc: 'UI & App Prototyping MCP by Google' },
|
|
50
|
+
{ id: 'supabase', name: '[+] Supabase MCP', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://api.supabase.com/mcp', reqAuth: 'SUPABASE_ACCESS_TOKEN', desc: 'Database, Auth & Storage Management' },
|
|
51
|
+
{ id: 'cloudflare-workers', name: '[+] Cloudflare Workers', category: 'Cloud & AI', type: 'stdio', command: 'npx -y @cloudflare/mcp-server-cloudflare', reqAuth: 'CLOUDFLARE_API_TOKEN', desc: 'Deploy & manage edge functions' },
|
|
52
|
+
{ id: 'vercel', name: '[+] Vercel MCP', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://api.vercel.com/v1/mcp', reqAuth: 'VERCEL_TOKEN', desc: 'Manage projects & deployments' },
|
|
53
|
+
{ id: 'railway', name: '[+] Railway MCP', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://backboard.railway.app/mcp', reqAuth: 'RAILWAY_API_TOKEN', desc: 'Cloud infrastructure & DB runner' },
|
|
54
|
+
{ id: 'firebase', name: '[+] Firebase MCP', category: 'Cloud & AI', type: 'stdio', command: 'npx -y firebase-mcp-server', reqAuth: 'FIREBASE_TOKEN', desc: 'Firestore, Auth & Realtime DB' },
|
|
55
|
+
{ id: 'huggingface', name: '[+] HuggingFace Spaces', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://huggingface.co/api/mcp', reqAuth: 'HF_TOKEN', desc: 'Run open-source AI models & spaces' },
|
|
56
|
+
{ id: 'replicate', name: '[+] Replicate MCP', category: 'Cloud & AI', type: 'stdio', command: 'npx -y @replicate/mcp-server', reqAuth: 'REPLICATE_API_TOKEN', desc: 'Run image/video AI generation models' },
|
|
57
|
+
{ id: 'github', name: '[+] GitHub Official', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-github', reqAuth: 'GITHUB_PERSONAL_ACCESS_TOKEN', desc: 'Repository, Issues, PRs & Gists' },
|
|
58
|
+
{ id: 'gitlab', name: '[+] GitLab MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-gitlab', reqAuth: 'GITLAB_PERSONAL_ACCESS_TOKEN', desc: 'GitLab projects & CI/CD pipelines' },
|
|
59
|
+
{ id: 'postman', name: '[+] Postman MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @postman/mcp-server', reqAuth: 'POSTMAN_API_KEY', desc: 'API Collection testing & documentation' },
|
|
60
|
+
{ id: 'sentry', name: '[+] Sentry Error Tracker', category: 'Developer Tools', type: 'stdio', command: 'npx -y @sentry/mcp-server', reqAuth: 'SENTRY_AUTH_TOKEN', desc: 'Inspect application bugs & stacktraces' },
|
|
61
|
+
{ id: 'docker', name: '[+] Docker Local MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-docker', desc: 'Manage local containers & images' },
|
|
62
|
+
{ id: 'postgresql', name: '[+] PostgreSQL', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-postgres', reqAuth: 'DATABASE_URL', desc: 'Direct SQL query execution' },
|
|
63
|
+
{ id: 'mysql', name: '[+] MySQL MCP', category: 'Databases', type: 'stdio', command: 'npx -y mysql-mcp-server', reqAuth: 'MYSQL_CONNECTION_STRING', desc: 'MySQL relational database runner' },
|
|
64
|
+
{ id: 'sqlite', name: '[+] SQLite Local', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-sqlite', desc: 'Lightweight local database query tool' },
|
|
65
|
+
{ id: 'mongodb', name: '[+] MongoDB MCP', category: 'Databases', type: 'stdio', command: 'npx -y mongodb-mcp-server', reqAuth: 'MONGODB_URI', desc: 'NoSQL collection queries & aggregation' },
|
|
66
|
+
{ id: 'redis', name: '[+] Redis Cache', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-redis', reqAuth: 'REDIS_URL', desc: 'Key-value data structure store' },
|
|
67
|
+
{ id: 'pinecone', name: '[+] Pinecone Vector DB', category: 'Databases', type: 'direct-api', endpoint: 'https://api.pinecone.io/mcp', reqAuth: 'PINECONE_API_KEY', desc: 'Vector database for AI embeddings' },
|
|
68
|
+
{ id: 'brave-search', name: '[+] Brave Search', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-brave-search', reqAuth: 'BRAVE_API_KEY', desc: 'Real-time global web search engine' },
|
|
69
|
+
{ id: 'tavily', name: '[+] Tavily AI Search', category: 'Search & Scraping', type: 'stdio', command: 'npx -y tavily-mcp', reqAuth: 'TAVILY_API_KEY', desc: 'Optimized search engine for LLMs' },
|
|
70
|
+
{ id: 'puppeteer', name: '[+] Puppeteer Scraper', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-puppeteer', desc: 'Headless browser web automation & screenshot' },
|
|
71
|
+
{ id: 'firecrawl', name: '[+] Firecrawl Scraper', category: 'Search & Scraping', type: 'direct-api', endpoint: 'https://api.firecrawl.dev/v1/mcp', reqAuth: 'FIRECRAWL_API_KEY', desc: 'Convert entire websites to clean Markdown' },
|
|
72
|
+
{ id: 'fetch', name: '[+] Fetch HTTP Utility', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-fetch', desc: 'HTTP request & web page parser' },
|
|
73
|
+
{ id: 'notion', name: '[+] Notion Workspace', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-notion', reqAuth: 'NOTION_API_TOKEN', desc: 'Read & edit Notion pages and databases' },
|
|
74
|
+
{ id: 'slack', name: '[+] Slack Channel', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-slack', reqAuth: 'SLACK_BOT_TOKEN', desc: 'Send messages & read channels' },
|
|
75
|
+
{ id: 'discord', name: '[+] Discord Bot MCP', category: 'Productivity', type: 'stdio', command: 'npx -y discord-mcp-server', reqAuth: 'DISCORD_BOT_TOKEN', desc: 'Interact with Discord servers & channels' },
|
|
76
|
+
{ id: 'linear', name: '[+] Linear Issue Tracker', category: 'Productivity', type: 'stdio', command: 'npx -y @linear/mcp-server', reqAuth: 'LINEAR_API_KEY', desc: 'Manage project issues & sprint cycles' },
|
|
77
|
+
{ id: 'google-drive', name: '[+] Google Drive', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-gdrive', reqAuth: 'GDRIVE_CLIENT_ID', desc: 'Access Drive documents & sheets' }
|
|
88
78
|
];
|
|
89
79
|
|
|
90
80
|
function loadConfig() {
|
|
@@ -94,8 +84,8 @@ function loadConfig() {
|
|
|
94
84
|
return {};
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
function saveConfig(
|
|
98
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
87
|
+
function saveConfig(cfg) {
|
|
88
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
99
89
|
}
|
|
100
90
|
|
|
101
91
|
function loadRegistry() {
|
|
@@ -105,19 +95,30 @@ function loadRegistry() {
|
|
|
105
95
|
return { skills: {}, mcp: {} };
|
|
106
96
|
}
|
|
107
97
|
|
|
108
|
-
function saveRegistry(
|
|
109
|
-
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(
|
|
98
|
+
function saveRegistry(reg) {
|
|
99
|
+
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(reg, null, 2));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function clearScreenFull() {
|
|
103
|
+
process.stdout.write('\x1B[2J\x1B[0;0H');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getTermWidth() {
|
|
107
|
+
return Math.min(process.stdout.columns || 60, 64);
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
function drawBox(title, contentLines, borderColor = C.cyan) {
|
|
113
|
-
const width =
|
|
114
|
-
|
|
115
|
-
console.log(`${borderColor}
|
|
116
|
-
console.log(`${borderColor}
|
|
111
|
+
const width = getTermWidth();
|
|
112
|
+
const innerWidth = width - 4;
|
|
113
|
+
console.log(`${borderColor}+${'-'.repeat(width - 2)}+${C.reset}`);
|
|
114
|
+
console.log(`${borderColor}| ${C.bold}${title.padEnd(innerWidth)}${C.reset}${borderColor} |${C.reset}`);
|
|
115
|
+
console.log(`${borderColor}+${'-'.repeat(width - 2)}+${C.reset}`);
|
|
117
116
|
contentLines.forEach(line => {
|
|
118
|
-
|
|
117
|
+
const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, '');
|
|
118
|
+
const padding = Math.max(0, innerWidth - cleanLine.length);
|
|
119
|
+
console.log(`${borderColor}|${C.reset} ${line}${' '.repeat(padding)} ${borderColor}|${C.reset}`);
|
|
119
120
|
});
|
|
120
|
-
console.log(`${borderColor}
|
|
121
|
+
console.log(`${borderColor}+${'-'.repeat(width - 2)}+${C.reset}`);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
let rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -125,18 +126,13 @@ let config = loadConfig();
|
|
|
125
126
|
let registry = loadRegistry();
|
|
126
127
|
|
|
127
128
|
function showBanner() {
|
|
128
|
-
|
|
129
|
+
clearScreenFull();
|
|
130
|
+
const width = getTermWidth();
|
|
129
131
|
console.log(`
|
|
130
|
-
${C.c1}
|
|
131
|
-
${C.
|
|
132
|
-
|
|
133
|
-
${C.
|
|
134
|
-
${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ ██║${C.reset}
|
|
135
|
-
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
136
|
-
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
137
|
-
${C.reset}The Multi Platform AI ${C.green}[MCP Hub & Interactive Provider Engine]${C.reset}
|
|
138
|
-
${C.gray}/config | /models | /skill | /mcp | /new-mcp | /scan | /uninstall | /exit${C.reset}
|
|
139
|
-
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
132
|
+
${C.c1}TMPA CLI Engine - Multi-Platform AI${C.reset}
|
|
133
|
+
${C.darkGray}${'-'.repeat(width)}${C.reset}
|
|
134
|
+
Commands: /config | /models | /custom-models | /skill | /mcp | /new-mcp | /scan | /uninstall
|
|
135
|
+
${C.darkGray}${'-'.repeat(width)}${C.reset}
|
|
140
136
|
`);
|
|
141
137
|
}
|
|
142
138
|
|
|
@@ -219,41 +215,42 @@ Executing User Prompt under this tool context only:\n`;
|
|
|
219
215
|
return "\n\n[SYSTEM ENVIRONMENT SUMMARY]\n" + contextParts.join("\n") + "\n";
|
|
220
216
|
}
|
|
221
217
|
|
|
222
|
-
//
|
|
218
|
+
// MCP CENTER
|
|
223
219
|
function showMCPHub() {
|
|
224
|
-
|
|
225
|
-
console.log(
|
|
226
|
-
console.log(`${C.c4}
|
|
220
|
+
const width = getTermWidth();
|
|
221
|
+
console.log(`\n${C.c4}${'-'.repeat(width)}${C.reset}`);
|
|
222
|
+
console.log(`${C.c4} [MCP] MODEL CONTEXT PROTOCOL CENTER ${C.reset}`);
|
|
223
|
+
console.log(`${C.c4}${'-'.repeat(width)}${C.reset}\n`);
|
|
227
224
|
|
|
228
225
|
const keys = Object.keys(registry.mcp || {});
|
|
229
226
|
|
|
230
227
|
if (keys.length === 0) {
|
|
231
|
-
console.log(`${C.yellow} [!]
|
|
232
|
-
console.log(`${C.gray}
|
|
228
|
+
console.log(`${C.yellow} [!] No MCP Servers currently connected.${C.reset}`);
|
|
229
|
+
console.log(`${C.gray} Use ${C.green}/new-mcp${C.gray} to add a new server.${C.reset}\n`);
|
|
233
230
|
} else {
|
|
234
|
-
console.log(`${C.bold}
|
|
231
|
+
console.log(`${C.bold}Active MCP Servers:${C.reset}`);
|
|
235
232
|
keys.forEach((name, i) => {
|
|
236
233
|
const m = registry.mcp[name];
|
|
237
|
-
const typeBadge = m.type === 'remote' || m.type === 'direct-api' ? `${C.cyan}[
|
|
234
|
+
const typeBadge = m.type === 'remote' || m.type === 'direct-api' ? `${C.cyan}[api]${C.reset}` : `${C.green}[stdio]${C.reset}`;
|
|
238
235
|
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.bold}${name}${C.reset} ${typeBadge}`);
|
|
239
|
-
console.log(`
|
|
240
|
-
if (m.authToken) console.log(`
|
|
236
|
+
console.log(` -> Target: ${C.gray}${m.target}${C.reset}`);
|
|
237
|
+
if (m.authToken) console.log(` -> Auth : ${C.green}Encrypted/Token set${C.reset}`);
|
|
241
238
|
});
|
|
242
239
|
console.log('');
|
|
243
240
|
}
|
|
244
241
|
|
|
245
242
|
const lines = [
|
|
246
|
-
`${C.bold}
|
|
247
|
-
`${C.yellow}/new-mcp${C.reset}
|
|
248
|
-
`${C.yellow}/mcp-<
|
|
249
|
-
`${C.yellow}/scan${C.reset}
|
|
243
|
+
`${C.bold}Instant MCP Commands:${C.reset}`,
|
|
244
|
+
`${C.yellow}/new-mcp${C.reset} -> Add MCP Server (Interactive Wizard)`,
|
|
245
|
+
`${C.yellow}/mcp-<name>${C.reset} <prompt> -> Execute specific MCP server`,
|
|
246
|
+
`${C.yellow}/scan${C.reset} -> Scan .mcp directory automatically`
|
|
250
247
|
];
|
|
251
248
|
|
|
252
|
-
drawBox("
|
|
249
|
+
drawBox("[i] MCP CENTER HELP", lines, C.c4);
|
|
253
250
|
console.log('');
|
|
254
251
|
}
|
|
255
252
|
|
|
256
|
-
// INTERACTIVE
|
|
253
|
+
// INTERACTIVE PROVIDER SELECTOR
|
|
257
254
|
function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
258
255
|
let filteredItems = [...allItems];
|
|
259
256
|
let selectedIndex = 0;
|
|
@@ -268,7 +265,8 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
268
265
|
}
|
|
269
266
|
|
|
270
267
|
function renderMenu() {
|
|
271
|
-
|
|
268
|
+
clearScreenFull();
|
|
269
|
+
const width = getTermWidth();
|
|
272
270
|
const totalPages = Math.ceil(filteredItems.length / pageSize) || 1;
|
|
273
271
|
if (currentPage >= totalPages) currentPage = totalPages - 1;
|
|
274
272
|
if (currentPage < 0) currentPage = 0;
|
|
@@ -278,31 +276,31 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
278
276
|
|
|
279
277
|
if (selectedIndex >= pageItems.length) selectedIndex = Math.max(0, pageItems.length - 1);
|
|
280
278
|
|
|
281
|
-
console.log(`${C.c4}
|
|
282
|
-
console.log(`${C.c4}
|
|
283
|
-
console.log(`${C.c4}
|
|
279
|
+
console.log(`${C.c4}${'-'.repeat(width)}${C.reset}`);
|
|
280
|
+
console.log(`${C.c4} [+] SETUP MCP SERVER PROVIDER ${C.reset}`);
|
|
281
|
+
console.log(`${C.c4}${'-'.repeat(width)}${C.reset}\n`);
|
|
284
282
|
|
|
285
283
|
if (pageItems.length === 0) {
|
|
286
|
-
console.log(`${C.red}
|
|
284
|
+
console.log(`${C.red} No provider matched your search.${C.reset}\n`);
|
|
287
285
|
} else {
|
|
288
286
|
pageItems.forEach((item, idx) => {
|
|
289
287
|
const globalNum = startIdx + idx;
|
|
290
288
|
const isFocused = idx === selectedIndex;
|
|
291
|
-
const pointer = isFocused ? `${C.cyan}
|
|
289
|
+
const pointer = isFocused ? `${C.cyan}>${C.reset}` : ' ';
|
|
292
290
|
const numStr = globalNum.toString().padStart(2, ' ');
|
|
293
291
|
const badge = item.type ? `${C.gray}[${item.type}]${C.reset}` : '';
|
|
294
292
|
const nameStyled = isFocused ? `${C.bold}${C.green}${item.name}${C.reset}` : item.name;
|
|
295
293
|
|
|
296
|
-
console.log(`${pointer} ${C.yellow}${numStr}.${C.reset}
|
|
294
|
+
console.log(`${pointer} ${C.yellow}${numStr}.${C.reset} ${nameStyled} ${badge}`);
|
|
297
295
|
console.log(` ${C.darkGray}${item.desc || item.category}${C.reset}`);
|
|
298
296
|
});
|
|
299
297
|
}
|
|
300
298
|
|
|
301
|
-
console.log(`\n${C.darkGray}
|
|
302
|
-
console.log(`${C.cyan}
|
|
303
|
-
console.log(`${C.gray}
|
|
304
|
-
console.log(`${C.gray}
|
|
305
|
-
console.log(`${C.darkGray}
|
|
299
|
+
console.log(`\n${C.darkGray}${'-'.repeat(width)}${C.reset}`);
|
|
300
|
+
console.log(`${C.cyan}Page ${currentPage + 1}/${totalPages}${C.reset} | Total: ${filteredItems.length} Providers`);
|
|
301
|
+
console.log(`${C.gray}Controls: [Up/Down] Arrow Keys | [ENTER] Select${C.reset}`);
|
|
302
|
+
console.log(`${C.gray}Keys: [f] Search | [n] Next | [p] Prev | [q] Cancel${C.reset}`);
|
|
303
|
+
console.log(`${C.darkGray}${'-'.repeat(width)}${C.reset}\n`);
|
|
306
304
|
}
|
|
307
305
|
|
|
308
306
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -346,7 +344,7 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
346
344
|
renderMenu();
|
|
347
345
|
} else if (str === 'f' || str === 'F') {
|
|
348
346
|
cleanup();
|
|
349
|
-
rl.question(`\n${C.yellow}[>]
|
|
347
|
+
rl.question(`\n${C.yellow}[>] Enter provider search query:${C.reset} `, (query) => {
|
|
350
348
|
const q = query.trim().toLowerCase();
|
|
351
349
|
filteredItems = allItems.filter(item => item.name.toLowerCase().includes(q) || (item.desc && item.desc.toLowerCase().includes(q)));
|
|
352
350
|
currentPage = 0;
|
|
@@ -359,7 +357,7 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
359
357
|
if (num >= 0 && num < allItems.length) {
|
|
360
358
|
onSelect(allItems[num]);
|
|
361
359
|
} else {
|
|
362
|
-
console.log(`${C.red}
|
|
360
|
+
console.log(`${C.red}Invalid index.${C.reset}`);
|
|
363
361
|
setTimeout(() => interactiveProviderSelector(allItems, onSelect, onCancel), 800);
|
|
364
362
|
}
|
|
365
363
|
}
|
|
@@ -369,9 +367,9 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
369
367
|
renderMenu();
|
|
370
368
|
}
|
|
371
369
|
|
|
372
|
-
// INTERACTIVE
|
|
370
|
+
// INTERACTIVE MODEL SELECTOR (/models) - ANTI-SPAM & CLEAN REDRAW
|
|
373
371
|
function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
374
|
-
let filteredModels =
|
|
372
|
+
let filteredModels = allModels.slice(0, 600);
|
|
375
373
|
let selectedIndex = 0;
|
|
376
374
|
let currentPage = 0;
|
|
377
375
|
const pageSize = 10;
|
|
@@ -384,7 +382,8 @@ function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
|
384
382
|
}
|
|
385
383
|
|
|
386
384
|
function renderMenu() {
|
|
387
|
-
|
|
385
|
+
clearScreenFull();
|
|
386
|
+
const width = getTermWidth();
|
|
388
387
|
const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
|
|
389
388
|
if (currentPage >= totalPages) currentPage = totalPages - 1;
|
|
390
389
|
if (currentPage < 0) currentPage = 0;
|
|
@@ -394,33 +393,33 @@ function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
|
394
393
|
|
|
395
394
|
if (selectedIndex >= pageItems.length) selectedIndex = Math.max(0, pageItems.length - 1);
|
|
396
395
|
|
|
397
|
-
console.log(`${C.c1}
|
|
398
|
-
console.log(`${C.c1}
|
|
399
|
-
console.log(`${C.c1}
|
|
400
|
-
console.log(
|
|
396
|
+
console.log(`${C.c1}${'-'.repeat(width)}${C.reset}`);
|
|
397
|
+
console.log(`${C.c1} [AI] CHOOSE AI MODEL (${config.provider || 'Provider'}) ${C.reset}`);
|
|
398
|
+
console.log(`${C.c1}${'-'.repeat(width)}${C.reset}`);
|
|
399
|
+
console.log(` Active Model: ${C.green}${config.model || 'None'}${C.reset}\n`);
|
|
401
400
|
|
|
402
401
|
if (pageItems.length === 0) {
|
|
403
|
-
console.log(`${C.red}
|
|
402
|
+
console.log(`${C.red} No model matched your query.${C.reset}\n`);
|
|
404
403
|
} else {
|
|
405
404
|
pageItems.forEach((m, idx) => {
|
|
406
405
|
const globalNum = startIdx + idx;
|
|
407
406
|
const isFocused = idx === selectedIndex;
|
|
408
407
|
const isCurrentActive = m.id === config.model;
|
|
409
408
|
|
|
410
|
-
const pointer = isFocused ? `${C.cyan}
|
|
411
|
-
const activeBadge = isCurrentActive ? `${C.green}
|
|
412
|
-
const numStr = globalNum.toString().padStart(3, ' ');
|
|
409
|
+
const pointer = isFocused ? `${C.cyan}>${C.reset}` : ' ';
|
|
410
|
+
const activeBadge = isCurrentActive ? `${C.green}[Active]${C.reset}` : '';
|
|
411
|
+
const numStr = (globalNum + 1).toString().padStart(3, ' ');
|
|
413
412
|
const modelNameStyled = isFocused ? `${C.bold}${C.yellow}${m.id}${C.reset}` : m.id;
|
|
414
413
|
|
|
415
|
-
console.log(`${pointer} ${C.gray}${numStr}.${C.reset}
|
|
414
|
+
console.log(`${pointer} ${C.gray}${numStr}.${C.reset} ${modelNameStyled} ${activeBadge}`);
|
|
416
415
|
});
|
|
417
416
|
}
|
|
418
417
|
|
|
419
|
-
console.log(`\n${C.darkGray}
|
|
420
|
-
console.log(`${C.cyan}
|
|
421
|
-
console.log(`${C.gray}
|
|
422
|
-
console.log(`${C.gray}
|
|
423
|
-
console.log(`${C.darkGray}
|
|
418
|
+
console.log(`\n${C.darkGray}${'-'.repeat(width)}${C.reset}`);
|
|
419
|
+
console.log(`${C.cyan}Page ${currentPage + 1}/${totalPages}${C.reset} | Total Models Loaded: ${filteredModels.length}`);
|
|
420
|
+
console.log(`${C.gray}Controls: [Up/Down] Arrow Keys | [ENTER] Select${C.reset}`);
|
|
421
|
+
console.log(`${C.gray}Keys: [f] Filter Model | [n] Next | [p] Prev | [q] Cancel${C.reset}`);
|
|
422
|
+
console.log(`${C.darkGray}${'-'.repeat(width)}${C.reset}\n`);
|
|
424
423
|
}
|
|
425
424
|
|
|
426
425
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -464,9 +463,9 @@ function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
|
464
463
|
renderMenu();
|
|
465
464
|
} else if (str === 'f' || str === 'F') {
|
|
466
465
|
cleanup();
|
|
467
|
-
rl.question(`\n${C.yellow}[>]
|
|
466
|
+
rl.question(`\n${C.yellow}[>] Enter model filter (e.g. llama, gemini, claude, qwen):${C.reset} `, (query) => {
|
|
468
467
|
const q = query.trim().toLowerCase();
|
|
469
|
-
filteredModels = allModels.filter(m => m.id.toLowerCase().includes(q));
|
|
468
|
+
filteredModels = allModels.filter(m => m.id.toLowerCase().includes(q)).slice(0, 600);
|
|
470
469
|
currentPage = 0;
|
|
471
470
|
selectedIndex = 0;
|
|
472
471
|
interactiveModelSelector(filteredModels, onSelect, onCancel);
|
|
@@ -478,38 +477,37 @@ function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
|
478
477
|
renderMenu();
|
|
479
478
|
}
|
|
480
479
|
|
|
481
|
-
//
|
|
480
|
+
// SETUP NEW MCP SERVER WIZARD (/new-mcp)
|
|
482
481
|
function createNewMCPWizard(callback) {
|
|
483
482
|
interactiveProviderSelector(
|
|
484
483
|
MCP_PRESETS,
|
|
485
484
|
(selectedProvider) => {
|
|
486
|
-
|
|
485
|
+
clearScreenFull();
|
|
487
486
|
|
|
488
|
-
// CASE 1: CUSTOM MCP INPUT MANUAL
|
|
489
487
|
if (selectedProvider.id === 'custom') {
|
|
490
|
-
console.log(`\n${C.cyan}===
|
|
491
|
-
rl.question(`${C.yellow}[1/4]
|
|
488
|
+
console.log(`\n${C.cyan}=== [+] MANUAL MCP SETUP (CUSTOM) ===${C.reset}\n`);
|
|
489
|
+
rl.question(`${C.yellow}[1/4] Enter MCP Server Name (e.g. my-mcp):${C.reset} `, (nameInput) => {
|
|
492
490
|
const mcpName = nameInput.trim().toLowerCase().replace(/\s+/g, '-');
|
|
493
491
|
if (!mcpName) {
|
|
494
|
-
console.log(`${C.red}[x]
|
|
492
|
+
console.log(`${C.red}[x] MCP name cannot be empty.${C.reset}\n`);
|
|
495
493
|
return callback();
|
|
496
494
|
}
|
|
497
495
|
|
|
498
|
-
console.log(`\n${C.cyan}
|
|
496
|
+
console.log(`\n${C.cyan}Select MCP Connection Type:${C.reset}`);
|
|
499
497
|
console.log(` ${C.yellow}1.${C.reset} Direct REST API / Endpoint`);
|
|
500
498
|
console.log(` ${C.yellow}2.${C.reset} Local Stdio Command / Path`);
|
|
501
499
|
|
|
502
|
-
rl.question(`\n${C.yellow}[2/4]
|
|
500
|
+
rl.question(`\n${C.yellow}[2/4] Select Type (1/2):${C.reset} `, (typeChoice) => {
|
|
503
501
|
const isApi = typeChoice.trim() === '1';
|
|
504
502
|
|
|
505
|
-
rl.question(`${C.yellow}[3/4]
|
|
503
|
+
rl.question(`${C.yellow}[3/4] Enter Target URL / Command Executable:${C.reset} `, (targetInput) => {
|
|
506
504
|
const target = targetInput.trim();
|
|
507
505
|
if (!target) {
|
|
508
|
-
console.log(`${C.red}[x] Target
|
|
506
|
+
console.log(`${C.red}[x] Target cannot be empty.${C.reset}\n`);
|
|
509
507
|
return callback();
|
|
510
508
|
}
|
|
511
509
|
|
|
512
|
-
rl.question(`${C.yellow}[4/4]
|
|
510
|
+
rl.question(`${C.yellow}[4/4] Enter Auth Token / API Key (Optional - Press Enter to skip):${C.reset} `, (tokenInput) => {
|
|
513
511
|
const authToken = tokenInput.trim();
|
|
514
512
|
|
|
515
513
|
registry.mcp = registry.mcp || {};
|
|
@@ -523,23 +521,21 @@ function createNewMCPWizard(callback) {
|
|
|
523
521
|
|
|
524
522
|
saveRegistry(registry);
|
|
525
523
|
|
|
526
|
-
console.log(`\n${C.green}[
|
|
527
|
-
console.log(`${C.gray}
|
|
524
|
+
console.log(`\n${C.green}[+] MCP Server "${mcpName}" successfully added!${C.reset}`);
|
|
525
|
+
console.log(`${C.gray}Execute via: ${C.yellow}/mcp-${mcpName} <your prompt>${C.reset}\n`);
|
|
528
526
|
callback();
|
|
529
527
|
});
|
|
530
528
|
});
|
|
531
529
|
});
|
|
532
530
|
});
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
else {
|
|
536
|
-
console.log(`\n${C.cyan}=== ⚡ SETUP PROVIDER: ${selectedProvider.name} ===${C.reset}`);
|
|
531
|
+
} else {
|
|
532
|
+
console.log(`\n${C.cyan}=== [+] SETUP PROVIDER: ${selectedProvider.name} ===${C.reset}`);
|
|
537
533
|
console.log(`${C.gray}${selectedProvider.desc}${C.reset}\n`);
|
|
538
534
|
|
|
539
535
|
const mcpName = selectedProvider.id;
|
|
540
536
|
|
|
541
537
|
if (selectedProvider.reqAuth) {
|
|
542
|
-
rl.question(`${C.yellow}[>]
|
|
538
|
+
rl.question(`${C.yellow}[>] Enter ${selectedProvider.reqAuth}: ${C.reset}`, (authVal) => {
|
|
543
539
|
const token = authVal.trim();
|
|
544
540
|
|
|
545
541
|
registry.mcp = registry.mcp || {};
|
|
@@ -553,8 +549,8 @@ function createNewMCPWizard(callback) {
|
|
|
553
549
|
|
|
554
550
|
saveRegistry(registry);
|
|
555
551
|
|
|
556
|
-
console.log(`\n${C.green}[
|
|
557
|
-
console.log(`${C.gray}
|
|
552
|
+
console.log(`\n${C.green}[+] MCP Preset "${selectedProvider.name}" successfully connected!${C.reset}`);
|
|
553
|
+
console.log(`${C.gray}Access via: ${C.yellow}/mcp-${mcpName} <your prompt>${C.reset}\n`);
|
|
558
554
|
callback();
|
|
559
555
|
});
|
|
560
556
|
} else {
|
|
@@ -569,14 +565,14 @@ function createNewMCPWizard(callback) {
|
|
|
569
565
|
|
|
570
566
|
saveRegistry(registry);
|
|
571
567
|
|
|
572
|
-
console.log(`\n${C.green}[
|
|
573
|
-
console.log(`${C.gray}
|
|
568
|
+
console.log(`\n${C.green}[+] MCP Preset "${selectedProvider.name}" successfully connected!${C.reset}`);
|
|
569
|
+
console.log(`${C.gray}Access via: ${C.yellow}/mcp-${mcpName} <your prompt>${C.reset}\n`);
|
|
574
570
|
callback();
|
|
575
571
|
}
|
|
576
572
|
}
|
|
577
573
|
},
|
|
578
574
|
() => {
|
|
579
|
-
console.log(`\n${C.gray}
|
|
575
|
+
console.log(`\n${C.gray}MCP setup cancelled.${C.reset}\n`);
|
|
580
576
|
callback();
|
|
581
577
|
}
|
|
582
578
|
);
|
|
@@ -587,7 +583,7 @@ function listSkills() {
|
|
|
587
583
|
const keys = Object.keys(registry.skills || {});
|
|
588
584
|
|
|
589
585
|
if (keys.length === 0) {
|
|
590
|
-
console.log(`${C.gray}
|
|
586
|
+
console.log(`${C.gray}No skills connected yet. Type ${C.yellow}/scan${C.gray} or ${C.yellow}/connect skill <path>${C.reset}\n`);
|
|
591
587
|
return;
|
|
592
588
|
}
|
|
593
589
|
|
|
@@ -598,14 +594,14 @@ function listSkills() {
|
|
|
598
594
|
console.log('');
|
|
599
595
|
|
|
600
596
|
const lines = [
|
|
601
|
-
`${C.bold}
|
|
597
|
+
`${C.bold}Instant Skill Commands:${C.reset}`
|
|
602
598
|
];
|
|
603
599
|
|
|
604
600
|
keys.forEach(name => {
|
|
605
|
-
lines.push(` ${C.yellow}/skill-${name}${C.reset} <prompt
|
|
601
|
+
lines.push(` ${C.yellow}/skill-${name}${C.reset} <your prompt>`);
|
|
606
602
|
});
|
|
607
603
|
|
|
608
|
-
drawBox("
|
|
604
|
+
drawBox("[+] AVAILABLE SKILL COMMANDS", lines, C.c1);
|
|
609
605
|
console.log('');
|
|
610
606
|
}
|
|
611
607
|
|
|
@@ -615,9 +611,9 @@ function connectResource(inputArgs) {
|
|
|
615
611
|
const targetPath = parts.slice(1).join(' ');
|
|
616
612
|
|
|
617
613
|
if (!type || !targetPath) {
|
|
618
|
-
console.log(`${C.red}[x]
|
|
619
|
-
console.log(` ${C.yellow}/connect skill <
|
|
620
|
-
console.log(` ${C.yellow}/connect mcp <
|
|
614
|
+
console.log(`${C.red}[x] Invalid format! Use:${C.reset}`);
|
|
615
|
+
console.log(` ${C.yellow}/connect skill <filepath_or_folder>${C.reset}`);
|
|
616
|
+
console.log(` ${C.yellow}/connect mcp <filepath_or_url>${C.reset}\n`);
|
|
621
617
|
return;
|
|
622
618
|
}
|
|
623
619
|
|
|
@@ -632,7 +628,7 @@ function connectResource(inputArgs) {
|
|
|
632
628
|
status: 'Active'
|
|
633
629
|
};
|
|
634
630
|
saveRegistry(registry);
|
|
635
|
-
console.log(`${C.green}[+] Skill "${skillName}"
|
|
631
|
+
console.log(`${C.green}[+] Skill "${skillName}" connected! Access via: /skill-${skillName}${C.reset}\n`);
|
|
636
632
|
} else if (type === 'mcp') {
|
|
637
633
|
const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
|
|
638
634
|
registry.mcp = registry.mcp || {};
|
|
@@ -643,14 +639,14 @@ function connectResource(inputArgs) {
|
|
|
643
639
|
status: 'Connected'
|
|
644
640
|
};
|
|
645
641
|
saveRegistry(registry);
|
|
646
|
-
console.log(`${C.green}[+] MCP "${mcpName}"
|
|
642
|
+
console.log(`${C.green}[+] MCP "${mcpName}" connected! Access via: /mcp-${mcpName}${C.reset}\n`);
|
|
647
643
|
} else {
|
|
648
|
-
console.log(`${C.red}[x]
|
|
644
|
+
console.log(`${C.red}[x] Unknown resource type. Use "skill" or "mcp".${C.reset}\n`);
|
|
649
645
|
}
|
|
650
646
|
}
|
|
651
647
|
|
|
652
648
|
function scanResources() {
|
|
653
|
-
console.log(`\n${C.yellow}[...]
|
|
649
|
+
console.log(`\n${C.yellow}[...] Scanning internal directories, home user, Gemini CLI & Claude Desktop...${C.reset}`);
|
|
654
650
|
registry = loadRegistry();
|
|
655
651
|
registry.skills = registry.skills || {};
|
|
656
652
|
registry.mcp = registry.mcp || {};
|
|
@@ -693,33 +689,33 @@ function scanResources() {
|
|
|
693
689
|
});
|
|
694
690
|
|
|
695
691
|
saveRegistry(registry);
|
|
696
|
-
console.log(`${C.green}[+]
|
|
692
|
+
console.log(`${C.green}[+] Scan completed.${C.reset}\n`);
|
|
697
693
|
|
|
698
694
|
if (newDetected.length > 0) {
|
|
699
|
-
drawBox("
|
|
700
|
-
`${C.green}
|
|
701
|
-
...newDetected.slice(0, 5).map(cmd => `
|
|
702
|
-
newDetected.length > 5 ? ` ...
|
|
695
|
+
drawBox("[+] NEW COMMANDS DETECTED!", [
|
|
696
|
+
`${C.green}You can now use the following commands:${C.reset}`,
|
|
697
|
+
...newDetected.slice(0, 5).map(cmd => ` * ${C.yellow}${cmd}${C.reset} <prompt>`),
|
|
698
|
+
newDetected.length > 5 ? ` ...and ${newDetected.length - 5} more commands.` : '',
|
|
703
699
|
``,
|
|
704
|
-
`${C.cyan}
|
|
700
|
+
`${C.cyan}Type /skill or /mcp to view all available commands.${C.reset}`
|
|
705
701
|
], C.green);
|
|
706
702
|
} else {
|
|
707
|
-
drawBox("
|
|
708
|
-
`
|
|
709
|
-
`
|
|
703
|
+
drawBox("[i] SCAN RESULTS", [
|
|
704
|
+
`No new Skills or MCP servers detected.`,
|
|
705
|
+
`Type ${C.yellow}/skill${C.reset} or ${C.yellow}/mcp${C.reset} to view active commands.`
|
|
710
706
|
], C.gray);
|
|
711
707
|
}
|
|
712
708
|
console.log('');
|
|
713
709
|
}
|
|
714
710
|
|
|
715
711
|
function askConfig(callback) {
|
|
716
|
-
console.log(`\n${C.cyan}[+]
|
|
712
|
+
console.log(`\n${C.cyan}[+] Select AI Provider:${C.reset}`);
|
|
717
713
|
console.log(` ${C.yellow}1.${C.reset} OpenRouter`);
|
|
718
714
|
console.log(` ${C.yellow}2.${C.reset} Groq`);
|
|
719
715
|
console.log(` ${C.yellow}3.${C.reset} NVIDIA NIM`);
|
|
720
716
|
console.log(` ${C.yellow}4.${C.reset} OpenAI`);
|
|
721
717
|
console.log(` ${C.yellow}5.${C.reset} Anthropic`);
|
|
722
|
-
console.log(` ${C.yellow}6.${C.reset} Google Gemini
|
|
718
|
+
console.log(` ${C.yellow}6.${C.reset} Google Gemini`);
|
|
723
719
|
console.log(` ${C.yellow}7.${C.reset} Custom / Auto-Detect Endpoint\n`);
|
|
724
720
|
|
|
725
721
|
rl.question(`${C.yellow}[>] Select Provider (1-7):${C.reset} `, (choice) => {
|
|
@@ -751,6 +747,7 @@ function askConfig(callback) {
|
|
|
751
747
|
});
|
|
752
748
|
}
|
|
753
749
|
|
|
750
|
+
// FETCH MODELS UNIVERSAL FOR ALL PROVIDERS
|
|
754
751
|
async function fetchAvailableModels() {
|
|
755
752
|
if (!config.apiKey) {
|
|
756
753
|
console.log(`${C.red}[x] API Key is not set. Use /config first.${C.reset}\n`);
|
|
@@ -761,49 +758,85 @@ async function fetchAvailableModels() {
|
|
|
761
758
|
|
|
762
759
|
try {
|
|
763
760
|
let url = config.endpoint;
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return startPrompt();
|
|
767
|
-
}
|
|
761
|
+
let fetchUrl = '';
|
|
762
|
+
let headers = {};
|
|
768
763
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
headers
|
|
764
|
+
if (url.includes('googleapis.com')) {
|
|
765
|
+
fetchUrl = `https://generativelanguage.googleapis.com/v1beta/models?key=${config.apiKey}`;
|
|
766
|
+
} else if (url.includes('anthropic.com')) {
|
|
767
|
+
fetchUrl = 'https://api.anthropic.com/v1/models';
|
|
768
|
+
headers = {
|
|
769
|
+
'x-api-key': config.apiKey,
|
|
770
|
+
'anthropic-version': '2023-06-01'
|
|
771
|
+
};
|
|
772
|
+
} else {
|
|
773
|
+
let baseUrl = url.replace(/\/chat\/completions$/, '').replace(/\/$/, '');
|
|
774
|
+
fetchUrl = baseUrl.endsWith('/models') ? baseUrl : `${baseUrl}/models`;
|
|
775
|
+
headers = {
|
|
774
776
|
'Authorization': `Bearer ${config.apiKey}`,
|
|
775
777
|
'Content-Type': 'application/json'
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
+
};
|
|
779
|
+
}
|
|
778
780
|
|
|
781
|
+
const response = await fetch(fetchUrl, { method: 'GET', headers: headers });
|
|
779
782
|
const data = await response.json();
|
|
780
783
|
|
|
781
|
-
|
|
784
|
+
let extractedModels = [];
|
|
785
|
+
|
|
786
|
+
if (data.data && Array.isArray(data.data)) {
|
|
787
|
+
extractedModels = data.data.map(m => ({ id: typeof m === 'string' ? m : (m.id || m.name) }));
|
|
788
|
+
} else if (data.models && Array.isArray(data.models)) {
|
|
789
|
+
extractedModels = data.models.map(m => ({ id: typeof m === 'string' ? m : (m.name?.replace('models/', '') || m.id) }));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
if (extractedModels.length > 0) {
|
|
782
793
|
interactiveModelSelector(
|
|
783
|
-
|
|
794
|
+
extractedModels,
|
|
784
795
|
(selectedModelId) => {
|
|
785
796
|
config.model = selectedModelId;
|
|
786
797
|
saveConfig(config);
|
|
787
|
-
|
|
788
|
-
console.log(`${C.green}[
|
|
798
|
+
clearScreenFull();
|
|
799
|
+
console.log(`${C.green}[+] Model successfully updated to: ${config.model}${C.reset}\n`);
|
|
789
800
|
startPrompt();
|
|
790
801
|
},
|
|
791
802
|
() => {
|
|
792
|
-
|
|
793
|
-
console.log(`${C.gray}
|
|
803
|
+
clearScreenFull();
|
|
804
|
+
console.log(`${C.gray}Model selection cancelled.${C.reset}\n`);
|
|
794
805
|
startPrompt();
|
|
795
806
|
}
|
|
796
807
|
);
|
|
797
808
|
} else {
|
|
798
|
-
console.log(`${C.red}[x]
|
|
809
|
+
console.log(`${C.red}[x] Failed to auto-fetch models from ${config.provider}.${C.reset}`);
|
|
810
|
+
console.log(`${C.yellow}[i] Tip: Use ${C.bold}/custom-models${C.reset}${C.yellow} to manually set the exact model ID.${C.reset}\n`);
|
|
799
811
|
startPrompt();
|
|
800
812
|
}
|
|
801
813
|
} catch (err) {
|
|
802
|
-
console.log(`${C.red}[x] Error fetching models: ${err.message}${C.reset}
|
|
814
|
+
console.log(`${C.red}[x] Error fetching models: ${err.message}${C.reset}`);
|
|
815
|
+
console.log(`${C.yellow}[i] Tip: Use ${C.bold}/custom-models${C.reset}${C.yellow} to set model ID manually.${C.reset}\n`);
|
|
803
816
|
startPrompt();
|
|
804
817
|
}
|
|
805
818
|
}
|
|
806
819
|
|
|
820
|
+
// /custom-models HANDLER
|
|
821
|
+
function handleCustomModels() {
|
|
822
|
+
console.log(`\n${C.cyan}=== [>] SET CUSTOM MODEL (HIGH-PRECISION) ===${C.reset}`);
|
|
823
|
+
console.log(`${C.gray}Active Provider : ${C.yellow}${config.provider || 'Not configured'}${C.reset}`);
|
|
824
|
+
console.log(`${C.gray}Current Model : ${C.green}${config.model || 'Not configured'}${C.reset}\n`);
|
|
825
|
+
|
|
826
|
+
rl.question(`${C.yellow}[>] Enter exact Model ID/Name:${C.reset} `, (inputModel) => {
|
|
827
|
+
const cleanModel = inputModel.trim();
|
|
828
|
+
if (!cleanModel) {
|
|
829
|
+
console.log(`${C.red}[x] Model name cannot be empty.${C.reset}\n`);
|
|
830
|
+
return startPrompt();
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
config.model = cleanModel;
|
|
834
|
+
saveConfig(config);
|
|
835
|
+
console.log(`\n${C.green}[+] Model successfully updated to: ${C.bold}${config.model}${C.reset}\n`);
|
|
836
|
+
startPrompt();
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
|
|
807
840
|
async function handleChat(prompt, forcedTool = null) {
|
|
808
841
|
if (!config.apiKey) {
|
|
809
842
|
console.log(`${C.yellow}[!] API Key is not set.${C.reset}`);
|
|
@@ -811,7 +844,7 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
811
844
|
}
|
|
812
845
|
|
|
813
846
|
if (forcedTool) {
|
|
814
|
-
console.log(`${C.cyan}[
|
|
847
|
+
console.log(`${C.cyan}[+] Executing via ${forcedTool.type.toUpperCase()}: ${forcedTool.name}${C.reset}`);
|
|
815
848
|
}
|
|
816
849
|
|
|
817
850
|
console.log(`${C.yellow}TMPA CLI processing...${C.reset}`);
|
|
@@ -822,10 +855,11 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
822
855
|
let bodyData = {};
|
|
823
856
|
|
|
824
857
|
const toolsContext = getActiveToolsContext(forcedTool);
|
|
825
|
-
const fullPrompt = (prompt || "
|
|
858
|
+
const fullPrompt = (prompt || "Execute this tool context.") + toolsContext;
|
|
826
859
|
|
|
827
860
|
if (url.includes('googleapis.com')) {
|
|
828
|
-
|
|
861
|
+
const activeModel = config.model || 'gemini-2.5-flash';
|
|
862
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${activeModel}:generateContent?key=${config.apiKey}`;
|
|
829
863
|
bodyData = { contents: [{ parts: [{ text: fullPrompt }] }] };
|
|
830
864
|
} else {
|
|
831
865
|
if (!url.endsWith('/chat/completions')) {
|
|
@@ -848,7 +882,6 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
848
882
|
|
|
849
883
|
let aiResponse = "";
|
|
850
884
|
|
|
851
|
-
// DUKUNGAN UNTUK REASONING MODELS & STANDAR OPENAI/GEMINI
|
|
852
885
|
if (data.choices && data.choices[0] && data.choices[0].message) {
|
|
853
886
|
const msg = data.choices[0].message;
|
|
854
887
|
aiResponse = msg.content || msg.reasoning || msg.reasoning_content || "";
|
|
@@ -872,23 +905,23 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
872
905
|
}
|
|
873
906
|
|
|
874
907
|
function handleUninstall() {
|
|
875
|
-
rl.question(`${C.red}[!]
|
|
908
|
+
rl.question(`${C.red}[!] Are you sure you want to uninstall TMPA CLI? (y/N):${C.reset} `, (answer) => {
|
|
876
909
|
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
877
|
-
console.log(`\n${C.yellow}[...]
|
|
910
|
+
console.log(`\n${C.yellow}[...] Cleaning configurations and uninstalling TMPA CLI...${C.reset}`);
|
|
878
911
|
try {
|
|
879
912
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
880
913
|
fs.unlinkSync(CONFIG_FILE);
|
|
881
914
|
}
|
|
882
|
-
console.log(`${C.green}[+]
|
|
915
|
+
console.log(`${C.green}[+] Local configuration cleaned.${C.reset}`);
|
|
883
916
|
execSync('npm uninstall -g tmpa-cli', { stdio: 'inherit' });
|
|
884
|
-
console.log(`\n${C.green}[+] TMPA CLI
|
|
917
|
+
console.log(`\n${C.green}[+] TMPA CLI successfully uninstalled. Goodbye!${C.reset}\n`);
|
|
885
918
|
process.exit(0);
|
|
886
919
|
} catch (err) {
|
|
887
|
-
console.log(`\n${C.red}[x]
|
|
920
|
+
console.log(`\n${C.red}[x] Uninstall failed automatically. Please run: npm uninstall -g tmpa-cli manually.${C.reset}\n`);
|
|
888
921
|
process.exit(0);
|
|
889
922
|
}
|
|
890
923
|
} else {
|
|
891
|
-
console.log(`${C.gray}
|
|
924
|
+
console.log(`${C.gray}Uninstall cancelled.${C.reset}\n`);
|
|
892
925
|
startPrompt();
|
|
893
926
|
}
|
|
894
927
|
});
|
|
@@ -908,6 +941,8 @@ function startPrompt() {
|
|
|
908
941
|
askConfig(() => startPrompt());
|
|
909
942
|
} else if (cmd === '/models') {
|
|
910
943
|
fetchAvailableModels();
|
|
944
|
+
} else if (cmd === '/custom-models') {
|
|
945
|
+
handleCustomModels();
|
|
911
946
|
} else if (cmd === '/skill' || cmd === '/skills') {
|
|
912
947
|
listSkills();
|
|
913
948
|
startPrompt();
|
|
@@ -938,7 +973,7 @@ function startPrompt() {
|
|
|
938
973
|
if (registry.skills && registry.skills[skillName]) {
|
|
939
974
|
handleChat(userPrompt, { type: 'skill', name: skillName, target: registry.skills[skillName].path });
|
|
940
975
|
} else {
|
|
941
|
-
console.log(`${C.red}[x] Skill "${skillName}"
|
|
976
|
+
console.log(`${C.red}[x] Skill "${skillName}" not found. Type /skill to list available skills.${C.reset}\n`);
|
|
942
977
|
startPrompt();
|
|
943
978
|
}
|
|
944
979
|
} else if (cmd.startsWith('/mcp-')) {
|
|
@@ -961,7 +996,7 @@ function startPrompt() {
|
|
|
961
996
|
authToken: registry.mcp[mcpName].authToken
|
|
962
997
|
});
|
|
963
998
|
} else {
|
|
964
|
-
console.log(`${C.red}[x] MCP "${mcpName}"
|
|
999
|
+
console.log(`${C.red}[x] MCP "${mcpName}" not found. Type /mcp to list active MCP servers.${C.reset}\n`);
|
|
965
1000
|
startPrompt();
|
|
966
1001
|
}
|
|
967
1002
|
} else if (cmd === '') {
|