tmpa-cli 1.0.13 → 1.0.15

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 +117 -44
  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,19 @@ 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 = 60;
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
+ // Basic text padding layout
77
+ console.log(`${borderColor}│${C.reset} ${line.padEnd(width - 4)} ${borderColor}│${C.reset}`);
78
+ });
79
+ console.log(`${borderColor}└${'─'.repeat(width - 2)}┘${C.reset}`);
80
+ }
81
+
68
82
  let rl = readline.createInterface({ input: process.stdin, output: process.stdout });
69
83
  let config = loadConfig();
70
84
  let registry = loadRegistry();
@@ -79,17 +93,16 @@ ${C.c3} ██║ ██║╚██╔╝██║██╔═══╝ █
79
93
  ${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ ██║${C.reset}
80
94
  ${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
81
95
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
82
- ${C.reset}The Multi Platform AI ${C.green}[Interactive Mode + Tool Runner]${C.reset}
96
+ ${C.reset}The Multi Platform AI ${C.green}[Interactive Popup UI Engine]${C.reset}
83
97
  ${C.gray}/config | /models | /skill | /mcp | /connect | /scan | /exit${C.reset}
84
98
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
85
99
  `);
86
100
  }
87
101
 
88
- // System Prompt Builder for Skills and MCPs
102
+ // System Context Builder
89
103
  function getActiveToolsContext() {
90
104
  let contextParts = [];
91
105
 
92
- // Skills Context
93
106
  const activeSkills = Object.keys(registry.skills || {});
94
107
  if (activeSkills.length > 0) {
95
108
  contextParts.push("AVAILABLE SKILLS:");
@@ -99,7 +112,6 @@ function getActiveToolsContext() {
99
112
  });
100
113
  }
101
114
 
102
- // MCP Context
103
115
  const activeMCP = Object.keys(registry.mcp || {});
104
116
  if (activeMCP.length > 0) {
105
117
  contextParts.push("AVAILABLE MCP SERVERS:");
@@ -112,36 +124,67 @@ function getActiveToolsContext() {
112
124
  if (contextParts.length === 0) return "";
113
125
 
114
126
  return "\n\n[SYSTEM CONTEXT: ACTIVE TOOLS & PROTOCOLS]\n" + contextParts.join("\n") +
115
- "\nIf you need to execute a connected JS skill or run local tools, specify standard instructions or output [EXEC_TOOL: tool_name(params)].\n";
127
+ "\nIf user wants to execute a skill/MCP, assist them using available tools.\n";
116
128
  }
117
129
 
118
- // Handlers for Skills and MCP
130
+ // Handlers for Skills with Popup Helper
119
131
  function listSkills() {
120
132
  console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
121
133
  const keys = Object.keys(registry.skills || {});
134
+
122
135
  if (keys.length === 0) {
123
- console.log(`${C.gray}Belum ada skill yang terhubung. Gunakan ${C.yellow}/connect skill <path>${C.gray} untuk menghubungkan.${C.reset}\n`);
136
+ console.log(`${C.gray}Belum ada skill terhubung. Ketik ${C.yellow}/scan${C.gray} atau ${C.yellow}/connect skill <path>${C.reset}\n`);
124
137
  return;
125
138
  }
139
+
126
140
  keys.forEach((name, i) => {
127
141
  const item = registry.skills[name];
128
- console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset} [${item.status || 'Active'}]`);
142
+ console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset}`);
129
143
  });
130
144
  console.log('');
145
+
146
+ // Interactive Action Helper Popup
147
+ const sampleSkill = keys[0] || 'nama_skill';
148
+ drawBox("💡 CARA MENGGUNAKAN SKILL TERDAFTAR", [
149
+ `${C.bold}Perintah yang Bisa Kamu Ketik di Chat:${C.reset}`,
150
+ ` 1. Langsung minta AI panggil skill:`,
151
+ ` ${C.yellow}TMPA > "Jalankan skill ${sampleSkill}"${C.reset}`,
152
+ ` 2. Tanya fungsi skill:`,
153
+ ` ${C.yellow}TMPA > "Apa kegunaan dari skill ${sampleSkill}?"${C.reset}`,
154
+ ` 3. Hubungkan file JS baru:`,
155
+ ` ${C.yellow}TMPA > /connect skill ./path/ke/file.js${C.reset}`
156
+ ], C.c1);
157
+ console.log('');
131
158
  }
132
159
 
160
+ // Handlers for MCP with Popup Helper
133
161
  function listMCP() {
134
162
  console.log(`\n${C.cyan}=== Registered MCP (Model Context Protocol) Servers ===${C.reset}`);
135
163
  const keys = Object.keys(registry.mcp || {});
164
+
136
165
  if (keys.length === 0) {
137
- console.log(`${C.gray}Belum ada MCP server terhubung. Gunakan ${C.yellow}/connect mcp <target>${C.gray} untuk menghubungkan.${C.reset}\n`);
166
+ console.log(`${C.gray}Belum ada MCP server terhubung. Ketik ${C.yellow}/scan${C.gray} atau ${C.yellow}/connect mcp <target>${C.reset}\n`);
138
167
  return;
139
168
  }
169
+
140
170
  keys.forEach((name, i) => {
141
171
  const item = registry.mcp[name];
142
- console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type || 'local'}]`);
172
+ console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type}]`);
143
173
  });
144
174
  console.log('');
175
+
176
+ // Interactive Action Helper Popup
177
+ const sampleMcp = keys[0] || 'filesystem';
178
+ drawBox("⚡ CARA MENGGUNAKAN MCP SERVER", [
179
+ `${C.bold}Perintah & Integrasi yang Bisa Kamu Gunakan:${C.reset}`,
180
+ ` 1. Minta AI akses fitur MCP:`,
181
+ ` ${C.yellow}TMPA > "Gunakan MCP ${sampleMcp} untuk analisis data"${C.reset}`,
182
+ ` 2. Hubungkan MCP via URL / HTTP SSE:`,
183
+ ` ${C.yellow}TMPA > /connect mcp http://localhost:3000/sse${C.reset}`,
184
+ ` 3. Pindai server MCP otomatis:`,
185
+ ` ${C.yellow}TMPA > /scan${C.reset}`
186
+ ], C.c4);
187
+ console.log('');
145
188
  }
146
189
 
147
190
  function connectResource(inputArgs) {
@@ -167,7 +210,7 @@ function connectResource(inputArgs) {
167
210
  status: 'Active'
168
211
  };
169
212
  saveRegistry(registry);
170
- console.log(`${C.green}[+] Skill "${skillName}" berhasil dihubungkan dari: ${resolvedPath}${C.reset}\n`);
213
+ console.log(`${C.green}[+] Skill "${skillName}" berhasil terhubung!${C.reset}\n`);
171
214
  } else if (type === 'mcp') {
172
215
  const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
173
216
  registry.mcp = registry.mcp || {};
@@ -178,42 +221,78 @@ function connectResource(inputArgs) {
178
221
  status: 'Connected'
179
222
  };
180
223
  saveRegistry(registry);
181
- console.log(`${C.green}[+] MCP Server "${mcpName}" berhasil dihubungkan!${C.reset}\n`);
224
+ console.log(`${C.green}[+] MCP Server "${mcpName}" berhasil terhubung!${C.reset}\n`);
182
225
  } else {
183
226
  console.log(`${C.red}[x] Tipe tidak dikenal. Gunakan "skill" atau "mcp".${C.reset}\n`);
184
227
  }
185
228
  }
186
229
 
230
+ // Deep Multi-Location Scanner Function with Result Popup Card
187
231
  function scanResources() {
188
- console.log(`${C.yellow}[...] Memindai folder ~/.tmpa/skills dan ~/.tmpa/mcp...${C.reset}`);
232
+ console.log(`\n${C.yellow}[...] Memindai lokasi internal, home user, Gemini CLI & Claude Desktop...${C.reset}`);
189
233
  registry = loadRegistry();
190
-
191
- if (fs.existsSync(SKILLS_DIR)) {
192
- const files = fs.readdirSync(SKILLS_DIR);
193
- files.forEach(file => {
194
- const fullPath = path.join(SKILLS_DIR, file);
195
- const name = path.basename(file, path.extname(file));
196
- if (!registry.skills[name]) {
197
- registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
198
- console.log(`${C.green}[+] Auto-detected skill: ${name}${C.reset}`);
199
- }
200
- });
201
- }
202
-
203
- if (fs.existsSync(MCP_DIR)) {
204
- const files = fs.readdirSync(MCP_DIR);
205
- files.forEach(file => {
206
- const fullPath = path.join(MCP_DIR, file);
207
- const name = path.basename(file, path.extname(file));
208
- if (!registry.mcp[name]) {
209
- registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
210
- console.log(`${C.green}[+] Auto-detected MCP: ${name}${C.reset}`);
211
- }
212
- });
213
- }
234
+ registry.skills = registry.skills || {};
235
+ registry.mcp = registry.mcp || {};
236
+
237
+ const home = os.homedir();
238
+ const searchTargets = [
239
+ { type: 'skill', dir: SKILLS_DIR },
240
+ { type: 'mcp', dir: MCP_DIR },
241
+ { type: 'skill', dir: path.join(home, 'skills') },
242
+ { type: 'mcp', dir: path.join(home, 'mcp') },
243
+ { type: 'mcp', dir: path.join(home, '.mcp') },
244
+ { type: 'skill', dir: path.join(home, '.gemini', 'skills') },
245
+ { type: 'mcp', dir: path.join(home, '.gemini', 'mcp') },
246
+ { type: 'skill', dir: path.join(home, '.config', 'gemini', 'skills') },
247
+ { type: 'mcp', dir: path.join(home, '.config', 'gemini', 'mcp') },
248
+ { type: 'mcp', dir: path.join(home, '.claude', 'mcp') },
249
+ { type: 'mcp', dir: path.join(home, '.config', 'claude', 'mcp') }
250
+ ];
251
+
252
+ let newDetected = [];
253
+
254
+ searchTargets.forEach(({ type, dir }) => {
255
+ if (fs.existsSync(dir)) {
256
+ try {
257
+ const items = fs.readdirSync(dir);
258
+ items.forEach(item => {
259
+ const fullPath = path.join(dir, item);
260
+ const name = path.basename(item, path.extname(item));
261
+
262
+ if (type === 'skill' && !registry.skills[name]) {
263
+ registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
264
+ newDetected.push(`[Skill] ${name}`);
265
+ } else if (type === 'mcp' && !registry.mcp[name]) {
266
+ registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
267
+ newDetected.push(`[MCP] ${name}`);
268
+ }
269
+ });
270
+ } catch (e) {}
271
+ }
272
+ });
214
273
 
215
274
  saveRegistry(registry);
275
+
216
276
  console.log(`${C.green}[+] Pemindaian selesai.${C.reset}\n`);
277
+
278
+ // POPUP CARD SUMMARY FOR SCAN RESULTS
279
+ if (newDetected.length > 0) {
280
+ drawBox("🎉 ITEM BARU BERHASIL DITEMUKAN!", [
281
+ `${C.green}Berhasil menambahkan ${newDetected.length} resource baru:${C.reset}`,
282
+ ...newDetected.slice(0, 4).map(item => ` • ${item}`),
283
+ newDetected.length > 4 ? ` ...dan ${newDetected.length - 4} item lainnya.` : '',
284
+ ``,
285
+ `${C.yellow}Ketik /skill atau /mcp untuk melihat daftar lengkap!${C.reset}`
286
+ ], C.green);
287
+ } else {
288
+ drawBox("ℹ️ HASIL PEMINDAIAN", [
289
+ `Tidak ada file Skill atau MCP baru yang terdeteksi.`,
290
+ `Semua resource sudah terhubung di registry.`,
291
+ ``,
292
+ `Gunakan ${C.yellow}/skill${C.reset} atau ${C.yellow}/mcp${C.reset} untuk melihat item aktif.`
293
+ ], C.gray);
294
+ }
295
+ console.log('');
217
296
  }
218
297
 
219
298
  function askConfig(callback) {
@@ -382,7 +461,6 @@ async function handleChat(prompt) {
382
461
  let headers = { 'Content-Type': 'application/json' };
383
462
  let bodyData = {};
384
463
 
385
- // Combine User Prompt + Active Tools Context
386
464
  const toolsContext = getActiveToolsContext();
387
465
  const fullPrompt = prompt + toolsContext;
388
466
 
@@ -423,11 +501,6 @@ async function handleChat(prompt) {
423
501
 
424
502
  console.log(`\n${C.c1}TMPA CLI (${config.model || 'AI'}) :${C.reset} ${aiResponse}\n`);
425
503
 
426
- // Check if AI requested execution of a local tool/skill
427
- if (aiResponse.includes('[EXEC_TOOL:')) {
428
- console.log(`${C.cyan}[+] Detected Tool Execution Request from AI...${C.reset}`);
429
- }
430
-
431
504
  } catch (error) {
432
505
  console.log(`\n${C.red}[x] Fetch Error: ${error.message}${C.reset}\n`);
433
506
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {