@deerdesign/deertools 1.2.26 → 1.2.31

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/src/skills-hub.js CHANGED
@@ -5,8 +5,11 @@ const crypto = require('crypto');
5
5
  const credentials = require('./credentials');
6
6
  const { fetchText } = require('./http');
7
7
  const { parseFrontmatterName } = require('./openapi');
8
+ const { compareSemver } = require('./updater');
8
9
 
9
10
  const DEFAULT_API_BASE = 'https://ezspmlsnmxuhtzvwrnoj.supabase.co/functions/v1/skills-api';
11
+ const DEFAULT_SUPABASE_URL = 'https://ezspmlsnmxuhtzvwrnoj.supabase.co';
12
+ const DEFAULT_ANON_KEY = 'sb_publishable_y8iokK-V2X6A1HZb94g_pw_Le0bm3f2';
10
13
  const SECRET_PATTERNS = [
11
14
  { name: 'DeerTools Live API Key', regex: /deer_[a-z]+_live_[A-Za-z0-9_]{20,}/g },
12
15
  { name: 'GitHub Token', regex: /gh[pousr]_[A-Za-z0-9_]{30,}/g },
@@ -86,6 +89,67 @@ async function listSkills(params = {}) {
86
89
  return hubRequest(`/skills${qs ? `?${qs}` : ''}`);
87
90
  }
88
91
 
92
+ function getSupabaseUrl() {
93
+ return process.env.DEERTOOLS_SUPABASE_URL || DEFAULT_SUPABASE_URL;
94
+ }
95
+
96
+ function getSupabaseAnonKey() {
97
+ return process.env.DEERTOOLS_SUPABASE_KEY || DEFAULT_ANON_KEY;
98
+ }
99
+
100
+ /**
101
+ * Serwerowe wyszukiwanie wektorowo-hybrydowe (PostgreSQL pgvector + trigram + keyword boost).
102
+ */
103
+ async function searchSkillsHybrid({ queryText = '', queryEmbedding = null, threshold = 0.05, count = 50 } = {}) {
104
+ const base = getSupabaseUrl().replace(/\/+$/, '');
105
+ const url = `${base}/rest/v1/rpc/match_skills_hybrid`;
106
+ const anonKey = getSupabaseAnonKey();
107
+ const userKey = getApiKey();
108
+
109
+ const body = {
110
+ query_text: String(queryText || ''),
111
+ query_embedding: queryEmbedding || null,
112
+ match_threshold: threshold,
113
+ match_count: count,
114
+ };
115
+ if (userKey) {
116
+ body.caller_api_key = userKey;
117
+ }
118
+
119
+ const headers = {
120
+ 'Content-Type': 'application/json',
121
+ 'apikey': anonKey,
122
+ 'Authorization': `Bearer ${anonKey}`,
123
+ 'User-Agent': 'deertools-cli',
124
+ };
125
+
126
+ try {
127
+ const res = await fetch(url, {
128
+ method: 'POST',
129
+ headers,
130
+ body: JSON.stringify(body),
131
+ });
132
+ let data = null;
133
+ try {
134
+ data = await res.json();
135
+ } catch (_) {
136
+ data = null;
137
+ }
138
+ return {
139
+ ok: res.ok,
140
+ status: res.status,
141
+ data,
142
+ };
143
+ } catch (err) {
144
+ return {
145
+ ok: false,
146
+ status: 500,
147
+ data: null,
148
+ error: err.message,
149
+ };
150
+ }
151
+ }
152
+
89
153
  async function getSkill(slug, version = null) {
90
154
  const ver = typeof version === 'object' && version !== null ? version.version : version;
91
155
  const qs = ver ? `?version=${encodeURIComponent(ver)}` : '';
@@ -272,6 +336,7 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
272
336
  let title = slug;
273
337
  let description = '';
274
338
  let frontmatterTags = [];
339
+ let frontmatterVersion = null;
275
340
  if (skillContent.startsWith('---')) {
276
341
  const parts = skillContent.split('---', 2);
277
342
  if (parts.length >= 2) {
@@ -282,6 +347,8 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
282
347
  }
283
348
  const descMatch = parts[1].match(/^description:\s*(.+)$/m);
284
349
  if (descMatch) description = descMatch[1].trim();
350
+ const verMatch = parts[1].match(/^version:\s*['"]?([0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9.]+)?)/im);
351
+ if (verMatch) frontmatterVersion = verMatch[1].trim();
285
352
  frontmatterTags = extractFrontmatterTags(parts[1]);
286
353
  }
287
354
  }
@@ -301,9 +368,15 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
301
368
  try {
302
369
  const existing = await getSkill(slug);
303
370
  const latestVer = existing?.data?.skill?.latest_version;
304
- targetVersion = latestVer ? bumpVersion(latestVer, bump) : '1.0.0';
371
+ if (frontmatterVersion && latestVer && compareSemver(frontmatterVersion, latestVer) > 0) {
372
+ targetVersion = frontmatterVersion;
373
+ } else if (frontmatterVersion && !latestVer) {
374
+ targetVersion = frontmatterVersion;
375
+ } else {
376
+ targetVersion = latestVer ? bumpVersion(latestVer, bump) : (frontmatterVersion || '1.0.0');
377
+ }
305
378
  } catch (_) {
306
- targetVersion = '1.0.0';
379
+ targetVersion = frontmatterVersion || '1.0.0';
307
380
  }
308
381
  }
309
382
 
@@ -462,8 +535,11 @@ async function submitReview(slug) {
462
535
  module.exports = {
463
536
  getApiBase,
464
537
  getApiKey,
538
+ getSupabaseUrl,
539
+ getSupabaseAnonKey,
465
540
  scanForSecrets,
466
541
  listSkills,
542
+ searchSkillsHybrid,
467
543
  getSkill,
468
544
  pushSkill,
469
545
  bumpVersion,
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+ const { runSupplierMcpServer } = require('./mcp-server');
3
+
4
+ function out(msg) { process.stdout.write(msg + '\n'); }
5
+
6
+ async function cmdSupplier(pos) {
7
+ const sub = pos[0];
8
+
9
+ if (sub === 'mcp') {
10
+ return runSupplierMcpServer();
11
+ }
12
+
13
+ out(`Użycie: deertools supplier mcp
14
+
15
+ Zgodnie ze standardem architektonicznym DeerTools CLI:
16
+ - Operacje biznesowe na rabatach, narzutach i dostawcach wykonuje Agent AI wyłącznie przez serwer MCP (deer-supplier).
17
+ - Dostępne narzędzia MCP:
18
+ * supplier_list_suppliers
19
+ * supplier_get_discounts
20
+ * supplier_get_markups
21
+ * supplier_get_sales_discounts
22
+ * supplier_evaluate_batch
23
+ * supplier_list_rules
24
+ * supplier_list_taxonomy
25
+ * supplier_list_configurations
26
+
27
+ Aby zainstalować serwer MCP w Cursor / Antigravity, uruchom:
28
+ deertools mcp install
29
+ `);
30
+ }
31
+
32
+ module.exports = { cmdSupplier };
@@ -0,0 +1,360 @@
1
+ 'use strict';
2
+ const readline = require('readline');
3
+ const credentials = require('../credentials');
4
+ const { getProduct } = require('../registry');
5
+
6
+ const PROTOCOL_VERSION = '2024-11-05';
7
+ const SERVER_INFO = { name: 'deer-supplier-mcp', version: '1.0.0' };
8
+ const SUPPLIER_API_BASE = 'https://hubnxvzxdgocbtmkfdsz.supabase.co/functions/v1/public-api';
9
+
10
+ function getSupplierKey() {
11
+ const p = getProduct('supplier');
12
+ const cred = credentials.getKey(p);
13
+ if (cred && cred.key) return cred.key;
14
+ if (process.env.SUPPLIER_API_KEY) return process.env.SUPPLIER_API_KEY;
15
+ if (process.env.DEERTOOLS_SUPPLIER_KEY) return process.env.DEERTOOLS_SUPPLIER_KEY;
16
+ return null;
17
+ }
18
+
19
+ function ensureSupplierAuth() {
20
+ const key = getSupplierKey();
21
+ if (!key) {
22
+ throw new Error('Brak klucza API do Supplier Hub. Zaloguj się poleceniem: deertools login supplier');
23
+ }
24
+ return key;
25
+ }
26
+
27
+ async function callSupplierApi(method, path, { query = {}, body = undefined } = {}) {
28
+ const apiKey = ensureSupplierAuth();
29
+ const url = new URL(SUPPLIER_API_BASE + path);
30
+ for (const [k, v] of Object.entries(query)) {
31
+ if (v !== undefined && v !== null && v !== '') {
32
+ url.searchParams.set(k, String(v));
33
+ }
34
+ }
35
+ const headers = {
36
+ 'x-api-key': apiKey,
37
+ 'Accept': 'application/json'
38
+ };
39
+ let payload;
40
+ if (body !== undefined) {
41
+ headers['Content-Type'] = 'application/json';
42
+ payload = JSON.stringify(body);
43
+ }
44
+ const res = await fetch(url.toString(), { method, headers, body: payload });
45
+ const text = await res.text();
46
+ let json = null;
47
+ try { json = JSON.parse(text); } catch (_) {}
48
+ if (!res.ok) {
49
+ throw new Error(`Supplier Hub API (${res.status}): ${(json && json.error) || text}`);
50
+ }
51
+ return json !== null ? json : text;
52
+ }
53
+
54
+ const SUPPLIER_TOOLS = [
55
+ {
56
+ name: 'supplier_list_suppliers',
57
+ description: 'Wyszukuje dostawców dla producenta i lokalizacji (kod pocztowy, miasto lub województwo) w DeerTools Supplier Hub.',
58
+ inputSchema: {
59
+ type: 'object',
60
+ properties: {
61
+ manufacturer: {
62
+ type: 'string',
63
+ description: 'Nazwa, external_id lub UUID producenta (np. "Flügger", "LAUFEN", "Roca").'
64
+ },
65
+ postal_code: {
66
+ type: 'string',
67
+ description: 'Kod pocztowy w formacie XX-XXX (np. "00-001", "30-001").'
68
+ },
69
+ city: {
70
+ type: 'string',
71
+ description: 'Nazwa miasta/gminy (np. "Warszawa", "Kraków").'
72
+ },
73
+ voivodeship: {
74
+ type: 'string',
75
+ description: 'Nazwa województwa.'
76
+ },
77
+ pick: {
78
+ type: 'string',
79
+ enum: ['random', 'first', 'all'],
80
+ default: 'all',
81
+ description: 'Sposób zwracania dopasowanych dostawców (domyślnie all).'
82
+ }
83
+ },
84
+ required: ['manufacturer']
85
+ }
86
+ },
87
+ {
88
+ name: 'supplier_get_discounts',
89
+ description: 'Dopasowuje aktywne rabaty zakupowe w Supplier Hub dla producenta, kolekcji, kategorii lub produktu.',
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
94
+ supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
95
+ collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
96
+ category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
97
+ tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
98
+ product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
99
+ best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najwyżej dopasowany rabat.' }
100
+ },
101
+ required: ['manufacturer']
102
+ }
103
+ },
104
+ {
105
+ name: 'supplier_get_markups',
106
+ description: 'Dopasowuje narzuty cenowe w Supplier Hub dla producenta, kolekcji, kategorii lub produktu.',
107
+ inputSchema: {
108
+ type: 'object',
109
+ properties: {
110
+ manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
111
+ supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
112
+ collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
113
+ category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
114
+ tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
115
+ product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
116
+ best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najlepiej dopasowany narzut.' }
117
+ },
118
+ required: ['manufacturer']
119
+ }
120
+ },
121
+ {
122
+ name: 'supplier_get_sales_discounts',
123
+ description: 'Dopasowuje aktywne promocje sprzedaży w Supplier Hub dla producenta, kolekcji lub produktu.',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
128
+ supplier: { type: 'string', description: 'Opcjonalna nazwa lub UUID dostawcy.' },
129
+ collection: { type: 'string', description: 'Opcjonalna nazwa kolekcji.' },
130
+ category: { type: 'string', description: 'Opcjonalna nazwa kategorii.' },
131
+ tag: { type: 'string', description: 'Opcjonalny tag produktu.' },
132
+ product_id: { type: 'string', description: 'Opcjonalny identyfikator lub SKU produktu.' },
133
+ best_only: { type: 'boolean', default: false, description: 'Czy zwrócić wyłącznie najlepiej dopasowaną promocję.' }
134
+ },
135
+ required: ['manufacturer']
136
+ }
137
+ },
138
+ {
139
+ name: 'supplier_evaluate_batch',
140
+ description: 'Wsadowe dopasowanie rabatów zakupowych, narzutów lub promocji sprzedaży dla maksymalnie 500 produktów jednego producenta.',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ type: {
145
+ type: 'string',
146
+ enum: ['discounts', 'markups', 'sales-discounts'],
147
+ default: 'discounts',
148
+ description: 'Typ wyliczanych reguł: discounts (rabaty zakupowe), markups (narzuty), sales-discounts (promocje).'
149
+ },
150
+ manufacturer: { type: 'string', description: 'Nazwa, external_id lub UUID producenta.' },
151
+ best_only: { type: 'boolean', default: true, description: 'Czy zwracać tylko najlepsze dopasowanie per pozycja.' },
152
+ items: {
153
+ type: 'array',
154
+ description: 'Lista pozycji do przeliczenia (każda z unikalnym kluczem key).',
155
+ items: {
156
+ type: 'object',
157
+ properties: {
158
+ key: { type: 'string' },
159
+ product_id: { type: 'string' },
160
+ collection: { type: 'string' },
161
+ category: { type: 'string' },
162
+ tag: { type: 'string' }
163
+ },
164
+ required: ['key']
165
+ }
166
+ }
167
+ },
168
+ required: ['manufacturer', 'items']
169
+ }
170
+ },
171
+ {
172
+ name: 'supplier_list_rules',
173
+ description: 'Pobiera listę reguł cenowych (discounts, sales-discounts lub markups) wraz z warunkami.',
174
+ inputSchema: {
175
+ type: 'object',
176
+ properties: {
177
+ type: {
178
+ type: 'string',
179
+ enum: ['discounts', 'markups', 'sales-discounts'],
180
+ default: 'discounts',
181
+ description: 'Typ reguł cenowych.'
182
+ },
183
+ limit: { type: 'number', default: 50, description: 'Limit wyników (domyślnie 50).' },
184
+ include_conditions: { type: 'boolean', default: true, description: 'Czy dołączyć warunki reguł.' }
185
+ }
186
+ }
187
+ },
188
+ {
189
+ name: 'supplier_list_taxonomy',
190
+ description: 'Wyszukuje pozycje taksonomii w Supplier Hub (collections, categories lub tags).',
191
+ inputSchema: {
192
+ type: 'object',
193
+ properties: {
194
+ type: {
195
+ type: 'string',
196
+ enum: ['collections', 'categories', 'tags'],
197
+ default: 'collections',
198
+ description: 'Typ taksonomii.'
199
+ },
200
+ search: { type: 'string', description: 'Fraza wyszukiwania.' },
201
+ limit: { type: 'number', default: 50, description: 'Limit wyników.' }
202
+ }
203
+ }
204
+ },
205
+ {
206
+ name: 'supplier_list_configurations',
207
+ description: 'Pobiera konfiguracje powiązań dostawca–producent z Supplier Hub.',
208
+ inputSchema: {
209
+ type: 'object',
210
+ properties: {}
211
+ }
212
+ }
213
+ ];
214
+
215
+ async function handleMcpMessage(msg) {
216
+ if (!msg || typeof msg !== 'object') return null;
217
+ const { id, method, params } = msg;
218
+
219
+ if (method === 'initialize') {
220
+ return {
221
+ jsonrpc: '2.0',
222
+ id,
223
+ result: {
224
+ protocolVersion: PROTOCOL_VERSION,
225
+ capabilities: { tools: {} },
226
+ serverInfo: SERVER_INFO
227
+ }
228
+ };
229
+ }
230
+
231
+ if (method === 'notifications/initialized' || method === 'initialized') {
232
+ return null;
233
+ }
234
+
235
+ if (method === 'ping') {
236
+ return { jsonrpc: '2.0', id, result: {} };
237
+ }
238
+
239
+ if (method === 'tools/list') {
240
+ return {
241
+ jsonrpc: '2.0',
242
+ id,
243
+ result: { tools: SUPPLIER_TOOLS }
244
+ };
245
+ }
246
+
247
+ if (method === 'tools/call') {
248
+ const toolName = params?.name;
249
+ const args = params?.arguments || {};
250
+
251
+ try {
252
+ let data;
253
+ if (toolName === 'supplier_list_suppliers') {
254
+ data = await callSupplierApi('GET', '/suppliers', {
255
+ query: {
256
+ manufacturer: args.manufacturer,
257
+ postal_code: args.postal_code,
258
+ city: args.city,
259
+ voivodeship: args.voivodeship,
260
+ pick: args.pick || 'all'
261
+ }
262
+ });
263
+ } else if (toolName === 'supplier_get_discounts') {
264
+ data = await callSupplierApi('GET', '/discounts', { query: args });
265
+ } else if (toolName === 'supplier_get_markups') {
266
+ data = await callSupplierApi('GET', '/markups', { query: args });
267
+ } else if (toolName === 'supplier_get_sales_discounts') {
268
+ data = await callSupplierApi('GET', '/sales-discounts', { query: args });
269
+ } else if (toolName === 'supplier_evaluate_batch') {
270
+ const type = args.type || 'discounts';
271
+ data = await callSupplierApi('POST', `/${type}/evaluate`, {
272
+ body: {
273
+ manufacturer: args.manufacturer,
274
+ best_only: args.best_only !== false,
275
+ items: args.items
276
+ }
277
+ });
278
+ } else if (toolName === 'supplier_list_rules') {
279
+ const type = args.type || 'discounts';
280
+ data = await callSupplierApi('GET', `/rules/${type}`, {
281
+ query: {
282
+ limit: args.limit || 50,
283
+ include_conditions: args.include_conditions !== false
284
+ }
285
+ });
286
+ } else if (toolName === 'supplier_list_taxonomy') {
287
+ const type = args.type || 'collections';
288
+ data = await callSupplierApi('GET', `/taxonomy/${type}`, {
289
+ query: { search: args.search, limit: args.limit || 50 }
290
+ });
291
+ } else if (toolName === 'supplier_list_configurations') {
292
+ data = await callSupplierApi('GET', '/supplier-configurations');
293
+ } else {
294
+ return {
295
+ jsonrpc: '2.0',
296
+ id,
297
+ error: { code: -32601, message: `Nieznane narzędzie: ${toolName}` }
298
+ };
299
+ }
300
+
301
+ return {
302
+ jsonrpc: '2.0',
303
+ id,
304
+ result: {
305
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }]
306
+ }
307
+ };
308
+ } catch (e) {
309
+ return {
310
+ jsonrpc: '2.0',
311
+ id,
312
+ error: { code: -32000, message: e.message }
313
+ };
314
+ }
315
+ }
316
+
317
+ return {
318
+ jsonrpc: '2.0',
319
+ id,
320
+ error: { code: -32601, message: `Nieobsługiwana metoda: ${method}` }
321
+ };
322
+ }
323
+
324
+ function runSupplierMcpServer() {
325
+ return new Promise((resolve) => {
326
+ const rl = readline.createInterface({
327
+ input: process.stdin,
328
+ output: process.stdout,
329
+ terminal: false
330
+ });
331
+
332
+ rl.on('line', async (line) => {
333
+ const trimmed = line.trim();
334
+ if (!trimmed) return;
335
+ try {
336
+ const msg = JSON.parse(trimmed);
337
+ const resp = await handleMcpMessage(msg);
338
+ if (resp) {
339
+ process.stdout.write(JSON.stringify(resp) + '\n');
340
+ }
341
+ } catch (e) {
342
+ process.stdout.write(JSON.stringify({
343
+ jsonrpc: '2.0',
344
+ id: null,
345
+ error: { code: -32700, message: `Parse error: ${e.message}` }
346
+ }) + '\n');
347
+ }
348
+ });
349
+
350
+ rl.on('close', () => resolve());
351
+ process.on('SIGINT', () => { rl.close(); resolve(); });
352
+ process.on('SIGTERM', () => { rl.close(); resolve(); });
353
+ });
354
+ }
355
+
356
+ module.exports = {
357
+ SUPPLIER_TOOLS,
358
+ handleMcpMessage,
359
+ runSupplierMcpServer
360
+ };
package/src/tui.js CHANGED
@@ -66,67 +66,52 @@ ${c.bold}${c.green} ____ _____ _
66
66
  | | | |/ _ \\/ _ \\ '__| | |/ _ \\ / _ \\ | / __|
67
67
  | |_| | __/ __/ | | | (_) | (_) || \\__ \\
68
68
  |____/ \\___|\\___|_| |_|\\___/ \\___(_)_|___/${c.reset}
69
- ${c.dim}Ekosystem narzędzi wykończeniowych Deer Design (v1.0.5)${c.reset}
69
+ ${c.dim}Identity, MCP & Skills Hub Manager — Deer Design${c.reset}
70
70
  \n`);
71
71
 
72
- const choice = await selectMenu('Co chcesz zrobić?', [
73
- { label: '1. Szybka wycena z metrażu (Kosztorys)', value: 'estimate_calc' },
74
- { label: '2. Wyszukaj produkt w Katalogu Connect', value: 'catalog_search' },
75
- { label: '3. Sprawdź status dostaw i logistyki', value: 'deliveries_list' },
76
- { label: '4. Kalkulator marży i rabatów (Supplier Hub)', value: 'supplier_calc' },
77
- { label: '5. Diagnostyka środowiska i połączeń (Doctor)', value: 'doctor' },
78
- { label: '6. Sprawdź status logowania i kluczy (Whoami)', value: 'whoami' },
79
- { label: '7. Zaloguj do wszystkich produktów (OAuth)', value: 'login' },
80
- { label: '8. Zainstaluj integracje MCP dla Cursor / Antigravity', value: 'mcp_install' },
81
- { label: '9. Historia wykonanych operacji (Audit log)', value: 'audit' },
72
+ const choice = await selectMenu('Wybierz operację administracyjną:', [
73
+ { label: '1. Status logowania i sesji (Whoami)', value: 'whoami' },
74
+ { label: '2. Zaloguj do systemów Deer Design (OAuth PKCE)', value: 'login' },
75
+ { label: '3. Zainstaluj serwery MCP dla Cursor / Antigravity', value: 'mcp_install' },
76
+ { label: '4. Synchronizuj skille z DeerSkills Hub (Skill Sync)', value: 'skill_sync' },
77
+ { label: '5. Przeglądaj skille w DeerSkills Hub (Skill List)', value: 'skill_list' },
78
+ { label: '6. Platformy agentów AI na komputerze (Agent List)', value: 'agent_list' },
79
+ { label: '7. Diagnostyka środowiska i połączeń (Doctor)', value: 'doctor' },
80
+ { label: '8. Historia operacji (Audit log)', value: 'audit' },
82
81
  ]);
83
82
 
84
- if (choice === 'estimate_calc') {
85
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
86
- rl.question('\nPodaj metraż mieszkania w m² (np. 64): ', (ans) => {
87
- rl.close();
88
- const area = Number(ans) || 50;
89
- dispatch(['estimate', 'calculate', String(area)], {});
90
- });
83
+ if (choice === 'whoami') {
84
+ await dispatch(['whoami'], {});
91
85
  return;
92
86
  }
93
87
 
94
- if (choice === 'catalog_search') {
95
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
96
- rl.question('\nSzukana fraza (np. bateria, płytka): ', (ans) => {
97
- rl.close();
98
- dispatch(['catalog', 'search', ans || 'bateria'], {});
99
- });
88
+ if (choice === 'login') {
89
+ await dispatch(['login', 'all'], {});
100
90
  return;
101
91
  }
102
92
 
103
- if (choice === 'deliveries_list') {
104
- await dispatch(['deliveries', 'list'], {});
93
+ if (choice === 'mcp_install') {
94
+ await dispatch(['mcp', 'install'], {});
105
95
  return;
106
96
  }
107
97
 
108
- if (choice === 'supplier_calc') {
109
- await dispatch(['supplier', 'calculate', '1000', '35', '20'], {});
98
+ if (choice === 'skill_sync') {
99
+ await dispatch(['skill', 'sync'], {});
110
100
  return;
111
101
  }
112
102
 
113
- if (choice === 'doctor') {
114
- await dispatch(['doctor'], {});
103
+ if (choice === 'skill_list') {
104
+ await dispatch(['skill', 'list'], {});
115
105
  return;
116
106
  }
117
107
 
118
- if (choice === 'whoami') {
119
- await dispatch(['whoami'], {});
108
+ if (choice === 'agent_list') {
109
+ await dispatch(['agent', 'list'], {});
120
110
  return;
121
111
  }
122
112
 
123
- if (choice === 'login') {
124
- await dispatch(['login', 'all'], {});
125
- return;
126
- }
127
-
128
- if (choice === 'mcp_install') {
129
- await dispatch(['mcp', 'install'], {});
113
+ if (choice === 'doctor') {
114
+ await dispatch(['doctor'], {});
130
115
  return;
131
116
  }
132
117