tmpa-cli 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +140 -42
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -29,7 +29,8 @@ const C = {
|
|
|
29
29
|
yellow: '\x1b[38;2;234;179;8m',
|
|
30
30
|
red: '\x1b[38;2;239;68;68m',
|
|
31
31
|
gray: '\x1b[38;2;148;163;184m',
|
|
32
|
-
darkGray: '\x1b[38;2;71;85;105m'
|
|
32
|
+
darkGray: '\x1b[38;2;71;85;105m',
|
|
33
|
+
bold: '\x1b[1m'
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
const PROVIDERS = {
|
|
@@ -65,6 +66,18 @@ function saveRegistry(registry) {
|
|
|
65
66
|
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(registry, null, 2));
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
// UI Popup Card Helper
|
|
70
|
+
function drawBox(title, contentLines, borderColor = C.cyan) {
|
|
71
|
+
const width = 64;
|
|
72
|
+
console.log(`${borderColor}┌${'─'.repeat(width - 2)}┐${C.reset}`);
|
|
73
|
+
console.log(`${borderColor}│ ${C.bold}${title.padEnd(width - 4)}${C.reset}${borderColor} │${C.reset}`);
|
|
74
|
+
console.log(`${borderColor}├${'─'.repeat(width - 2)}┤${C.reset}`);
|
|
75
|
+
contentLines.forEach(line => {
|
|
76
|
+
console.log(`${borderColor}│${C.reset} ${line.padEnd(width - 4)} ${borderColor}│${C.reset}`);
|
|
77
|
+
});
|
|
78
|
+
console.log(`${borderColor}└${'─'.repeat(width - 2)}┘${C.reset}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
let rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
69
82
|
let config = loadConfig();
|
|
70
83
|
let registry = loadRegistry();
|
|
@@ -79,22 +92,29 @@ ${C.c3} ██║ ██║╚██╔╝██║██╔═══╝ █
|
|
|
79
92
|
${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ ██║${C.reset}
|
|
80
93
|
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
81
94
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
82
|
-
${C.reset}The Multi Platform AI ${C.green}[
|
|
95
|
+
${C.reset}The Multi Platform AI ${C.green}[Dynamic Tool Slash Engine]${C.reset}
|
|
83
96
|
${C.gray}/config | /models | /skill | /mcp | /connect | /scan | /exit${C.reset}
|
|
84
97
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
85
98
|
`);
|
|
86
99
|
}
|
|
87
100
|
|
|
88
|
-
// System
|
|
89
|
-
function getActiveToolsContext() {
|
|
101
|
+
// Dynamic System Context Builder
|
|
102
|
+
function getActiveToolsContext(forcedTool = null) {
|
|
90
103
|
let contextParts = [];
|
|
104
|
+
|
|
105
|
+
if (forcedTool) {
|
|
106
|
+
contextParts.push(`[CRITICAL INSTRUCTION: FORCED TOOL EXECUTION]`);
|
|
107
|
+
contextParts.push(`User explicitly called tool command: /${forcedTool.type}-${forcedTool.name}`);
|
|
108
|
+
contextParts.push(`Target Path/URL: ${forcedTool.target}`);
|
|
109
|
+
contextParts.push(`You MUST use and invoke this ${forcedTool.type.toUpperCase()} tool to handle user prompt below.\n`);
|
|
110
|
+
}
|
|
91
111
|
|
|
92
112
|
const activeSkills = Object.keys(registry.skills || {});
|
|
93
113
|
if (activeSkills.length > 0) {
|
|
94
114
|
contextParts.push("AVAILABLE SKILLS:");
|
|
95
115
|
activeSkills.forEach(name => {
|
|
96
116
|
const s = registry.skills[name];
|
|
97
|
-
contextParts.push(`-
|
|
117
|
+
contextParts.push(`- /skill-${name} -> Path: ${s.path}`);
|
|
98
118
|
});
|
|
99
119
|
}
|
|
100
120
|
|
|
@@ -103,43 +123,79 @@ function getActiveToolsContext() {
|
|
|
103
123
|
contextParts.push("AVAILABLE MCP SERVERS:");
|
|
104
124
|
activeMCP.forEach(name => {
|
|
105
125
|
const m = registry.mcp[name];
|
|
106
|
-
contextParts.push(`-
|
|
126
|
+
contextParts.push(`- /mcp-${name} -> Target: ${m.target} (${m.type})`);
|
|
107
127
|
});
|
|
108
128
|
}
|
|
109
129
|
|
|
110
130
|
if (contextParts.length === 0) return "";
|
|
111
131
|
|
|
112
|
-
return "\n\n[SYSTEM CONTEXT:
|
|
113
|
-
"\nIf you need to execute a connected JS skill or run local tools, specify standard instructions or output [EXEC_TOOL: tool_name(params)].\n";
|
|
132
|
+
return "\n\n[SYSTEM CONTEXT: REGISTERED TOOLS & PROTOCOLS]\n" + contextParts.join("\n") + "\n";
|
|
114
133
|
}
|
|
115
134
|
|
|
116
|
-
// Handlers for Skills
|
|
135
|
+
// Handlers for Skills with Dynamic Command Helper
|
|
117
136
|
function listSkills() {
|
|
118
137
|
console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
|
|
119
138
|
const keys = Object.keys(registry.skills || {});
|
|
139
|
+
|
|
120
140
|
if (keys.length === 0) {
|
|
121
|
-
console.log(`${C.gray}Belum ada skill
|
|
141
|
+
console.log(`${C.gray}Belum ada skill terhubung. Ketik ${C.yellow}/scan${C.gray} atau ${C.yellow}/connect skill <path>${C.reset}\n`);
|
|
122
142
|
return;
|
|
123
143
|
}
|
|
144
|
+
|
|
124
145
|
keys.forEach((name, i) => {
|
|
125
146
|
const item = registry.skills[name];
|
|
126
|
-
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset}
|
|
147
|
+
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset}`);
|
|
127
148
|
});
|
|
128
149
|
console.log('');
|
|
150
|
+
|
|
151
|
+
// Interactive Action Helper Popup with dynamic slash commands
|
|
152
|
+
const lines = [
|
|
153
|
+
`${C.bold}Perintah Instan yang Bisa Kamu Ketik Langsung:${C.reset}`,
|
|
154
|
+
``
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
keys.forEach(name => {
|
|
158
|
+
lines.push(` ${C.yellow}/skill-${name}${C.reset} <prompt kamu>`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
lines.push(``);
|
|
162
|
+
lines.push(`${C.gray}Contoh: ${C.yellow}/skill-${keys[0]} buatkan animasi intro${C.reset}`);
|
|
163
|
+
|
|
164
|
+
drawBox("⚡ COMMAND INSTAN SKILL TERSEDIA", lines, C.c1);
|
|
165
|
+
console.log('');
|
|
129
166
|
}
|
|
130
167
|
|
|
168
|
+
// Handlers for MCP with Dynamic Command Helper
|
|
131
169
|
function listMCP() {
|
|
132
170
|
console.log(`\n${C.cyan}=== Registered MCP (Model Context Protocol) Servers ===${C.reset}`);
|
|
133
171
|
const keys = Object.keys(registry.mcp || {});
|
|
172
|
+
|
|
134
173
|
if (keys.length === 0) {
|
|
135
|
-
console.log(`${C.gray}Belum ada MCP server terhubung.
|
|
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`);
|
|
136
175
|
return;
|
|
137
176
|
}
|
|
177
|
+
|
|
138
178
|
keys.forEach((name, i) => {
|
|
139
179
|
const item = registry.mcp[name];
|
|
140
|
-
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type
|
|
180
|
+
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type}]`);
|
|
141
181
|
});
|
|
142
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('');
|
|
143
199
|
}
|
|
144
200
|
|
|
145
201
|
function connectResource(inputArgs) {
|
|
@@ -165,7 +221,7 @@ function connectResource(inputArgs) {
|
|
|
165
221
|
status: 'Active'
|
|
166
222
|
};
|
|
167
223
|
saveRegistry(registry);
|
|
168
|
-
console.log(`${C.green}[+] Skill "${skillName}"
|
|
224
|
+
console.log(`${C.green}[+] Skill "${skillName}" terhubung! Akses instan: /skill-${skillName}${C.reset}\n`);
|
|
169
225
|
} else if (type === 'mcp') {
|
|
170
226
|
const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
|
|
171
227
|
registry.mcp = registry.mcp || {};
|
|
@@ -176,31 +232,25 @@ function connectResource(inputArgs) {
|
|
|
176
232
|
status: 'Connected'
|
|
177
233
|
};
|
|
178
234
|
saveRegistry(registry);
|
|
179
|
-
console.log(`${C.green}[+] MCP
|
|
235
|
+
console.log(`${C.green}[+] MCP "${mcpName}" terhubung! Akses instan: /mcp-${mcpName}${C.reset}\n`);
|
|
180
236
|
} else {
|
|
181
237
|
console.log(`${C.red}[x] Tipe tidak dikenal. Gunakan "skill" atau "mcp".${C.reset}\n`);
|
|
182
238
|
}
|
|
183
239
|
}
|
|
184
240
|
|
|
185
|
-
// Deep Multi-Location Scanner Function
|
|
186
241
|
function scanResources() {
|
|
187
|
-
console.log(
|
|
242
|
+
console.log(`\n${C.yellow}[...] Memindai lokasi internal, home user, Gemini CLI & Claude Desktop...${C.reset}`);
|
|
188
243
|
registry = loadRegistry();
|
|
189
244
|
registry.skills = registry.skills || {};
|
|
190
245
|
registry.mcp = registry.mcp || {};
|
|
191
246
|
|
|
192
247
|
const home = os.homedir();
|
|
193
248
|
const searchTargets = [
|
|
194
|
-
// Folder Internal TMPA
|
|
195
249
|
{ type: 'skill', dir: SKILLS_DIR },
|
|
196
250
|
{ type: 'mcp', dir: MCP_DIR },
|
|
197
|
-
|
|
198
|
-
// Folder Umum Pengguna (Direct Home Directory)
|
|
199
251
|
{ type: 'skill', dir: path.join(home, 'skills') },
|
|
200
252
|
{ type: 'mcp', dir: path.join(home, 'mcp') },
|
|
201
253
|
{ type: 'mcp', dir: path.join(home, '.mcp') },
|
|
202
|
-
|
|
203
|
-
// Folder Konfigurasi Gemini CLI & Claude Desktop
|
|
204
254
|
{ type: 'skill', dir: path.join(home, '.gemini', 'skills') },
|
|
205
255
|
{ type: 'mcp', dir: path.join(home, '.gemini', 'mcp') },
|
|
206
256
|
{ type: 'skill', dir: path.join(home, '.config', 'gemini', 'skills') },
|
|
@@ -209,7 +259,7 @@ function scanResources() {
|
|
|
209
259
|
{ type: 'mcp', dir: path.join(home, '.config', 'claude', 'mcp') }
|
|
210
260
|
];
|
|
211
261
|
|
|
212
|
-
let
|
|
262
|
+
let newDetected = [];
|
|
213
263
|
|
|
214
264
|
searchTargets.forEach(({ type, dir }) => {
|
|
215
265
|
if (fs.existsSync(dir)) {
|
|
@@ -219,28 +269,36 @@ function scanResources() {
|
|
|
219
269
|
const fullPath = path.join(dir, item);
|
|
220
270
|
const name = path.basename(item, path.extname(item));
|
|
221
271
|
|
|
222
|
-
if (type === 'skill') {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
} else if (type === 'mcp') {
|
|
229
|
-
if (!registry.mcp[name]) {
|
|
230
|
-
registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
|
|
231
|
-
console.log(`${C.green}[+] Terdeteksi MCP: ${name}${C.reset} ${C.gray}(${fullPath})${C.reset}`);
|
|
232
|
-
addedCount++;
|
|
233
|
-
}
|
|
272
|
+
if (type === 'skill' && !registry.skills[name]) {
|
|
273
|
+
registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
|
|
274
|
+
newDetected.push(`/skill-${name}`);
|
|
275
|
+
} else if (type === 'mcp' && !registry.mcp[name]) {
|
|
276
|
+
registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
|
|
277
|
+
newDetected.push(`/mcp-${name}`);
|
|
234
278
|
}
|
|
235
279
|
});
|
|
236
|
-
} catch (e) {
|
|
237
|
-
// Skip folder jika permission terikat
|
|
238
|
-
}
|
|
280
|
+
} catch (e) {}
|
|
239
281
|
}
|
|
240
282
|
});
|
|
241
283
|
|
|
242
284
|
saveRegistry(registry);
|
|
243
|
-
console.log(`${C.green}[+] Pemindaian selesai
|
|
285
|
+
console.log(`${C.green}[+] Pemindaian selesai.${C.reset}\n`);
|
|
286
|
+
|
|
287
|
+
if (newDetected.length > 0) {
|
|
288
|
+
drawBox("🎉 COMMAND INSTAN BARU TERDETEKSI!", [
|
|
289
|
+
`${C.green}Kamu sekarang bisa langsung menggunakan perintah ini:${C.reset}`,
|
|
290
|
+
...newDetected.slice(0, 5).map(cmd => ` • ${C.yellow}${cmd}${C.reset} <prompt>`),
|
|
291
|
+
newDetected.length > 5 ? ` ...dan ${newDetected.length - 5} command lainnya.` : '',
|
|
292
|
+
``,
|
|
293
|
+
`${C.cyan}Ketik /skill atau /mcp untuk melihat semua command.${C.reset}`
|
|
294
|
+
], C.green);
|
|
295
|
+
} else {
|
|
296
|
+
drawBox("ℹ️ HASIL PEMINDAIAN", [
|
|
297
|
+
`Tidak ada Skill atau MCP baru yang terdeteksi.`,
|
|
298
|
+
`Ketik ${C.yellow}/skill${C.reset} atau ${C.yellow}/mcp${C.reset} untuk melihat command instan yang aktif.`
|
|
299
|
+
], C.gray);
|
|
300
|
+
}
|
|
301
|
+
console.log('');
|
|
244
302
|
}
|
|
245
303
|
|
|
246
304
|
function askConfig(callback) {
|
|
@@ -396,12 +454,16 @@ async function fetchAvailableModels() {
|
|
|
396
454
|
}
|
|
397
455
|
}
|
|
398
456
|
|
|
399
|
-
async function handleChat(prompt) {
|
|
457
|
+
async function handleChat(prompt, forcedTool = null) {
|
|
400
458
|
if (!config.apiKey) {
|
|
401
459
|
console.log(`${C.yellow}[!] API Key is not set.${C.reset}`);
|
|
402
460
|
return askConfig(() => startPrompt());
|
|
403
461
|
}
|
|
404
462
|
|
|
463
|
+
if (forcedTool) {
|
|
464
|
+
console.log(`${C.cyan}[🚀 Executing via ${forcedTool.type.toUpperCase()}: ${forcedTool.name}]${C.reset}`);
|
|
465
|
+
}
|
|
466
|
+
|
|
405
467
|
console.log(`${C.yellow}TMPA CLI processing...${C.reset}`);
|
|
406
468
|
|
|
407
469
|
try {
|
|
@@ -409,8 +471,8 @@ async function handleChat(prompt) {
|
|
|
409
471
|
let headers = { 'Content-Type': 'application/json' };
|
|
410
472
|
let bodyData = {};
|
|
411
473
|
|
|
412
|
-
const toolsContext = getActiveToolsContext();
|
|
413
|
-
const fullPrompt = prompt + toolsContext;
|
|
474
|
+
const toolsContext = getActiveToolsContext(forcedTool);
|
|
475
|
+
const fullPrompt = (prompt || "Jalankan instruksi tool ini.") + toolsContext;
|
|
414
476
|
|
|
415
477
|
if (url.includes('googleapis.com')) {
|
|
416
478
|
url = `${url}?key=${config.apiKey}`;
|
|
@@ -507,6 +569,42 @@ function startPrompt() {
|
|
|
507
569
|
startPrompt();
|
|
508
570
|
} else if (cmd === '/uninstall') {
|
|
509
571
|
handleUninstall();
|
|
572
|
+
} else if (cmd.startsWith('/skill-')) {
|
|
573
|
+
// Dynamic Slash Command for Skill: /skill-remotion-vidio <prompt>
|
|
574
|
+
const fullCmd = cmd.slice(7).trim(); // remove '/skill-'
|
|
575
|
+
const spaceIdx = fullCmd.indexOf(' ');
|
|
576
|
+
let skillName = fullCmd;
|
|
577
|
+
let userPrompt = '';
|
|
578
|
+
|
|
579
|
+
if (spaceIdx !== -1) {
|
|
580
|
+
skillName = fullCmd.slice(0, spaceIdx);
|
|
581
|
+
userPrompt = fullCmd.slice(spaceIdx + 1).trim();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (registry.skills && registry.skills[skillName]) {
|
|
585
|
+
handleChat(userPrompt, { type: 'skill', name: skillName, target: registry.skills[skillName].path });
|
|
586
|
+
} else {
|
|
587
|
+
console.log(`${C.red}[x] Skill "${skillName}" tidak ditemukan di registry. Ketik /skill untuk melihat daftar.${C.reset}\n`);
|
|
588
|
+
startPrompt();
|
|
589
|
+
}
|
|
590
|
+
} else if (cmd.startsWith('/mcp-')) {
|
|
591
|
+
// Dynamic Slash Command for MCP: /mcp-stitch <prompt>
|
|
592
|
+
const fullCmd = cmd.slice(5).trim(); // remove '/mcp-'
|
|
593
|
+
const spaceIdx = fullCmd.indexOf(' ');
|
|
594
|
+
let mcpName = fullCmd;
|
|
595
|
+
let userPrompt = '';
|
|
596
|
+
|
|
597
|
+
if (spaceIdx !== -1) {
|
|
598
|
+
mcpName = fullCmd.slice(0, spaceIdx);
|
|
599
|
+
userPrompt = fullCmd.slice(spaceIdx + 1).trim();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (registry.mcp && registry.mcp[mcpName]) {
|
|
603
|
+
handleChat(userPrompt, { type: 'mcp', name: mcpName, target: registry.mcp[mcpName].target });
|
|
604
|
+
} else {
|
|
605
|
+
console.log(`${C.red}[x] MCP "${mcpName}" tidak ditemukan di registry. Ketik /mcp untuk melihat daftar.${C.reset}\n`);
|
|
606
|
+
startPrompt();
|
|
607
|
+
}
|
|
510
608
|
} else if (cmd === '') {
|
|
511
609
|
startPrompt();
|
|
512
610
|
} else {
|