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.
Files changed (2) hide show
  1. package/bin/index.js +140 -42
  2. 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}[Interactive Mode + Multi-Location Scanner]${C.reset}
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 Prompt Builder for Skills and MCPs
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(`- Skill: ${name} (Path: ${s.path})`);
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(`- MCP Server: ${name} (Target: ${m.target}, Type: ${m.type})`);
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: ACTIVE TOOLS & PROTOCOLS]\n" + contextParts.join("\n") +
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 and MCP
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 yang terhubung. Gunakan ${C.yellow}/connect skill <path>${C.gray} atau ${C.yellow}/scan${C.gray} untuk memindai.${C.reset}\n`);
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} [${item.status || 'Active'}]`);
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. Gunakan ${C.yellow}/connect mcp <target>${C.gray} atau ${C.yellow}/scan${C.gray} untuk memindai.${C.reset}\n`);
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 || 'local'}]`);
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}" berhasil dihubungkan dari: ${resolvedPath}${C.reset}\n`);
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 Server "${mcpName}" berhasil dihubungkan!${C.reset}\n`);
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(`${C.yellow}[...] Memindai folder internal & lokasi umum pengguna (Gemini/Claude/System)...${C.reset}`);
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 addedCount = 0;
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
- if (!registry.skills[name]) {
224
- registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
225
- console.log(`${C.green}[+] Terdeteksi Skill: ${name}${C.reset} ${C.gray}(${fullPath})${C.reset}`);
226
- addedCount++;
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. Total item baru terdeteksi: ${addedCount}${C.reset}\n`);
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {