tmpa-cli 1.0.15 → 1.0.17
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 +141 -67
- package/package.json +1 -1
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,14 +65,12 @@ 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
|
-
const width =
|
|
69
|
+
const width = 64;
|
|
72
70
|
console.log(`${borderColor}┌${'─'.repeat(width - 2)}┐${C.reset}`);
|
|
73
71
|
console.log(`${borderColor}│ ${C.bold}${title.padEnd(width - 4)}${C.reset}${borderColor} │${C.reset}`);
|
|
74
72
|
console.log(`${borderColor}├${'─'.repeat(width - 2)}┤${C.reset}`);
|
|
75
73
|
contentLines.forEach(line => {
|
|
76
|
-
// Basic text padding layout
|
|
77
74
|
console.log(`${borderColor}│${C.reset} ${line.padEnd(width - 4)} ${borderColor}│${C.reset}`);
|
|
78
75
|
});
|
|
79
76
|
console.log(`${borderColor}└${'─'.repeat(width - 2)}┘${C.reset}`);
|
|
@@ -93,41 +90,88 @@ ${C.c3} ██║ ██║╚██╔╝██║██╔═══╝ █
|
|
|
93
90
|
${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ ██║${C.reset}
|
|
94
91
|
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
95
92
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
96
|
-
${C.reset}The Multi Platform AI ${C.green}[
|
|
93
|
+
${C.reset}The Multi Platform AI ${C.green}[Isolated Tool Execution Engine]${C.reset}
|
|
97
94
|
${C.gray}/config | /models | /skill | /mcp | /connect | /scan | /exit${C.reset}
|
|
98
95
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
99
96
|
`);
|
|
100
97
|
}
|
|
101
98
|
|
|
102
|
-
//
|
|
103
|
-
function
|
|
99
|
+
// Helper untuk membaca isi file skill/MCP secara otomatis
|
|
100
|
+
function readResourceContent(targetPath) {
|
|
101
|
+
if (!fs.existsSync(targetPath)) return "[Resource file/directory not found on local system]";
|
|
102
|
+
|
|
103
|
+
const stat = fs.statSync(targetPath);
|
|
104
|
+
if (stat.isFile()) {
|
|
105
|
+
try {
|
|
106
|
+
return fs.readFileSync(targetPath, 'utf8');
|
|
107
|
+
} catch (e) {
|
|
108
|
+
return `[Error reading file: ${e.message}]`;
|
|
109
|
+
}
|
|
110
|
+
} else if (stat.isDirectory()) {
|
|
111
|
+
// Cari file instruksi utama seperti SKILL.md, README.md, index.js, prompt.txt
|
|
112
|
+
const candidateFiles = ['SKILL.md', 'skill.md', 'PROMPT.md', 'README.md', 'index.js', 'index.ts', 'main.py'];
|
|
113
|
+
for (const file of candidateFiles) {
|
|
114
|
+
const full = path.join(targetPath, file);
|
|
115
|
+
if (fs.existsSync(full)) {
|
|
116
|
+
try {
|
|
117
|
+
return `--- Content from ${file} ---\n` + fs.readFileSync(full, 'utf8');
|
|
118
|
+
} catch (e) {}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Jika tidak ada file markdown/skrip khusus, baca beberapa file pertama
|
|
123
|
+
try {
|
|
124
|
+
const files = fs.readdirSync(targetPath);
|
|
125
|
+
let combinedText = `Directory Contents for ${path.basename(targetPath)}:\n`;
|
|
126
|
+
files.slice(0, 5).forEach(f => {
|
|
127
|
+
const fp = path.join(targetPath, f);
|
|
128
|
+
if (fs.statSync(fp).isFile()) {
|
|
129
|
+
combinedText += `\n--- File: ${f} ---\n` + fs.readFileSync(fp, 'utf8').slice(0, 2000);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return combinedText;
|
|
133
|
+
} catch (e) {
|
|
134
|
+
return `[Directory found at ${targetPath}, but unable to parse files]`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return "[Unknown resource format]";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// System Context Builder dengan ISOLASI KETAT
|
|
141
|
+
function getActiveToolsContext(forcedTool = null) {
|
|
142
|
+
// 1. Jika User Memanggil Tool Khusus (Misal: /skill-remotion-video)
|
|
143
|
+
if (forcedTool) {
|
|
144
|
+
const rawContent = readResourceContent(forcedTool.target);
|
|
145
|
+
|
|
146
|
+
return `\n\n[STRICT TOOL EXECUTION SYSTEM DIRECTIVE]
|
|
147
|
+
YOU ARE NOW STRICTLY ACTING AS THE FOLLOWING ${forcedTool.type.toUpperCase()} TOOL: "${forcedTool.name}".
|
|
148
|
+
DO NOT REFER TO OTHER TOOLS OR GENERAL ASSISTANT CAPABILITIES. FOCUS 100% ON EXECUTING THIS TOOL INSTRUCTION.
|
|
149
|
+
|
|
150
|
+
--- TOOL INSTRUCTION & CODE CONTENT ---
|
|
151
|
+
${rawContent}
|
|
152
|
+
--- END TOOL INSTRUCTION ---
|
|
153
|
+
|
|
154
|
+
Executing User Prompt under this tool context only:\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 2. Jika Chat Biasa (Tanpa Slash Specific Tool), tampilkan ringkasan umum
|
|
104
158
|
let contextParts = [];
|
|
105
|
-
|
|
106
159
|
const activeSkills = Object.keys(registry.skills || {});
|
|
107
160
|
if (activeSkills.length > 0) {
|
|
108
|
-
contextParts.push("AVAILABLE SKILLS:");
|
|
109
|
-
activeSkills.forEach(name => {
|
|
110
|
-
const s = registry.skills[name];
|
|
111
|
-
contextParts.push(`- Skill: ${name} (Path: ${s.path})`);
|
|
112
|
-
});
|
|
161
|
+
contextParts.push("AVAILABLE SKILLS (Use /skill-<name> to invoke directly):");
|
|
162
|
+
activeSkills.forEach(name => contextParts.push(`- /skill-${name}`));
|
|
113
163
|
}
|
|
114
164
|
|
|
115
165
|
const activeMCP = Object.keys(registry.mcp || {});
|
|
116
166
|
if (activeMCP.length > 0) {
|
|
117
|
-
contextParts.push("AVAILABLE MCP SERVERS:");
|
|
118
|
-
activeMCP.forEach(name => {
|
|
119
|
-
const m = registry.mcp[name];
|
|
120
|
-
contextParts.push(`- MCP Server: ${name} (Target: ${m.target}, Type: ${m.type})`);
|
|
121
|
-
});
|
|
167
|
+
contextParts.push("AVAILABLE MCP SERVERS (Use /mcp-<name> to invoke directly):");
|
|
168
|
+
activeMCP.forEach(name => contextParts.push(`- /mcp-${name}`));
|
|
122
169
|
}
|
|
123
170
|
|
|
124
171
|
if (contextParts.length === 0) return "";
|
|
125
|
-
|
|
126
|
-
return "\n\n[SYSTEM CONTEXT: ACTIVE TOOLS & PROTOCOLS]\n" + contextParts.join("\n") +
|
|
127
|
-
"\nIf user wants to execute a skill/MCP, assist them using available tools.\n";
|
|
172
|
+
return "\n\n[SYSTEM ENVIRONMENT SUMMARY]\n" + contextParts.join("\n") + "\n";
|
|
128
173
|
}
|
|
129
174
|
|
|
130
|
-
// Handlers for Skills with Popup Helper
|
|
131
175
|
function listSkills() {
|
|
132
176
|
console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
|
|
133
177
|
const keys = Object.keys(registry.skills || {});
|
|
@@ -143,23 +187,21 @@ function listSkills() {
|
|
|
143
187
|
});
|
|
144
188
|
console.log('');
|
|
145
189
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
], C.c1);
|
|
190
|
+
const lines = [
|
|
191
|
+
`${C.bold}Perintah Instan (Fokus 100% Khusus Tool Tersebut):${C.reset}`,
|
|
192
|
+
``
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
keys.forEach(name => {
|
|
196
|
+
lines.push(` ${C.yellow}/skill-${name}${C.reset} <prompt kamu>`);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
drawBox("⚡ COMMAND INSTAN SKILL TERSEDIA", lines, C.c1);
|
|
157
200
|
console.log('');
|
|
158
201
|
}
|
|
159
202
|
|
|
160
|
-
// Handlers for MCP with Popup Helper
|
|
161
203
|
function listMCP() {
|
|
162
|
-
console.log(`\n${C.cyan}=== Registered MCP
|
|
204
|
+
console.log(`\n${C.cyan}=== Registered MCP Servers ===${C.reset}`);
|
|
163
205
|
const keys = Object.keys(registry.mcp || {});
|
|
164
206
|
|
|
165
207
|
if (keys.length === 0) {
|
|
@@ -173,17 +215,16 @@ function listMCP() {
|
|
|
173
215
|
});
|
|
174
216
|
console.log('');
|
|
175
217
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
`
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
], C.c4);
|
|
218
|
+
const lines = [
|
|
219
|
+
`${C.bold}Perintah Instan MCP (Fokus Khusus Tool MCP):${C.reset}`,
|
|
220
|
+
``
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
keys.forEach(name => {
|
|
224
|
+
lines.push(` ${C.yellow}/mcp-${name}${C.reset} <prompt kamu>`);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
drawBox("🔌 COMMAND INSTAN MCP TERSEDIA", lines, C.c4);
|
|
187
228
|
console.log('');
|
|
188
229
|
}
|
|
189
230
|
|
|
@@ -210,7 +251,7 @@ function connectResource(inputArgs) {
|
|
|
210
251
|
status: 'Active'
|
|
211
252
|
};
|
|
212
253
|
saveRegistry(registry);
|
|
213
|
-
console.log(`${C.green}[+] Skill "${skillName}"
|
|
254
|
+
console.log(`${C.green}[+] Skill "${skillName}" terhubung! Akses instan: /skill-${skillName}${C.reset}\n`);
|
|
214
255
|
} else if (type === 'mcp') {
|
|
215
256
|
const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
|
|
216
257
|
registry.mcp = registry.mcp || {};
|
|
@@ -221,13 +262,12 @@ function connectResource(inputArgs) {
|
|
|
221
262
|
status: 'Connected'
|
|
222
263
|
};
|
|
223
264
|
saveRegistry(registry);
|
|
224
|
-
console.log(`${C.green}[+] MCP
|
|
265
|
+
console.log(`${C.green}[+] MCP "${mcpName}" terhubung! Akses instan: /mcp-${mcpName}${C.reset}\n`);
|
|
225
266
|
} else {
|
|
226
267
|
console.log(`${C.red}[x] Tipe tidak dikenal. Gunakan "skill" atau "mcp".${C.reset}\n`);
|
|
227
268
|
}
|
|
228
269
|
}
|
|
229
270
|
|
|
230
|
-
// Deep Multi-Location Scanner Function with Result Popup Card
|
|
231
271
|
function scanResources() {
|
|
232
272
|
console.log(`\n${C.yellow}[...] Memindai lokasi internal, home user, Gemini CLI & Claude Desktop...${C.reset}`);
|
|
233
273
|
registry = loadRegistry();
|
|
@@ -261,10 +301,10 @@ function scanResources() {
|
|
|
261
301
|
|
|
262
302
|
if (type === 'skill' && !registry.skills[name]) {
|
|
263
303
|
registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
|
|
264
|
-
newDetected.push(
|
|
304
|
+
newDetected.push(`/skill-${name}`);
|
|
265
305
|
} else if (type === 'mcp' && !registry.mcp[name]) {
|
|
266
306
|
registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
|
|
267
|
-
newDetected.push(
|
|
307
|
+
newDetected.push(`/mcp-${name}`);
|
|
268
308
|
}
|
|
269
309
|
});
|
|
270
310
|
} catch (e) {}
|
|
@@ -272,24 +312,20 @@ function scanResources() {
|
|
|
272
312
|
});
|
|
273
313
|
|
|
274
314
|
saveRegistry(registry);
|
|
275
|
-
|
|
276
315
|
console.log(`${C.green}[+] Pemindaian selesai.${C.reset}\n`);
|
|
277
316
|
|
|
278
|
-
// POPUP CARD SUMMARY FOR SCAN RESULTS
|
|
279
317
|
if (newDetected.length > 0) {
|
|
280
|
-
drawBox("🎉
|
|
281
|
-
`${C.green}
|
|
282
|
-
...newDetected.slice(0,
|
|
283
|
-
newDetected.length >
|
|
318
|
+
drawBox("🎉 COMMAND INSTAN BARU TERDETEKSI!", [
|
|
319
|
+
`${C.green}Kamu sekarang bisa langsung menggunakan perintah ini:${C.reset}`,
|
|
320
|
+
...newDetected.slice(0, 5).map(cmd => ` • ${C.yellow}${cmd}${C.reset} <prompt>`),
|
|
321
|
+
newDetected.length > 5 ? ` ...dan ${newDetected.length - 5} command lainnya.` : '',
|
|
284
322
|
``,
|
|
285
|
-
`${C.
|
|
323
|
+
`${C.cyan}Ketik /skill atau /mcp untuk melihat semua command.${C.reset}`
|
|
286
324
|
], C.green);
|
|
287
325
|
} else {
|
|
288
326
|
drawBox("ℹ️ HASIL PEMINDAIAN", [
|
|
289
|
-
`Tidak ada
|
|
290
|
-
`
|
|
291
|
-
``,
|
|
292
|
-
`Gunakan ${C.yellow}/skill${C.reset} atau ${C.yellow}/mcp${C.reset} untuk melihat item aktif.`
|
|
327
|
+
`Tidak ada Skill atau MCP baru yang terdeteksi.`,
|
|
328
|
+
`Ketik ${C.yellow}/skill${C.reset} atau ${C.yellow}/mcp${C.reset} untuk melihat command instan yang aktif.`
|
|
293
329
|
], C.gray);
|
|
294
330
|
}
|
|
295
331
|
console.log('');
|
|
@@ -448,12 +484,16 @@ async function fetchAvailableModels() {
|
|
|
448
484
|
}
|
|
449
485
|
}
|
|
450
486
|
|
|
451
|
-
async function handleChat(prompt) {
|
|
487
|
+
async function handleChat(prompt, forcedTool = null) {
|
|
452
488
|
if (!config.apiKey) {
|
|
453
489
|
console.log(`${C.yellow}[!] API Key is not set.${C.reset}`);
|
|
454
490
|
return askConfig(() => startPrompt());
|
|
455
491
|
}
|
|
456
492
|
|
|
493
|
+
if (forcedTool) {
|
|
494
|
+
console.log(`${C.cyan}[🚀 Executing via ${forcedTool.type.toUpperCase()}: ${forcedTool.name}]${C.reset}`);
|
|
495
|
+
}
|
|
496
|
+
|
|
457
497
|
console.log(`${C.yellow}TMPA CLI processing...${C.reset}`);
|
|
458
498
|
|
|
459
499
|
try {
|
|
@@ -461,8 +501,8 @@ async function handleChat(prompt) {
|
|
|
461
501
|
let headers = { 'Content-Type': 'application/json' };
|
|
462
502
|
let bodyData = {};
|
|
463
503
|
|
|
464
|
-
const toolsContext = getActiveToolsContext();
|
|
465
|
-
const fullPrompt = prompt + toolsContext;
|
|
504
|
+
const toolsContext = getActiveToolsContext(forcedTool);
|
|
505
|
+
const fullPrompt = (prompt || "Jalankan instruksi tool ini.") + toolsContext;
|
|
466
506
|
|
|
467
507
|
if (url.includes('googleapis.com')) {
|
|
468
508
|
url = `${url}?key=${config.apiKey}`;
|
|
@@ -499,7 +539,7 @@ async function handleChat(prompt) {
|
|
|
499
539
|
return startPrompt();
|
|
500
540
|
}
|
|
501
541
|
|
|
502
|
-
console.log(`\n${C.c1}TMPA CLI (${config.model || 'AI'}) :${C.reset}
|
|
542
|
+
console.log(`\n${C.c1}TMPA CLI (${config.model || 'AI'}) :${C.reset}\n${aiResponse}\n`);
|
|
503
543
|
|
|
504
544
|
} catch (error) {
|
|
505
545
|
console.log(`\n${C.red}[x] Fetch Error: ${error.message}${C.reset}\n`);
|
|
@@ -559,6 +599,40 @@ function startPrompt() {
|
|
|
559
599
|
startPrompt();
|
|
560
600
|
} else if (cmd === '/uninstall') {
|
|
561
601
|
handleUninstall();
|
|
602
|
+
} else if (cmd.startsWith('/skill-')) {
|
|
603
|
+
const fullCmd = cmd.slice(7).trim();
|
|
604
|
+
const spaceIdx = fullCmd.indexOf(' ');
|
|
605
|
+
let skillName = fullCmd;
|
|
606
|
+
let userPrompt = '';
|
|
607
|
+
|
|
608
|
+
if (spaceIdx !== -1) {
|
|
609
|
+
skillName = fullCmd.slice(0, spaceIdx);
|
|
610
|
+
userPrompt = fullCmd.slice(spaceIdx + 1).trim();
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (registry.skills && registry.skills[skillName]) {
|
|
614
|
+
handleChat(userPrompt, { type: 'skill', name: skillName, target: registry.skills[skillName].path });
|
|
615
|
+
} else {
|
|
616
|
+
console.log(`${C.red}[x] Skill "${skillName}" tidak ditemukan di registry. Ketik /skill untuk melihat daftar.${C.reset}\n`);
|
|
617
|
+
startPrompt();
|
|
618
|
+
}
|
|
619
|
+
} else if (cmd.startsWith('/mcp-')) {
|
|
620
|
+
const fullCmd = cmd.slice(5).trim();
|
|
621
|
+
const spaceIdx = fullCmd.indexOf(' ');
|
|
622
|
+
let mcpName = fullCmd;
|
|
623
|
+
let userPrompt = '';
|
|
624
|
+
|
|
625
|
+
if (spaceIdx !== -1) {
|
|
626
|
+
mcpName = fullCmd.slice(0, spaceIdx);
|
|
627
|
+
userPrompt = fullCmd.slice(spaceIdx + 1).trim();
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (registry.mcp && registry.mcp[mcpName]) {
|
|
631
|
+
handleChat(userPrompt, { type: 'mcp', name: mcpName, target: registry.mcp[mcpName].target });
|
|
632
|
+
} else {
|
|
633
|
+
console.log(`${C.red}[x] MCP "${mcpName}" tidak ditemukan di registry. Ketik /mcp untuk melihat daftar.${C.reset}\n`);
|
|
634
|
+
startPrompt();
|
|
635
|
+
}
|
|
562
636
|
} else if (cmd === '') {
|
|
563
637
|
startPrompt();
|
|
564
638
|
} else {
|