tmpa-cli 1.0.14 → 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 +87 -35
  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,13 +93,13 @@ ${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 + Multi-Location Scanner]${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
 
@@ -110,36 +124,67 @@ function getActiveToolsContext() {
110
124
  if (contextParts.length === 0) return "";
111
125
 
112
126
  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";
127
+ "\nIf user wants to execute a skill/MCP, assist them using available tools.\n";
114
128
  }
115
129
 
116
- // Handlers for Skills and MCP
130
+ // Handlers for Skills with Popup Helper
117
131
  function listSkills() {
118
132
  console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
119
133
  const keys = Object.keys(registry.skills || {});
134
+
120
135
  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`);
136
+ console.log(`${C.gray}Belum ada skill terhubung. Ketik ${C.yellow}/scan${C.gray} atau ${C.yellow}/connect skill <path>${C.reset}\n`);
122
137
  return;
123
138
  }
139
+
124
140
  keys.forEach((name, i) => {
125
141
  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'}]`);
142
+ console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset}`);
127
143
  });
128
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('');
129
158
  }
130
159
 
160
+ // Handlers for MCP with Popup Helper
131
161
  function listMCP() {
132
162
  console.log(`\n${C.cyan}=== Registered MCP (Model Context Protocol) Servers ===${C.reset}`);
133
163
  const keys = Object.keys(registry.mcp || {});
164
+
134
165
  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`);
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`);
136
167
  return;
137
168
  }
169
+
138
170
  keys.forEach((name, i) => {
139
171
  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'}]`);
172
+ console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type}]`);
141
173
  });
142
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('');
143
188
  }
144
189
 
145
190
  function connectResource(inputArgs) {
@@ -165,7 +210,7 @@ function connectResource(inputArgs) {
165
210
  status: 'Active'
166
211
  };
167
212
  saveRegistry(registry);
168
- 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`);
169
214
  } else if (type === 'mcp') {
170
215
  const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
171
216
  registry.mcp = registry.mcp || {};
@@ -176,31 +221,26 @@ function connectResource(inputArgs) {
176
221
  status: 'Connected'
177
222
  };
178
223
  saveRegistry(registry);
179
- 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`);
180
225
  } else {
181
226
  console.log(`${C.red}[x] Tipe tidak dikenal. Gunakan "skill" atau "mcp".${C.reset}\n`);
182
227
  }
183
228
  }
184
229
 
185
- // Deep Multi-Location Scanner Function
230
+ // Deep Multi-Location Scanner Function with Result Popup Card
186
231
  function scanResources() {
187
- console.log(`${C.yellow}[...] Memindai folder internal & lokasi umum pengguna (Gemini/Claude/System)...${C.reset}`);
232
+ console.log(`\n${C.yellow}[...] Memindai lokasi internal, home user, Gemini CLI & Claude Desktop...${C.reset}`);
188
233
  registry = loadRegistry();
189
234
  registry.skills = registry.skills || {};
190
235
  registry.mcp = registry.mcp || {};
191
236
 
192
237
  const home = os.homedir();
193
238
  const searchTargets = [
194
- // Folder Internal TMPA
195
239
  { type: 'skill', dir: SKILLS_DIR },
196
240
  { type: 'mcp', dir: MCP_DIR },
197
-
198
- // Folder Umum Pengguna (Direct Home Directory)
199
241
  { type: 'skill', dir: path.join(home, 'skills') },
200
242
  { type: 'mcp', dir: path.join(home, 'mcp') },
201
243
  { type: 'mcp', dir: path.join(home, '.mcp') },
202
-
203
- // Folder Konfigurasi Gemini CLI & Claude Desktop
204
244
  { type: 'skill', dir: path.join(home, '.gemini', 'skills') },
205
245
  { type: 'mcp', dir: path.join(home, '.gemini', 'mcp') },
206
246
  { type: 'skill', dir: path.join(home, '.config', 'gemini', 'skills') },
@@ -209,7 +249,7 @@ function scanResources() {
209
249
  { type: 'mcp', dir: path.join(home, '.config', 'claude', 'mcp') }
210
250
  ];
211
251
 
212
- let addedCount = 0;
252
+ let newDetected = [];
213
253
 
214
254
  searchTargets.forEach(({ type, dir }) => {
215
255
  if (fs.existsSync(dir)) {
@@ -219,28 +259,40 @@ function scanResources() {
219
259
  const fullPath = path.join(dir, item);
220
260
  const name = path.basename(item, path.extname(item));
221
261
 
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
- }
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}`);
234
268
  }
235
269
  });
236
- } catch (e) {
237
- // Skip folder jika permission terikat
238
- }
270
+ } catch (e) {}
239
271
  }
240
272
  });
241
273
 
242
274
  saveRegistry(registry);
243
- console.log(`${C.green}[+] Pemindaian selesai. Total item baru terdeteksi: ${addedCount}${C.reset}\n`);
275
+
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('');
244
296
  }
245
297
 
246
298
  function askConfig(callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {