tmpa-cli 1.0.16 → 1.0.18
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 +160 -67
- package/package.json +6 -2
package/bin/index.js
CHANGED
|
@@ -4,7 +4,7 @@ const readline = require('readline');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
-
const { execSync
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
8
|
|
|
9
9
|
// Base Paths
|
|
10
10
|
const TMPA_DIR = path.join(os.homedir(), '.tmpa');
|
|
@@ -43,7 +43,6 @@ const PROVIDERS = {
|
|
|
43
43
|
'7': { name: 'Custom / Auto-Detect', endpoint: '', defaultModel: '' }
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
// Config & Registry Helpers
|
|
47
46
|
function loadConfig() {
|
|
48
47
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
49
48
|
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch (e) { return {}; }
|
|
@@ -66,7 +65,6 @@ function saveRegistry(registry) {
|
|
|
66
65
|
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(registry, null, 2));
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
// UI Popup Card Helper
|
|
70
68
|
function drawBox(title, contentLines, borderColor = C.cyan) {
|
|
71
69
|
const width = 64;
|
|
72
70
|
console.log(`${borderColor}┌${'─'.repeat(width - 2)}┐${C.reset}`);
|
|
@@ -92,47 +90,173 @@ ${C.c3} ██║ ██║╚██╔╝██║██╔═══╝ █
|
|
|
92
90
|
${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ ██║${C.reset}
|
|
93
91
|
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
94
92
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
95
|
-
${C.reset}The Multi Platform AI ${C.green}[
|
|
96
|
-
${C.gray}/config | /models | /skill | /mcp | /
|
|
93
|
+
${C.reset}The Multi Platform AI ${C.green}[MCP Hub & Tool Engine]${C.reset}
|
|
94
|
+
${C.gray}/config | /models | /skill | /mcp | /new-mcp | /scan | /exit${C.reset}
|
|
97
95
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
98
96
|
`);
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
function readResourceContent(targetPath) {
|
|
100
|
+
if (!fs.existsSync(targetPath)) return "[Resource file/directory not found on local system]";
|
|
101
|
+
|
|
102
|
+
const stat = fs.statSync(targetPath);
|
|
103
|
+
if (stat.isFile()) {
|
|
104
|
+
try {
|
|
105
|
+
return fs.readFileSync(targetPath, 'utf8');
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return `[Error reading file: ${e.message}]`;
|
|
108
|
+
}
|
|
109
|
+
} else if (stat.isDirectory()) {
|
|
110
|
+
const candidateFiles = ['SKILL.md', 'skill.md', 'PROMPT.md', 'README.md', 'index.js', 'index.ts', 'main.py'];
|
|
111
|
+
for (const file of candidateFiles) {
|
|
112
|
+
const full = path.join(targetPath, file);
|
|
113
|
+
if (fs.existsSync(full)) {
|
|
114
|
+
try {
|
|
115
|
+
return `--- Content from ${file} ---\n` + fs.readFileSync(full, 'utf8');
|
|
116
|
+
} catch (e) {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
104
119
|
|
|
120
|
+
try {
|
|
121
|
+
const files = fs.readdirSync(targetPath);
|
|
122
|
+
let combinedText = `Directory Contents for ${path.basename(targetPath)}:\n`;
|
|
123
|
+
files.slice(0, 5).forEach(f => {
|
|
124
|
+
const fp = path.join(targetPath, f);
|
|
125
|
+
if (fs.statSync(fp).isFile()) {
|
|
126
|
+
combinedText += `\n--- File: ${f} ---\n` + fs.readFileSync(fp, 'utf8').slice(0, 2000);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return combinedText;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
return `[Directory found at ${targetPath}, but unable to parse files]`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return "[Unknown resource format]";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getActiveToolsContext(forcedTool = null) {
|
|
105
138
|
if (forcedTool) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
let rawContent = "";
|
|
140
|
+
if (forcedTool.type === 'mcp') {
|
|
141
|
+
rawContent = `MCP Server Info:
|
|
142
|
+
Name: ${forcedTool.name}
|
|
143
|
+
Target/Endpoint: ${forcedTool.target}
|
|
144
|
+
Type: ${forcedTool.mcpType || 'HTTP/SSE'}
|
|
145
|
+
Auth Token: ${forcedTool.authToken ? 'Provided' : 'None'}`;
|
|
146
|
+
} else {
|
|
147
|
+
rawContent = readResourceContent(forcedTool.target);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return `\n\n[STRICT TOOL EXECUTION SYSTEM DIRECTIVE]
|
|
151
|
+
YOU ARE NOW STRICTLY ACTING AS THE FOLLOWING ${forcedTool.type.toUpperCase()} TOOL: "${forcedTool.name}".
|
|
152
|
+
DO NOT REFER TO OTHER TOOLS OR GENERAL ASSISTANT CAPABILITIES. FOCUS 100% ON EXECUTING THIS TOOL INSTRUCTION.
|
|
153
|
+
|
|
154
|
+
--- TOOL INSTRUCTION & CODE CONTENT ---
|
|
155
|
+
${rawContent}
|
|
156
|
+
--- END TOOL INSTRUCTION ---
|
|
157
|
+
|
|
158
|
+
Executing User Prompt under this tool context only:\n`;
|
|
110
159
|
}
|
|
111
|
-
|
|
160
|
+
|
|
161
|
+
let contextParts = [];
|
|
112
162
|
const activeSkills = Object.keys(registry.skills || {});
|
|
113
163
|
if (activeSkills.length > 0) {
|
|
114
164
|
contextParts.push("AVAILABLE SKILLS:");
|
|
115
|
-
activeSkills.forEach(name => {
|
|
116
|
-
const s = registry.skills[name];
|
|
117
|
-
contextParts.push(`- /skill-${name} -> Path: ${s.path}`);
|
|
118
|
-
});
|
|
165
|
+
activeSkills.forEach(name => contextParts.push(`- /skill-${name}`));
|
|
119
166
|
}
|
|
120
167
|
|
|
121
168
|
const activeMCP = Object.keys(registry.mcp || {});
|
|
122
169
|
if (activeMCP.length > 0) {
|
|
123
170
|
contextParts.push("AVAILABLE MCP SERVERS:");
|
|
124
|
-
activeMCP.forEach(name => {
|
|
171
|
+
activeMCP.forEach(name => contextParts.push(`- /mcp-${name}`));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (contextParts.length === 0) return "";
|
|
175
|
+
return "\n\n[SYSTEM ENVIRONMENT SUMMARY]\n" + contextParts.join("\n") + "\n";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// RUANGAN KHUSUS MCP CENTER
|
|
179
|
+
function showMCPHub() {
|
|
180
|
+
console.log(`\n${C.c4}============================================================${C.reset}`);
|
|
181
|
+
console.log(`${C.c4} 🔌 MCP (MODEL CONTEXT PROTOCOL) CENTER ${C.reset}`);
|
|
182
|
+
console.log(`${C.c4}============================================================${C.reset}\n`);
|
|
183
|
+
|
|
184
|
+
const keys = Object.keys(registry.mcp || {});
|
|
185
|
+
|
|
186
|
+
if (keys.length === 0) {
|
|
187
|
+
console.log(`${C.yellow} [!] Belum ada MCP Server yang terhubung saat ini.${C.reset}`);
|
|
188
|
+
console.log(`${C.gray} Gunakan perintah ${C.green}/new-mcp${C.gray} untuk mendaftarkan MCP baru.${C.reset}\n`);
|
|
189
|
+
} else {
|
|
190
|
+
console.log(`${C.bold}Daftar MCP Server Aktif:${C.reset}`);
|
|
191
|
+
keys.forEach((name, i) => {
|
|
125
192
|
const m = registry.mcp[name];
|
|
126
|
-
|
|
193
|
+
const typeBadge = m.type === 'remote' ? `${C.cyan}[Remote SSE/HTTP]${C.reset}` : `${C.green}[Local Stdio]${C.reset}`;
|
|
194
|
+
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.bold}${name}${C.reset} ${typeBadge}`);
|
|
195
|
+
console.log(` ↳ Target: ${C.gray}${m.target}${C.reset}`);
|
|
196
|
+
if (m.authToken) console.log(` ↳ Auth : ${C.green}Encrypted/Token set${C.reset}`);
|
|
127
197
|
});
|
|
198
|
+
console.log('');
|
|
128
199
|
}
|
|
129
200
|
|
|
130
|
-
|
|
201
|
+
const lines = [
|
|
202
|
+
`${C.bold}Command Instan MCP:${C.reset}`,
|
|
203
|
+
`${C.yellow}/new-mcp${C.reset} 👉 Tambah MCP Server Baru (Wizard)`,
|
|
204
|
+
`${C.yellow}/mcp-<nama>${C.reset} <prompt> 👉 Eksekusi MCP secara spesifik`,
|
|
205
|
+
`${C.yellow}/scan${C.reset} 👉 Pindai folder .mcp otomatis`
|
|
206
|
+
];
|
|
131
207
|
|
|
132
|
-
|
|
208
|
+
drawBox("💡 PETUNJUK RUANG MCP", lines, C.c4);
|
|
209
|
+
console.log('');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// WIZARD KONFIGURASI MCP BARU (/new-mcp)
|
|
213
|
+
function createNewMCPWizard(callback) {
|
|
214
|
+
console.log(`\n${C.cyan}=== 🛠️ SETUP MCP (MODEL CONTEXT PROTOCOL) BARU ===${C.reset}\n`);
|
|
215
|
+
|
|
216
|
+
rl.question(`${C.yellow}[1/4] Masukkan Nama MCP Server (misal: stitch, github, filesystem):${C.reset} `, (nameInput) => {
|
|
217
|
+
const mcpName = nameInput.trim().toLowerCase().replace(/\s+/g, '-');
|
|
218
|
+
if (!mcpName) {
|
|
219
|
+
console.log(`${C.red}[x] Nama MCP tidak boleh kosong.${C.reset}\n`);
|
|
220
|
+
return callback();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`\n${C.cyan}Pilih Tipe Koneksi MCP:${C.reset}`);
|
|
224
|
+
console.log(` ${C.yellow}1.${C.reset} Remote URL / SSE Endpoint (misal: https://mcp.example.com/sse)`);
|
|
225
|
+
console.log(` ${C.yellow}2.${C.reset} Local Command / File Path (misal: npx -y @modelcontextprotocol/server-filesystem)`);
|
|
226
|
+
|
|
227
|
+
rl.question(`\n${C.yellow}[2/4] Pilih Tipe (1/2):${C.reset} `, (typeChoice) => {
|
|
228
|
+
const isRemote = typeChoice.trim() === '1';
|
|
229
|
+
|
|
230
|
+
rl.question(`${C.yellow}[3/4] Masukkan Target URL / Command Path:${C.reset} `, (targetInput) => {
|
|
231
|
+
const target = targetInput.trim();
|
|
232
|
+
if (!target) {
|
|
233
|
+
console.log(`${C.red}[x] Target tidak boleh kosong.${C.reset}\n`);
|
|
234
|
+
return callback();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
rl.question(`${C.yellow}[4/4] Masukkan Auth Token / API Key (Tekan Enter jika tidak ada):${C.reset} `, (tokenInput) => {
|
|
238
|
+
const authToken = tokenInput.trim();
|
|
239
|
+
|
|
240
|
+
registry.mcp = registry.mcp || {};
|
|
241
|
+
registry.mcp[mcpName] = {
|
|
242
|
+
target: target,
|
|
243
|
+
type: isRemote ? 'remote' : 'local',
|
|
244
|
+
authToken: authToken || null,
|
|
245
|
+
connectedAt: new Date().toISOString(),
|
|
246
|
+
status: 'Connected'
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
saveRegistry(registry);
|
|
250
|
+
|
|
251
|
+
console.log(`\n${C.green}[✔] MCP Server "${mcpName}" berhasil ditambahkan!${C.reset}`);
|
|
252
|
+
console.log(`${C.gray}Gunakan perintah instan: ${C.yellow}/mcp-${mcpName} <prompt kamu>${C.reset}\n`);
|
|
253
|
+
callback();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
133
258
|
}
|
|
134
259
|
|
|
135
|
-
// Handlers for Skills with Dynamic Command Helper
|
|
136
260
|
function listSkills() {
|
|
137
261
|
console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
|
|
138
262
|
const keys = Object.keys(registry.skills || {});
|
|
@@ -148,9 +272,8 @@ function listSkills() {
|
|
|
148
272
|
});
|
|
149
273
|
console.log('');
|
|
150
274
|
|
|
151
|
-
// Interactive Action Helper Popup with dynamic slash commands
|
|
152
275
|
const lines = [
|
|
153
|
-
`${C.bold}Perintah Instan
|
|
276
|
+
`${C.bold}Perintah Instan Skill:${C.reset}`,
|
|
154
277
|
``
|
|
155
278
|
];
|
|
156
279
|
|
|
@@ -158,46 +281,10 @@ function listSkills() {
|
|
|
158
281
|
lines.push(` ${C.yellow}/skill-${name}${C.reset} <prompt kamu>`);
|
|
159
282
|
});
|
|
160
283
|
|
|
161
|
-
lines.push(``);
|
|
162
|
-
lines.push(`${C.gray}Contoh: ${C.yellow}/skill-${keys[0]} buatkan animasi intro${C.reset}`);
|
|
163
|
-
|
|
164
284
|
drawBox("⚡ COMMAND INSTAN SKILL TERSEDIA", lines, C.c1);
|
|
165
285
|
console.log('');
|
|
166
286
|
}
|
|
167
287
|
|
|
168
|
-
// Handlers for MCP with Dynamic Command Helper
|
|
169
|
-
function listMCP() {
|
|
170
|
-
console.log(`\n${C.cyan}=== Registered MCP (Model Context Protocol) Servers ===${C.reset}`);
|
|
171
|
-
const keys = Object.keys(registry.mcp || {});
|
|
172
|
-
|
|
173
|
-
if (keys.length === 0) {
|
|
174
|
-
console.log(`${C.gray}Belum ada MCP server terhubung. Ketik ${C.yellow}/scan${C.gray} atau ${C.yellow}/connect mcp <target>${C.reset}\n`);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
keys.forEach((name, i) => {
|
|
179
|
-
const item = registry.mcp[name];
|
|
180
|
-
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type}]`);
|
|
181
|
-
});
|
|
182
|
-
console.log('');
|
|
183
|
-
|
|
184
|
-
// Interactive Action Helper Popup with dynamic slash commands
|
|
185
|
-
const lines = [
|
|
186
|
-
`${C.bold}Perintah Instan MCP yang Bisa Kamu Ketik Langsung:${C.reset}`,
|
|
187
|
-
``
|
|
188
|
-
];
|
|
189
|
-
|
|
190
|
-
keys.forEach(name => {
|
|
191
|
-
lines.push(` ${C.yellow}/mcp-${name}${C.reset} <prompt kamu>`);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
lines.push(``);
|
|
195
|
-
lines.push(`${C.gray}Contoh: ${C.yellow}/mcp-${keys[0]} sinkronkan file terbaru${C.reset}`);
|
|
196
|
-
|
|
197
|
-
drawBox("🔌 COMMAND INSTAN MCP TERSEDIA", lines, C.c4);
|
|
198
|
-
console.log('');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
288
|
function connectResource(inputArgs) {
|
|
202
289
|
const parts = inputArgs.trim().split(/\s+/);
|
|
203
290
|
const type = parts[0]?.toLowerCase();
|
|
@@ -509,7 +596,7 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
509
596
|
return startPrompt();
|
|
510
597
|
}
|
|
511
598
|
|
|
512
|
-
console.log(`\n${C.c1}TMPA CLI (${config.model || 'AI'}) :${C.reset}
|
|
599
|
+
console.log(`\n${C.c1}TMPA CLI (${config.model || 'AI'}) :${C.reset}\n${aiResponse}\n`);
|
|
513
600
|
|
|
514
601
|
} catch (error) {
|
|
515
602
|
console.log(`\n${C.red}[x] Fetch Error: ${error.message}${C.reset}\n`);
|
|
@@ -559,8 +646,10 @@ function startPrompt() {
|
|
|
559
646
|
listSkills();
|
|
560
647
|
startPrompt();
|
|
561
648
|
} else if (cmd === '/mcp') {
|
|
562
|
-
|
|
649
|
+
showMCPHub();
|
|
563
650
|
startPrompt();
|
|
651
|
+
} else if (cmd === '/new-mcp') {
|
|
652
|
+
createNewMCPWizard(() => startPrompt());
|
|
564
653
|
} else if (cmd.startsWith('/connect')) {
|
|
565
654
|
connectResource(cmd.replace('/connect', ''));
|
|
566
655
|
startPrompt();
|
|
@@ -570,8 +659,7 @@ function startPrompt() {
|
|
|
570
659
|
} else if (cmd === '/uninstall') {
|
|
571
660
|
handleUninstall();
|
|
572
661
|
} else if (cmd.startsWith('/skill-')) {
|
|
573
|
-
|
|
574
|
-
const fullCmd = cmd.slice(7).trim(); // remove '/skill-'
|
|
662
|
+
const fullCmd = cmd.slice(7).trim();
|
|
575
663
|
const spaceIdx = fullCmd.indexOf(' ');
|
|
576
664
|
let skillName = fullCmd;
|
|
577
665
|
let userPrompt = '';
|
|
@@ -588,8 +676,7 @@ function startPrompt() {
|
|
|
588
676
|
startPrompt();
|
|
589
677
|
}
|
|
590
678
|
} else if (cmd.startsWith('/mcp-')) {
|
|
591
|
-
|
|
592
|
-
const fullCmd = cmd.slice(5).trim(); // remove '/mcp-'
|
|
679
|
+
const fullCmd = cmd.slice(5).trim();
|
|
593
680
|
const spaceIdx = fullCmd.indexOf(' ');
|
|
594
681
|
let mcpName = fullCmd;
|
|
595
682
|
let userPrompt = '';
|
|
@@ -600,7 +687,13 @@ function startPrompt() {
|
|
|
600
687
|
}
|
|
601
688
|
|
|
602
689
|
if (registry.mcp && registry.mcp[mcpName]) {
|
|
603
|
-
handleChat(userPrompt, {
|
|
690
|
+
handleChat(userPrompt, {
|
|
691
|
+
type: 'mcp',
|
|
692
|
+
name: mcpName,
|
|
693
|
+
target: registry.mcp[mcpName].target,
|
|
694
|
+
mcpType: registry.mcp[mcpName].type,
|
|
695
|
+
authToken: registry.mcp[mcpName].authToken
|
|
696
|
+
});
|
|
604
697
|
} else {
|
|
605
698
|
console.log(`${C.red}[x] MCP "${mcpName}" tidak ditemukan di registry. Ketik /mcp untuk melihat daftar.${C.reset}\n`);
|
|
606
699
|
startPrompt();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmpa-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
"author": "",
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@remotion/cli": "^4.0.506",
|
|
17
18
|
"chalk": "^4.1.2",
|
|
18
19
|
"commander": "^11.0.0",
|
|
19
|
-
"ora": "^5.4.1"
|
|
20
|
+
"ora": "^5.4.1",
|
|
21
|
+
"react": "^19.2.8",
|
|
22
|
+
"react-dom": "^19.2.8",
|
|
23
|
+
"remotion": "^4.0.506"
|
|
20
24
|
}
|
|
21
25
|
}
|