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.
- package/bin/index.js +117 -44
- 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
|
|
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
|
|
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
|
|
127
|
+
"\nIf user wants to execute a skill/MCP, assist them using available tools.\n";
|
|
116
128
|
}
|
|
117
129
|
|
|
118
|
-
// Handlers for Skills
|
|
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
|
|
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}
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
232
|
+
console.log(`\n${C.yellow}[...] Memindai lokasi internal, home user, Gemini CLI & Claude Desktop...${C.reset}`);
|
|
189
233
|
registry = loadRegistry();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
}
|