@deerdesign/deertools 1.2.25 → 1.2.26

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/README.md CHANGED
@@ -127,9 +127,9 @@ deertools api rendermind POST /api/v1/visualizations --data '{"prompt":"Modern S
127
127
  deertools api rendermind POST /api/v1/visualizations --data-file request.json
128
128
  ```
129
129
 
130
- ### 3. Specialized Helpers & Domain Commands
130
+ ### 3. Developer & Environment Utilities
131
131
 
132
- DeerTools CLI provides first-class domain commands with colored table outputs and JSON flags:
132
+ DeerTools CLI focuses strictly on authentication, Remote MCP orchestration, and DeerSkills Hub management. Domain calculations and business logic are hosted directly on the product platforms (Kosztorys, Katalog, Dostawy, Supplier Hub, RenderMind) and accessed via their own REST APIs and Remote MCP endpoints.
133
133
 
134
134
  ```bash
135
135
  # 1. Environment & API Doctor:
@@ -139,31 +139,7 @@ deertools doctor --json
139
139
  # 2. Shell Autocompletion (Zsh, Bash, Fish):
140
140
  deertools completion install # automatically configures ~/.zshrc or ~/.bashrc
141
141
 
142
- # 3. Catalog (Material Specs & Products):
143
- deertools catalog search "bateria nero" --package modern-life --limit 10
144
- deertools catalog get <sku_or_uuid>
145
- deertools catalog categories
146
- deertools catalog manufacturers
147
-
148
- # 4. Deliveries (Logistics & Shipments):
149
- deertools deliveries list --status active
150
- deertools deliveries track <tracking_number>
151
- deertools deliveries suppliers
152
-
153
- # 5. Estimate (Cost Calculations & Floor Plan AI):
154
- deertools estimate calculate 64.5 modern_life
155
- deertools estimate margins 64.5
156
- deertools estimate --file floorplan.pdf --kitchen 6
157
-
158
- # 6. Supplier Hub (Discounts & Price Chains):
159
- deertools supplier discounts Flügger --postal_code 00-001
160
- deertools supplier markups warszawa
161
- deertools supplier calculate 1000 --discount 35 --markup 25
162
-
163
- # 7. RenderMind (Interior Styles & Presets):
164
- deertools rendermind styles
165
-
166
- # 8. BaseLinker Hub & MCP Proxy:
142
+ # 3. BaseLinker Hub & MCP Proxy:
167
143
  deertools baselinker auth <X-BLToken> # Zapisz token w credentials.json
168
144
  deertools baselinker test # Sprawdź połączenie i listę katalogów
169
145
  deertools baselinker serve --port 3456 # Uruchom lokalny dashboard Web UI + MCP SSE
@@ -173,18 +149,18 @@ deertools baselinker search "relations" # Wyszukaj metody i schematy par
173
149
  deertools baselinker call getOrders # Bezpośrednie wywołanie API z terminala
174
150
  deertools baselinker catalog search 105 "dąb" # Wyszukaj produkty w katalogu BaseLinkera
175
151
 
176
- # 9. Local Stdio MCP Bridge (for Claude Desktop, Ollama, local LLM tools):
152
+ # 4. Local Stdio MCP Bridge (for Claude Desktop, Ollama, local LLM tools):
177
153
  deertools mcp serve
178
154
 
179
- # 10. Audit Log:
155
+ # 5. Audit Log:
180
156
  deertools audit --limit 25
181
157
  deertools audit --clear
182
158
 
183
- # 11. Multi-Profile Management:
159
+ # 6. Multi-Profile Management:
184
160
  deertools profile list
185
161
  deertools profile use staging
186
162
 
187
- # 12. AI Agent Skills (DeerSkills Hub):
163
+ # 7. AI Agent Skills (DeerSkills Hub):
188
164
  deertools skill list # Wyświetl listę skilli ze statusem zainstalowania w projekcie
189
165
  deertools skill sync # Jedna komenda: instaluje brakujące i aktualizuje obecne skille zespołu
190
166
  deertools skill install --all # To samo co skill sync (pełna synchronizacja zespołu)
@@ -200,7 +176,7 @@ deertools skill update deer-kosztorys # Zaktualizuj zainstalowany skill d
200
176
  deertools skill update # Zaktualizuj wszystkie zainstalowane skille
201
177
  deertools skill promote my-skill # Zgłoś wniosek do admina o nominację do globalnego katalogu
202
178
 
203
- # 13. Team Management (Konfiguracja Zespołów):
179
+ # 8. Team Management (Konfiguracja Zespołów):
204
180
  deertools team list # Lista zespołów, liczba członków i skilli
205
181
  deertools team members architekci # Członkowie i role w zespole architekci
206
182
  deertools team add architekci jan@deer.pl # Dodaj członka do zespołu (rola member)
@@ -208,7 +184,7 @@ deertools team add architekci ewa@deer.pl --role lead # Dodaj lidera zespołu
208
184
  deertools team remove architekci jan@deer.pl # Usuń członka z zespołu
209
185
  deertools team create "Projektanci Wnętrz" --slug projektanci # Utwórz nowy zespół
210
186
 
211
- # 14. AI Agent MCP Registry & Configuration:
187
+ # 9. AI Agent MCP Registry & Configuration:
212
188
  deertools mcp list # Lista dostępnych serwerów MCP w ekosystemie
213
189
  deertools mcp config # Wygeneruj konfigurację JSON dla Cursor, Claude, Antigravity
214
190
  deertools mcp install # Automatycznie dopisz konfigurację do narzędzi AI
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deerdesign/deertools",
3
- "version": "1.2.25",
3
+ "version": "1.2.26",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
package/src/cli.js CHANGED
@@ -31,13 +31,12 @@ async function main(argv) {
31
31
 
32
32
  const table = {
33
33
  login: c.cmdLogin, logout: c.cmdLogout, whoami: c.cmdWhoami, docs: c.cmdDocs,
34
- endpoints: c.cmdEndpoints, api: c.cmdApi, estimate: c.cmdEstimate, skill: c.cmdSkill,
34
+ endpoints: c.cmdEndpoints, api: c.cmdApi, skill: c.cmdSkill,
35
35
  sync: (pos, flags) => c.cmdSkill(['sync', ...pos], flags),
36
36
  agent: c.cmdAgent, agents: c.cmdAgent,
37
37
  team: c.cmdTeam, teams: c.cmdTeam,
38
38
  mcp: c.cmdMcp, doctor: c.cmdDoctor, audit: c.cmdAudit, profile: c.cmdProfile,
39
- completion: c.cmdCompletion, catalog: c.cmdCatalog, deliveries: c.cmdDeliveries,
40
- supplier: c.cmdSupplier, rendermind: c.cmdRendermind, baselinker: c.cmdBaseLinker,
39
+ completion: c.cmdCompletion, baselinker: c.cmdBaseLinker,
41
40
  update: require('./updater').cmdUpdate,
42
41
  };
43
42
 
package/src/commands.js CHANGED
@@ -7,7 +7,6 @@ const { getProduct, resolveProducts, ALL_IDS } = require('./registry');
7
7
  const { login, normalizeScope } = require('./login');
8
8
  const { discover, fetchText, apiRequest, explain } = require('./http');
9
9
  const { listOperations, parseFrontmatterName } = require('./openapi');
10
- const { calculateEstimate, getEstimateMargins } = require('./estimate-client');
11
10
 
12
11
  const out = (s = '') => process.stdout.write(`${s}\n`);
13
12
  const err = (s = '') => process.stderr.write(`${s}\n`);
@@ -215,153 +214,6 @@ async function cmdApi(pos, flags) {
215
214
  out(res.json ? JSON.stringify(res.json, null, flags.raw ? 0 : 2) : res.text);
216
215
  }
217
216
 
218
- async function cmdEstimate(pos, flags, options = {}) {
219
- const sub = pos[0];
220
- const write = options.out || out;
221
-
222
- if (sub === 'calculate' || sub === 'calc') {
223
- const area = Number(pos[1] || flags.area);
224
- if (!area || isNaN(area)) fail('Użycie: deertools estimate calculate <metraż_m2> [smart_life|modern_life|high_life] [--wet=<liczba>] [--doors=<liczba>] [--basis=avg|max]', 2);
225
-
226
- const requested = pos[2] || flags.package;
227
- const wetInput = flags.wet !== undefined ? Number(flags.wet) : (flags.wetZones !== undefined ? Number(flags.wetZones) : undefined);
228
- const doorsInput = flags.doors !== undefined ? Number(flags.doors) : undefined;
229
-
230
- const data = await calculateEstimate({
231
- area_m2: area,
232
- package: requested || undefined,
233
- wet_zones: wetInput,
234
- doors: doorsInput,
235
- }, options);
236
-
237
- const estimates = (data && data.estimates) ? data.estimates : [];
238
- if (!estimates.length) {
239
- fail('Nie otrzymano wyników kalkulacji z serwera Kosztorysu.', 4);
240
- }
241
-
242
- const results = estimates.map(e => ({
243
- package: e.package,
244
- package_key: e.slug,
245
- slug: e.slug,
246
- area_m2: e.area_m2,
247
- price_per_m2_gross: e.price_per_m2_gross,
248
- is_flat_rate: !!e.is_lump_sum,
249
- is_lump_sum: !!e.is_lump_sum,
250
- lump_sum_max_area_m2: e.lump_sum_max_area_m2,
251
- base_gross_pln: e.base_gross,
252
- base_net_pln: Math.round((e.base_gross / 1.08) * 100) / 100,
253
- standard_wet_zones: e.standard_wet_zones,
254
- wet_zones: e.wet_zones,
255
- extra_wet_zones: Math.max(0, (e.wet_zones || 0) - (e.standard_wet_zones || 0)),
256
- wet_surcharge_gross_pln: e.wet_zone_surcharge_gross,
257
- wet_surcharge_net_pln: Math.round(((e.wet_zone_surcharge_gross || 0) / 1.08) * 100) / 100,
258
- standard_doors: e.standard_doors,
259
- doors: (e.standard_doors || 0) + (e.complexity_levels || 0),
260
- complexity_levels: e.complexity_levels,
261
- complexity_surcharge_label: e.complexity_surcharge_label || 'Dopłata z tytułu złożoności projektu',
262
- complexity_surcharge_gross_pln: e.complexity_surcharge_gross,
263
- complexity_surcharge_net_pln: Math.round(((e.complexity_surcharge_gross || 0) / 1.08) * 100) / 100,
264
- total_gross_pln: e.total_gross,
265
- total_net_pln: e.total_net,
266
- vat_pln: e.vat_pln,
267
- currency: e.currency || 'PLN',
268
- max_discount_pct: e.max_discount_pct,
269
- discount_locked: e.discount_locked,
270
- source: 'Kosztorys Backend API (estimate-mcp)',
271
- }));
272
-
273
- if (flags.json) {
274
- write(JSON.stringify(results.length === 1 ? results[0] : results, null, 2));
275
- return;
276
- }
277
-
278
- const isFlat = results.some(r => r.is_flat_rate);
279
- write(`\nKalkulacja wykończenia dla metrażu ${area} m² (źródło: Kosztorys API${isFlat ? ', ryczałt do 40 m²' : ''}):`);
280
- const fmt = (n) => new Intl.NumberFormat('pl-PL', { maximumFractionDigits: 0 }).format(Math.round(n));
281
- for (const r of results) {
282
- write(` ▶ ${r.package.padEnd(14)} ${fmt(r.total_gross_pln)} zł brutto (${fmt(r.total_net_pln)} zł netto + ${fmt(r.vat_pln)} zł VAT)`);
283
- if (r.extra_wet_zones > 0 || r.complexity_levels > 0) {
284
- write(` - Podstawa: ${fmt(r.base_gross_pln)} zł brutto`);
285
- if (r.extra_wet_zones > 0) write(` - Dodatkowe strefy mokre (+${r.extra_wet_zones}): +${fmt(r.wet_surcharge_gross_pln)} zł brutto`);
286
- if (r.complexity_levels > 0) write(` - ${r.complexity_surcharge_label} (+${r.complexity_levels} drzwi): +${fmt(r.complexity_surcharge_gross_pln)} zł brutto`);
287
- }
288
- }
289
- return;
290
- }
291
-
292
- if (sub === 'margins') {
293
- const area = Number(pos[1] || flags.area || 50);
294
-
295
- const data = await getEstimateMargins({
296
- area_m2: area,
297
- }, options);
298
-
299
- const estimates = (data && data.estimates) ? data.estimates : [];
300
- if (!estimates.length) {
301
- fail('Nie otrzymano reguł marżowych z serwera Kosztorysu.', 4);
302
- }
303
-
304
- const rows = estimates.map(e => ({
305
- package: e.package,
306
- slug: e.slug,
307
- area_m2: e.area_m2,
308
- base_gross_pln: e.base_gross,
309
- base_net_pln: e.total_net,
310
- is_flat_rate: !!e.is_lump_sum,
311
- std_wet_zones: e.standard_wet_zones,
312
- std_doors: e.standard_doors,
313
- wet_surcharge_gross: e.wet_zone_surcharge_gross,
314
- complexity_surcharge_label: e.complexity_surcharge_label || 'Dopłata z tytułu złożoności projektu',
315
- complexity_surcharge_gross: e.complexity_surcharge_gross,
316
- margin_rule_simplified: e.margin_rule_simplified,
317
- margin_rule_quick: e.margin_rule_quick,
318
- formula: e.formula,
319
- note: e.note,
320
- source: 'Kosztorys Backend API (estimate-mcp)',
321
- }));
322
-
323
- if (flags.json) { write(JSON.stringify(rows, null, 2)); return; }
324
- write(`\nZasady wyliczania marży Deer Design (Nowy standard 2026 z Kosztorysu dla ${area} m²):`);
325
- write(` • Kosztorys Uproszczony: ${rows[0]?.margin_rule_simplified}`);
326
- write(` • Kosztorys Szybki: ${rows[0]?.margin_rule_quick}`);
327
- write(` • Formuła marży: ${rows[0]?.formula}`);
328
- write(` • Uwaga: ${rows[0]?.note}\n`);
329
- for (const r of rows) {
330
- write(` ▶ ${r.package.padEnd(14)} Podstawa: ${r.base_gross_pln} zł brutto (${r.base_net_pln} zł netto) | Std: ${r.std_wet_zones} strefa mokra, ${r.std_doors} drzwi`);
331
- write(` Reguła marży: ${r.margin_rule_simplified}`);
332
- }
333
- return;
334
- }
335
-
336
- const file = flags.file || (sub === 'plan' ? pos[1] : pos[0]);
337
- if (!file) fail('Użycie:\n deertools estimate calculate <metraż> [pakiet]\n deertools estimate margins <metraż>\n deertools estimate --file <rzut.pdf> [--kitchen 6]', 2);
338
- if (!fs.existsSync(file)) fail(`Plik nie istnieje: ${file}`, 2);
339
- const bytes = fs.readFileSync(file);
340
- if (bytes.length > 10 * 1024 * 1024) fail('Plik jest większy niż 10 MB (limit analizy rzutu).', 2);
341
- const ext = path.extname(file).toLowerCase().slice(1);
342
- const mime = { pdf: 'application/pdf', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', webp: 'image/webp' }[ext];
343
- if (!mime) fail('Obsługiwane formaty: PDF, PNG, JPG, WEBP.', 2);
344
- const kitchen = flags.kitchen ? Number(String(flags.kitchen).replace(',', '.')) : 6;
345
- if (!(kitchen >= 2 && kitchen <= 30)) fail('--kitchen musi być w zakresie 2–30 m².', 2);
346
- const product = getProduct('kosztorys');
347
- out(`Analizuję ${path.basename(file)} (aneks ${kitchen} m²) — to trwa do ok. minuty…`);
348
- const res = await apiRequest(product, {
349
- method: 'POST',
350
- path: '/estimate-from-floor-plan',
351
- body: { file_url: `data:${mime};base64,${bytes.toString('base64')}`, file_name: path.basename(file), file_type: mime, kitchen_open_area_m2: kitchen },
352
- base: product.fallbackApiBase,
353
- });
354
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
355
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
356
- const d = res.json || {};
357
- const s = d.summary || {};
358
- out(`\nŁączny metraż: ${s.total_area_m2} m² · pokoje: ${s.rooms} · aneks wydzielony: ${s.kitchen_open_area_m2} m²`);
359
- for (const r of d.rooms || []) out(` • ${String(r.name).padEnd(26)} ${r.area_m2} m²`);
360
- out('\nPakiety (brutto / netto):');
361
- const fmt = (n) => new Intl.NumberFormat('pl-PL', { maximumFractionDigits: 0 }).format(Math.round(n));
362
- for (const p of d.packages || []) out(` ▶ ${String(p.name).padEnd(12)} ${fmt(p.total_gross)} zł / ${fmt(p.total_net)} zł${p.is_lump_sum ? ' (ryczałt < 45 m²)' : ''}`);
363
- }
364
-
365
217
  const { detectAgents, installSkillContent } = require('./agent-detector');
366
218
  const skillCommands = require('./skill-commands');
367
219
 
@@ -668,7 +520,6 @@ const { cmdDoctor } = require('./doctor');
668
520
  const { cmdAudit } = require('./audit');
669
521
  const { cmdProfile } = require('./profiles');
670
522
  const { cmdCompletion } = require('./completion');
671
- const { cmdCatalog, cmdDeliveries, cmdSupplier, cmdRendermind } = require('./domain-commands');
672
523
  const { cmdBaseLinker } = require('./baselinker-commands');
673
524
 
674
525
  const HELP = `
@@ -682,24 +533,7 @@ Dostęp i autoryzacja:
682
533
  deertools whoami [produkt|all] [--json] Status logowania, zakres, ważność.
683
534
  deertools profile [list|use <nazwa>|current] Przełączanie profili poświadczeń.
684
535
 
685
- Komendy domenowe:
686
- deertools catalog <search|get|categories|manufacturers> Zarządzanie Katalogiem Connect.
687
- deertools deliveries <list|track|suppliers> Zarządzanie Delivery Hub.
688
- deertools estimate <calculate|margins|plan> Kalkulacje i analiza rzutów PDF.
689
- deertools supplier <discounts|markups|calculate> Rabaty, narzuty i łańcuch marżowy.
690
- deertools rendermind <styles|get> Style i wizualizacje wnętrz.
691
-
692
- BaseLinker MCP Proxy & Hub:
693
- deertools baselinker auth [token] Zapisuje klucz API BaseLinkera w credentials.json.
694
- deertools baselinker test Testuje autoryzację i listuje katalogi.
695
- deertools baselinker serve [--port 3456] Lokalny serwer Web UI z dashboardem i MCP SSE.
696
- deertools baselinker mcp [--all] Mostek MCP stdio (Cursor / Claude Desktop).
697
- deertools baselinker sync-schema Pobiera i aktualizuje bazę 204 metod z api.baselinker.com.
698
- deertools baselinker search <fraza> Przeszukuje bazę metod i schematów parametrów.
699
- deertools baselinker call <metoda> [json] Wywołuje dowolną metodę BaseLinkera z CLI.
700
- deertools baselinker catalog <search|get> Operacje katalogowe z poziomu terminala.
701
-
702
- Integracje MCP i Agenci AI (DeerSkills Hub):
536
+ Zarządzanie skillami i zespołami (DeerSkills Hub):
703
537
  deertools agent [list|select <agenty>|reset] Wykrywanie i konfiguracja platform agentów AI na PC.
704
538
  deertools skill list [--tag <tag>] Przegląda skille w Hubie wraz ze statusem zainstalowania.
705
539
  deertools skill sync [--agent <id>|-i|--force] Instaluje brakujące i aktualizuje obecne skille zespołu.
@@ -712,6 +546,13 @@ Integracje MCP i Agenci AI (DeerSkills Hub):
712
546
  deertools skill review [list|approve|reject|submit] Zarządzanie procesem Peer Review i weryfikacją skilli zespołu.
713
547
  deertools skill update [slug] Aktualizuje zainstalowane skille do najnowszej wersji.
714
548
  deertools skill promote <slug> Zgłasza nominację skilla do oficjalnego katalogu globalnego.
549
+ deertools team [list] Przegląda listę zespołów, liczbę członków i przypisanych skilli.
550
+ deertools team members [zespół] Lista członków zespołów i ich ról (Lider / Członek).
551
+ deertools team add <zespół> <email> [--role lead] Dodaje lub aktualizuje rolę pracownika w zespole.
552
+ deertools team remove <zespół> <email> Usuwa pracownika ze wskazanego zespołu.
553
+ deertools team create <nazwa> [--slug <s>] [--desc] Tworzy nowy zespół w organizacji Deer Design.
554
+
555
+ Integracja MCP i inspekcja API produktów:
715
556
  deertools mcp list Lista dostępnych serwerów MCP w chmurze.
716
557
  deertools mcp config [--json] Generuje konfigurację Remote MCP dla Cursor i Antigravity.
717
558
  deertools mcp install Instaluje serwery MCP w Cursor i Antigravity.
@@ -720,12 +561,15 @@ Integracje MCP i Agenci AI (DeerSkills Hub):
720
561
  deertools endpoints <produkt> [--json] Lista endpointów REST z openapi.json.
721
562
  deertools api <produkt> <METODA> </ścieżka> Wywołuje bezpośrednio API produktu.
722
563
 
723
- Zarządzanie zespołami (DeerSkills Hub Teams):
724
- deertools team [list] Przegląda listę zespołów, liczbę członków i przypisanych skilli.
725
- deertools team members [zespół] Lista członków zespołów i ich ról (Lider / Członek).
726
- deertools team add <zespół> <email> [--role lead] Dodaje lub aktualizuje rolę pracownika w zespole.
727
- deertools team remove <zespół> <email> Usuwa pracownika ze wskazanego zespołu.
728
- deertools team create <nazwa> [--slug <s>] [--desc] Tworzy nowy zespół w organizacji Deer Design.
564
+ BaseLinker Hub & Proxy:
565
+ deertools baselinker auth [token] Zapisuje klucz API BaseLinkera w credentials.json.
566
+ deertools baselinker test Testuje autoryzację i listuje katalogi.
567
+ deertools baselinker serve [--port 3456] Lokalny serwer Web UI z dashboardem i MCP SSE.
568
+ deertools baselinker mcp [--all] Mostek MCP stdio (Cursor / Claude Desktop).
569
+ deertools baselinker sync-schema Pobiera i aktualizuje bazę 204 metod z api.baselinker.com.
570
+ deertools baselinker search <fraza> Przeszukuje bazę metod i schematów parametrów.
571
+ deertools baselinker call <metoda> [json] Wywołuje dowolną metodę BaseLinkera z CLI.
572
+ deertools baselinker catalog <search|get> Operacje katalogowe z poziomu terminala.
729
573
 
730
574
  Narzędzia systemowe:
731
575
  deertools update Sprawdza i podpowiada aktualizację CLI.
@@ -737,8 +581,8 @@ Zmienne środowiskowe: DEERTOOLS_<PRODUKT>_KEY, BASELINKER_TOKEN, DEERTOOLS_HOME
737
581
  `;
738
582
 
739
583
  module.exports = {
740
- cmdLogin, cmdLogout, cmdWhoami, cmdDocs, cmdEndpoints, cmdApi, cmdEstimate, cmdSkill, cmdMcp,
741
- cmdDoctor, cmdAudit, cmdProfile, cmdCompletion, cmdCatalog, cmdDeliveries, cmdSupplier, cmdRendermind,
584
+ cmdLogin, cmdLogout, cmdWhoami, cmdDocs, cmdEndpoints, cmdApi, cmdSkill, cmdMcp,
585
+ cmdDoctor, cmdAudit, cmdProfile, cmdCompletion,
742
586
  cmdBaseLinker, cmdAgent, cmdTeam,
743
587
  HELP, parseQuery,
744
588
  };
package/src/completion.js CHANGED
@@ -19,12 +19,9 @@ _deertools() {
19
19
  'docs:Pobranie dokumentacji produktu dla agentów AI'
20
20
  'endpoints:Lista endpointów REST z openapi.json'
21
21
  'api:Wywołanie API z automatycznym nagłówkiem autoryzacji'
22
- 'catalog:Zarządzanie produktami w Katalogu Connect'
23
- 'deliveries:Zarządzanie dostawami, paczkami i WZ'
24
- 'estimate:Kalkulator kosztorysowania i analiza rzutów'
25
- 'supplier:Zarządzanie rabatami, marżami i promocjami'
26
- 'rendermind:Wizualizacje AI wnętrz i reguły pCon'
27
- 'skill:Instalacja skilla agenta w wykrytych IDE'
22
+ 'skill:Zarządzanie skillami, pakietami i synchronizacją'
23
+ 'team:Zarządzanie zespołami i uprawnieniami w DeerSkills Hub'
24
+ 'agent:Wykrywanie i konfiguracja platform agentów AI'
28
25
  'mcp:Konfiguracja i instalacja serwerów Remote MCP'
29
26
  'doctor:Diagnostyka środowiska, połączeń i uprawnień'
30
27
  'audit:Historia wywołań API i log operacji'
@@ -53,26 +50,6 @@ _deertools() {
53
50
  login|logout|docs|endpoints)
54
51
  _values 'produkty' ${ALL_IDS.map(p => `'${p}'`).join(' ')} 'all'
55
52
  ;;
56
- catalog)
57
- local -a sub=( 'search:Wyszukaj produkty' 'get:Szczegóły produktu po SKU/ID' 'sync:Uruchom synchronizację Baselinker' 'categories:Lista kategorii' 'manufacturers:Lista producentów' )
58
- _describe -t sub 'katalog podkomendy' sub
59
- ;;
60
- deliveries)
61
- local -a sub=( 'list:Lista dostaw' 'get:Pobierz szczegóły dostawy' 'track:Śledź paczkę po numerze' 'wz:Wygeneruj dokument WZ' 'suppliers:Lista dostawców' 'warehouses:Lista magazynów' )
62
- _describe -t sub 'dostawy podkomendy' sub
63
- ;;
64
- supplier)
65
- local -a sub=( 'discounts:Lista rabatów zakupowych' 'markups:Lista narzutów sprzedaży' 'calculate:Kalkulator marży i ceny efektywnej' 'promotions:Lista promocji' )
66
- _describe -t sub 'supplier podkomendy' sub
67
- ;;
68
- estimate)
69
- local -a sub=( 'calculate:Szybka kalkulacja z metrażu' 'plan:Analiza rzutu PDF/obrazu' 'margins:Kalkulator marży referencyjnych' 'list:Lista zapisanych kosztorysów' )
70
- _describe -t sub 'kosztorys podkomendy' sub
71
- ;;
72
- rendermind)
73
- local -a sub=( 'styles:Lista dostępnych stylów wnętrz' 'get:Status zadania renderowania' 'render:Wygeneruj wizualizację ze szkicu' )
74
- _describe -t sub 'rendermind podkomendy' sub
75
- ;;
76
53
  baselinker)
77
54
  local -a sub=( 'auth:Zapisz token X-BLToken' 'test:Testuj połączenie' 'serve:Uruchom lokalny serwer Web UI' 'mcp:Uruchom mostek MCP stdio' 'sync-schema:Synchronizuj bazę metod' 'search:Wyszukaj metodę' 'call:Wywołaj metodę' 'catalog:Operacje katalogowe' )
78
55
  _describe -t sub 'baselinker podkomendy' sub
@@ -101,7 +78,7 @@ _deertools_completions() {
101
78
  cur="\${COMP_WORDS[COMP_CWORD]}"
102
79
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
103
80
 
104
- opts="login logout whoami docs endpoints api catalog deliveries estimate supplier rendermind baselinker skill mcp doctor audit profile completion --help --version --json"
81
+ opts="login logout whoami docs endpoints api baselinker skill team agent mcp doctor audit profile completion --help --version --json"
105
82
 
106
83
  case "$prev" in
107
84
  login|logout|docs|endpoints)
@@ -116,22 +93,6 @@ _deertools_completions() {
116
93
  COMPREPLY=( $(compgen -W "config install serve" -- "$cur") )
117
94
  return 0
118
95
  ;;
119
- catalog)
120
- COMPREPLY=( $(compgen -W "search get sync categories manufacturers" -- "$cur") )
121
- return 0
122
- ;;
123
- deliveries)
124
- COMPREPLY=( $(compgen -W "list get track wz suppliers warehouses" -- "$cur") )
125
- return 0
126
- ;;
127
- estimate)
128
- COMPREPLY=( $(compgen -W "calculate plan margins list" -- "$cur") )
129
- return 0
130
- ;;
131
- supplier)
132
- COMPREPLY=( $(compgen -W "discounts markups calculate promotions" -- "$cur") )
133
- return 0
134
- ;;
135
96
  esac
136
97
 
137
98
  if [[ "\$cur" == -* ]]; then
package/src/mcp-stdio.js CHANGED
@@ -5,7 +5,6 @@ const { ALL_PRODUCTS, getProduct } = require('./registry');
5
5
  const { apiRequest } = require('./http');
6
6
  const { checkSkillUpdates, updateSkills } = require('./skill-commands');
7
7
  const skillsHub = require('./skills-hub');
8
- const { calculateEstimate, getEstimateMargins } = require('./estimate-client');
9
8
 
10
9
  const PROTOCOL_VERSION = '2024-11-05';
11
10
  const SERVER_INFO = { name: 'deertools-stdio-bridge', version: '1.0.5' };
@@ -52,68 +51,6 @@ async function cmdMcpServe(pos, flags) {
52
51
 
53
52
  if (method === 'tools/list') {
54
53
  const tools = [
55
- {
56
- name: 'estimate_calculate',
57
- description: 'Szybkie wyliczenie kosztu wykończenia mieszkania z podanego metrażu w pakietach Smart Life, Modern Life, High Life (kalkulacja po stronie serwera Kosztorys).',
58
- inputSchema: {
59
- type: 'object',
60
- properties: {
61
- area_m2: { type: 'number', description: 'Metraż mieszkania w m²' },
62
- package: { type: 'string', enum: ['smart_life', 'modern_life', 'high_life'], description: 'Pakiet wykończeniowy (opcjonalny — bez podania zwraca 3 pakiety)' },
63
- wet_zones: { type: 'number', description: 'Liczba stref mokrych (np. łazienki, WC)' },
64
- doors: { type: 'number', description: 'Liczba kompletów drzwi wewnętrznych' },
65
- },
66
- required: ['area_m2'],
67
- },
68
- },
69
- {
70
- name: 'estimate_get_margins',
71
- description: 'Pobiera reguły wyliczania marży i strukturę kosztorysową Deer Design dla zadanego metrażu z serwera Kosztorys.',
72
- inputSchema: {
73
- type: 'object',
74
- properties: {
75
- area_m2: { type: 'number', description: 'Metraż mieszkania w m²' },
76
- },
77
- required: ['area_m2'],
78
- },
79
- },
80
- {
81
- name: 'catalog_search_products',
82
- description: 'Wyszukiwanie produktów w Katalogu Connect Deer Design po frazie, kategorii i pakiecie.',
83
- inputSchema: {
84
- type: 'object',
85
- properties: {
86
- query: { type: 'string', description: 'Szukana fraza (nazwa, SKU, producent)' },
87
- package: { type: 'string', enum: ['smart-life', 'modern-life', 'high-life'] },
88
- limit: { type: 'number', default: 10 },
89
- },
90
- required: ['query'],
91
- },
92
- },
93
- {
94
- name: 'deliveries_list',
95
- description: 'Pobieranie listy dostaw z systemu logistycznego Delivery Hub.',
96
- inputSchema: {
97
- type: 'object',
98
- properties: {
99
- status: { type: 'string', description: 'Filtruj po statusie dostawy' },
100
- limit: { type: 'number', default: 20 },
101
- },
102
- },
103
- },
104
- {
105
- name: 'supplier_calculate_price',
106
- description: 'Kalkulator łańcucha marżowego: cena katalogowa -> rabat zakupu -> narzut sprzedaży -> marża zysku.',
107
- inputSchema: {
108
- type: 'object',
109
- properties: {
110
- catalog_price_net: { type: 'number', description: 'Cena katalogowa netto' },
111
- discount_pct: { type: 'number', description: 'Rabat zakupu w % (np. 35)' },
112
- markup_pct: { type: 'number', description: 'Narzut sprzedaży w % (np. 20)' },
113
- },
114
- required: ['catalog_price_net'],
115
- },
116
- },
117
54
  {
118
55
  name: 'deer_check_skill_updates',
119
56
  description: 'Sprawdza czy zainstalowane lokalnie skille AI DeerTools posiadają nowsze wersje w DeerSkills Hub. Zwraca listę skilli z dostępnymi aktualizacjami i ich changelogami.',
@@ -179,97 +116,6 @@ async function cmdMcpServe(pos, flags) {
179
116
  const args = params?.arguments || {};
180
117
 
181
118
  try {
182
- if (toolName === 'estimate_calculate') {
183
- const area = Number(args.area_m2);
184
- const pkg = args.package;
185
- const wetZones = args.wet_zones !== undefined ? Number(args.wet_zones) : undefined;
186
- const doors = args.doors !== undefined ? Number(args.doors) : undefined;
187
-
188
- const data = await calculateEstimate({
189
- area_m2: area,
190
- package: pkg,
191
- wet_zones: wetZones,
192
- doors: doors,
193
- });
194
-
195
- send({
196
- jsonrpc: '2.0',
197
- id,
198
- result: {
199
- content: [{
200
- type: 'text',
201
- text: JSON.stringify(data, null, 2),
202
- }],
203
- structuredContent: data,
204
- },
205
- });
206
- return;
207
- }
208
-
209
- if (toolName === 'estimate_get_margins') {
210
- const area = Number(args.area_m2 || 50);
211
- const data = await getEstimateMargins({ area_m2: area });
212
- send({
213
- jsonrpc: '2.0',
214
- id,
215
- result: {
216
- content: [{
217
- type: 'text',
218
- text: JSON.stringify(data, null, 2),
219
- }],
220
- structuredContent: data,
221
- },
222
- });
223
- return;
224
- }
225
-
226
- if (toolName === 'catalog_search_products') {
227
- const p = getProduct('katalog');
228
- const res = await apiRequest(p, { method: 'GET', path: '/products', query: { search: args.query, program: args.package, limit: args.limit || 10 } });
229
- send({
230
- jsonrpc: '2.0',
231
- id,
232
- result: { content: [{ type: 'text', text: JSON.stringify(res.json?.data || res.json || [], null, 2) }] },
233
- });
234
- return;
235
- }
236
-
237
- if (toolName === 'deliveries_list') {
238
- const p = getProduct('dostawy');
239
- const res = await apiRequest(p, { method: 'GET', path: '/deliveries-api/deliveries', query: { status: args.status, limit: args.limit || 20 } });
240
- send({
241
- jsonrpc: '2.0',
242
- id,
243
- result: { content: [{ type: 'text', text: JSON.stringify(res.json || [], null, 2) }] },
244
- });
245
- return;
246
- }
247
-
248
- if (toolName === 'supplier_calculate_price') {
249
- const cat = Number(args.catalog_price_net);
250
- const disc = Number(args.discount_pct || 30);
251
- const mark = Number(args.markup_pct || 20);
252
- const buy = cat * (1 - disc / 100);
253
- const sell = buy * (1 + mark / 100);
254
- send({
255
- jsonrpc: '2.0',
256
- id,
257
- result: {
258
- content: [{
259
- type: 'text',
260
- text: JSON.stringify({
261
- catalog_price_net: cat,
262
- buy_price_net: Math.round(buy * 100) / 100,
263
- sell_price_net: Math.round(sell * 100) / 100,
264
- margin_net: Math.round((sell - buy) * 100) / 100,
265
- margin_pct: Math.round(((sell - buy) / sell) * 1000) / 10,
266
- }, null, 2),
267
- }],
268
- },
269
- });
270
- return;
271
- }
272
-
273
119
  if (toolName === 'deer_check_skill_updates') {
274
120
  const updates = await checkSkillUpdates();
275
121
  const outdated = updates.filter((u) => u.hasUpdate);
@@ -1,288 +0,0 @@
1
- 'use strict';
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { getProduct } = require('./registry');
5
- const { apiRequest, explain } = require('./http');
6
- const { c, formatTable } = require('./formatters');
7
- const { logAudit } = require('./audit');
8
-
9
- function out(s = '') { process.stdout.write(`${s}\n`); }
10
- function err(s = '') { process.stderr.write(`${s}\n`); }
11
- function fail(msg, code = 1) {
12
- const e = new Error(msg);
13
- e.exitCode = code;
14
- throw e;
15
- }
16
-
17
- // ==================== KATALOG ====================
18
- async function cmdCatalog(pos, flags) {
19
- const action = pos[0] || 'help';
20
- const product = getProduct('katalog');
21
-
22
- if (action === 'search') {
23
- const query = pos.slice(1).join(' ') || flags.q || flags.search || '';
24
- if (!query) fail('Użycie: deertools catalog search <fraza> [--package smart-life|modern-life|high-life] [--limit 20]', 2);
25
-
26
- const q = { search: query };
27
- if (flags.package || flags.program) q.program = flags.package || flags.program;
28
- if (flags.limit) q.limit = flags.limit;
29
- if (flags.category) q.category = flags.category;
30
-
31
- const t0 = Date.now();
32
- const res = await apiRequest(product, { method: 'GET', path: '/products', query: q });
33
- logAudit({ product: 'katalog', method: 'GET', path: '/products', status: res.status, duration_ms: Date.now() - t0 });
34
-
35
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
36
- const data = res.json || {};
37
- const items = data.data || [];
38
-
39
- if (flags.json) { out(JSON.stringify(data, null, 2)); return; }
40
- if (flags.quiet || flags.q) { for (const it of items) out(it.sku || it.id); return; }
41
-
42
- out(`\n${c.bold}Wyniki wyszukiwania w Katalogu (${items.length} z ${data.count || items.length}):${c.reset}`);
43
- const headers = ['SKU', 'Nazwa produktu', 'Kategoria', 'Cena brutto', 'Pakiety'];
44
- const rows = items.map(it => [
45
- it.sku || '',
46
- it.name || '',
47
- it.category_name || (it.category && it.category.name) || '',
48
- it.price != null ? `${it.price.toFixed(2)} zł` : '(ukryta)',
49
- (it.packages || []).join(', ') || ((it.tags || []).filter(t => ['SMART', 'MODERN', 'HIGH'].includes(t)).join(', ')),
50
- ]);
51
- out(formatTable(headers, rows));
52
- return;
53
- }
54
-
55
- if (action === 'get') {
56
- const idOrSku = pos[1];
57
- if (!idOrSku) fail('Użycie: deertools catalog get <id_lub_sku>', 2);
58
- const res = await apiRequest(product, { method: 'GET', path: `/products/${encodeURIComponent(idOrSku)}` });
59
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
60
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
61
- const p = res.json || {};
62
- out(`\n${c.bold}${p.name || 'Produkt'}${c.reset} (SKU: ${p.sku})`);
63
- out(` Cena: ${p.price != null ? `${p.price} zł` : '(brak)'} · Kategoria: ${p.category?.name || p.category_id || '–'}`);
64
- out(` Producent: ${p.manufacturer?.name || p.manufacturer_id || '–'} · EAN: ${p.ean || '–'}`);
65
- out(` Zdjęcia (${(p.images || []).length}): ${(p.images || []).slice(0, 3).join(', ')}`);
66
- return;
67
- }
68
-
69
- if (action === 'categories') {
70
- const res = await apiRequest(product, { method: 'GET', path: '/categories' });
71
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
72
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
73
- const cats = res.json?.data || res.json || [];
74
- out(`\n${c.bold}Kategorie wykończeniowe Deer Design (${cats.length}):${c.reset}`);
75
- for (const cat of cats.slice(0, 30)) {
76
- out(` • ${String(cat.name).padEnd(30)} ${c.dim}(slug: ${cat.slug || cat.id})${c.reset}`);
77
- }
78
- return;
79
- }
80
-
81
- if (action === 'manufacturers') {
82
- const res = await apiRequest(product, { method: 'GET', path: '/manufacturers' });
83
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
84
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
85
- const mfs = res.json?.data || res.json || [];
86
- out(`\n${c.bold}Producenci w Katalogu Connect (${mfs.length}):${c.reset}`);
87
- for (const m of mfs.slice(0, 35)) {
88
- out(` • ${String(m.name).padEnd(28)} ${m.country ? `[${m.country}]` : ''}`);
89
- }
90
- return;
91
- }
92
-
93
- out(`\n${c.bold}Polecenia Katalogu Connect:${c.reset}
94
- deertools catalog search <fraza> [--package modern-life] [--json]
95
- deertools catalog get <id_lub_sku> [--json]
96
- deertools catalog categories [--json]
97
- deertools catalog manufacturers [--json]
98
- `);
99
- }
100
-
101
- // ==================== DOSTAWY ====================
102
- async function cmdDeliveries(pos, flags) {
103
- const action = pos[0] || 'list';
104
- const product = getProduct('dostawy');
105
-
106
- if (action === 'list') {
107
- const q = {};
108
- if (flags.status) q.status = flags.status;
109
- if (flags.limit) q.limit = flags.limit;
110
- if (flags.warehouse) q.warehouse_id = flags.warehouse;
111
-
112
- const t0 = Date.now();
113
- const res = await apiRequest(product, { method: 'GET', path: '/deliveries-api/deliveries', query: q });
114
- logAudit({ product: 'dostawy', method: 'GET', path: '/deliveries-api/deliveries', status: res.status, duration_ms: Date.now() - t0 });
115
-
116
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
117
- const data = res.json || [];
118
- const list = Array.isArray(data) ? data : (data.data || []);
119
-
120
- if (flags.json) { out(JSON.stringify(data, null, 2)); return; }
121
- if (flags.quiet || flags.q) { for (const d of list) out(d.id); return; }
122
-
123
- out(`\n${c.bold}Dostawy DeerGroup (${list.length}):${c.reset}`);
124
- const headers = ['ID', 'Data / Miesiąc', 'Status', 'Dostawca', 'Magazyn'];
125
- const rows = list.map(d => [
126
- d.id ? d.id.slice(0, 8) + '…' : '',
127
- d.month && d.year ? `${d.month}/${d.year}` : (d.delivery_date ? d.delivery_date.slice(0, 10) : '–'),
128
- d.status || 'nowa',
129
- d.supplier?.name || d.supplier_name || '–',
130
- d.warehouse?.name || d.warehouse_name || '–',
131
- ]);
132
- out(formatTable(headers, rows));
133
- return;
134
- }
135
-
136
- if (action === 'track') {
137
- const tracking = pos[1] || flags.number;
138
- if (!tracking) fail('Użycie: deertools deliveries track <numer_listu_lub_paczki>', 2);
139
- const res = await apiRequest(product, { method: 'GET', path: `/deliveries-api/packages/by-tracking/${encodeURIComponent(tracking)}` });
140
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
141
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
142
- const pkg = res.json || {};
143
- out(`\n${c.bold}Status paczki: ${tracking}${c.reset}`);
144
- out(` Kurier: ${pkg.courier || '–'} · Status: ${pkg.status || 'w drodze'}`);
145
- out(` Dostawa ID: ${pkg.delivery_id || '–'}`);
146
- return;
147
- }
148
-
149
- if (action === 'suppliers') {
150
- const res = await apiRequest(product, { method: 'GET', path: '/suppliers-api/suppliers' });
151
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
152
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
153
- const sups = res.json?.data || res.json || [];
154
- out(`\n${c.bold}Dostawcy w Delivery Hub (${sups.length}):${c.reset}`);
155
- for (const s of sups.slice(0, 30)) {
156
- out(` • ${String(s.name).padEnd(30)} ${s.email || ''}`);
157
- }
158
- return;
159
- }
160
-
161
- out(`\n${c.bold}Polecenia systemu Dostaw:${c.reset}
162
- deertools deliveries list [--status active] [--warehouse UUID] [--json]
163
- deertools deliveries track <numer_śledzenia> [--json]
164
- deertools deliveries suppliers [--json]
165
- `);
166
- }
167
-
168
- // ==================== SUPPLIER HUB ====================
169
- async function cmdSupplier(pos, flags) {
170
- const action = pos[0] || 'help';
171
- const product = getProduct('supplier');
172
-
173
- if (action === 'discounts') {
174
- const manufacturer = pos[1] || flags.manufacturer;
175
- const q = {};
176
- if (manufacturer) q.manufacturer = manufacturer;
177
- if (flags.postal_code) q.postal_code = flags.postal_code;
178
-
179
- const res = await apiRequest(product, { method: 'GET', path: '/discounts', query: q });
180
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
181
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
182
- const list = res.json?.data || res.json || [];
183
- out(`\n${c.bold}Rabaty zakupowe dostawców (${list.length}):${c.reset}`);
184
- const headers = ['Producent', 'Dostawca', 'Rabat %', 'Kolekcja / Warunek'];
185
- const rows = list.map(d => [
186
- d.manufacturer_name || d.manufacturer || '–',
187
- d.supplier_name || d.supplier || '–',
188
- d.discount_pct != null ? `${d.discount_pct}%` : `${d.value || 0}%`,
189
- d.collection || d.notes || '–',
190
- ]);
191
- out(formatTable(headers, rows));
192
- return;
193
- }
194
-
195
- if (action === 'markups') {
196
- const location = pos[1] || flags.location;
197
- const q = {};
198
- if (location) q.location = location;
199
-
200
- const res = await apiRequest(product, { method: 'GET', path: '/markups', query: q });
201
- if (!res.ok) fail(explain(res.status, res.json, product), res.status === 401 ? 3 : 1);
202
- if (flags.json) { out(JSON.stringify(res.json, null, 2)); return; }
203
- const list = res.json?.data || res.json || [];
204
- out(`\n${c.bold}Narzuty sprzedaży (${list.length}):${c.reset}`);
205
- const headers = ['Lokalizacja', 'Pakiet', 'Narzut %', 'Kategoria'];
206
- const rows = list.map(m => [
207
- m.location || 'ogólna',
208
- m.package || 'wszystkie',
209
- `${m.markup_pct != null ? m.markup_pct : (m.value || 0)}%`,
210
- m.category || '–',
211
- ]);
212
- out(formatTable(headers, rows));
213
- return;
214
- }
215
-
216
- if (action === 'calculate') {
217
- const catalogPrice = Number(pos[1] || flags.price);
218
- if (!catalogPrice || isNaN(catalogPrice)) fail('Użycie: deertools supplier calculate <cena_katalogowa_netto> [--discount 35] [--markup 25]', 2);
219
-
220
- const discountPct = Number(pos[2] || flags.discount || 30);
221
- const markupPct = Number(pos[3] || flags.markup || 20);
222
-
223
- const buyNet = catalogPrice * (1 - discountPct / 100);
224
- const sellNet = buyNet * (1 + markupPct / 100);
225
- const marginNet = sellNet - buyNet;
226
- const marginPct = (marginNet / sellNet) * 100;
227
-
228
- const result = {
229
- catalogPriceNet: catalogPrice,
230
- discountPct,
231
- buyPriceNet: Math.round(buyNet * 100) / 100,
232
- markupPct,
233
- sellPriceNet: Math.round(sellNet * 100) / 100,
234
- marginNet: Math.round(marginNet * 100) / 100,
235
- marginPct: Math.round(marginPct * 10) / 10,
236
- };
237
-
238
- if (flags.json) { out(JSON.stringify(result, null, 2)); return; }
239
-
240
- out(`\n${c.bold}Symulacja łańcucha marżowego DeerTools:${c.reset}`);
241
- out(` Cena katalogowa producenta: ${c.bold}${catalogPrice.toFixed(2)} zł netto${c.reset}`);
242
- out(` Rabat zakupu dostawcy (${discountPct}%): -${(catalogPrice - buyNet).toFixed(2)} zł`);
243
- out(` --------------------------------------------------`);
244
- out(` Twój koszt zakupu netto: ${c.bold}${buyNet.toFixed(2)} zł netto${c.reset}`);
245
- out(` Narzut sprzedaży (${markupPct}%): +${(sellNet - buyNet).toFixed(2)} zł`);
246
- out(` --------------------------------------------------`);
247
- out(` Sugerowana cena sprzedaży: ${c.green}${c.bold}${sellNet.toFixed(2)} zł netto${c.reset}`);
248
- out(` Zysk z marży: ${c.green}${marginNet.toFixed(2)} zł (${marginPct.toFixed(1)}%)${c.reset}\n`);
249
- return;
250
- }
251
-
252
- out(`\n${c.bold}Polecenia Supplier Hub:${c.reset}
253
- deertools supplier discounts [producent] [--postal_code 00-001] [--json]
254
- deertools supplier markups [lokalizacja] [--json]
255
- deertools supplier calculate <cena_katalogowa> [--discount 30] [--markup 20] [--json]
256
- `);
257
- }
258
-
259
- // ==================== RENDERMIND ====================
260
- async function cmdRendermind(pos, flags) {
261
- const action = pos[0] || 'styles';
262
- const product = getProduct('rendermind');
263
-
264
- if (action === 'styles') {
265
- const res = await apiRequest(product, { method: 'GET', path: '/styles' }).catch(() => ({ ok: false }));
266
- const defaultStyles = [
267
- { name: 'Japandi', palette: 'Jasne drewno, beże, biel, len, ceramika' },
268
- { name: 'Modern Classic', palette: 'Sztukateria, mosiądz, marmur, welur' },
269
- { name: 'Loft / Industrial', palette: 'Cegła, czarna stal, surowy beton' },
270
- { name: 'Skandynawski', palette: 'Biel, jasny dąb, pastele, naturalne światło' },
271
- { name: 'Minimalistyczny', palette: 'Gładkie fronty, monolityczne płaszczyzny, ukryte detale' },
272
- ];
273
- const styles = (res.ok && res.json?.styles) ? res.json.styles : defaultStyles;
274
- if (flags.json) { out(JSON.stringify(styles, null, 2)); return; }
275
- out(`\n${c.bold}Dostępne style wykończeniowe RenderMind:${c.reset}`);
276
- for (const s of styles) {
277
- out(` • ${c.bold}${String(s.name).padEnd(20)}${c.reset} ${s.palette || s.description || ''}`);
278
- }
279
- return;
280
- }
281
-
282
- out(`\n${c.bold}Polecenia RenderMind:${c.reset}
283
- deertools rendermind styles [--json]
284
- deertools rendermind get <job_id> [--json]
285
- `);
286
- }
287
-
288
- module.exports = { cmdCatalog, cmdDeliveries, cmdSupplier, cmdRendermind };
@@ -1,110 +0,0 @@
1
- 'use strict';
2
- const credentials = require('./credentials');
3
- const { getProduct } = require('./registry');
4
-
5
- /**
6
- * Klient API / MCP dla Kosztorysu Deer Design.
7
- * Wszystkie kalkulacje (stawki, progi ryczałtowe do 40 m², dopłaty za strefy mokre,
8
- * dopłaty ze względu na złożoność projektu oraz reguły marżowe) są wykonywane
9
- * wyłącznie po stronie serwera Kosztorysu (Supabase Edge Function: estimate-mcp).
10
- * CLI nie zawiera lokalnie zakodowanych tabel cenowych ani algorytmów marży.
11
- */
12
-
13
- async function callEstimateMcp(toolName, args = {}, options = {}) {
14
- const product = getProduct('estimate');
15
- const found = credentials.getKey(product);
16
- const apiKey = options.apiKey || (found && found.key);
17
-
18
- if (!apiKey && !options.allowAnonymous) {
19
- const err = new Error(
20
- `Nie jesteś zalogowany do Kosztorysu. Kalkulacja wymaga autoryzacji z serwera DeerTools.\nUruchom: deertools login estimate`
21
- );
22
- err.exitCode = 3;
23
- throw err;
24
- }
25
-
26
- const endpoint = options.endpoint || product.mcpEndpoint || `${product.fallbackApiBase}/estimate-mcp`;
27
- const fetchFn = options.fetchImpl || globalThis.fetch;
28
-
29
- const payload = {
30
- jsonrpc: '2.0',
31
- id: Date.now(),
32
- method: 'tools/call',
33
- params: {
34
- name: toolName,
35
- arguments: args,
36
- },
37
- };
38
-
39
- const headers = {
40
- 'Content-Type': 'application/json',
41
- Accept: 'application/json',
42
- };
43
- if (apiKey) {
44
- headers['x-api-key'] = apiKey;
45
- }
46
-
47
- let res;
48
- try {
49
- res = await fetchFn(endpoint, {
50
- method: 'POST',
51
- headers,
52
- body: JSON.stringify(payload),
53
- });
54
- } catch (netErr) {
55
- const err = new Error(`Błąd połączenia z serwerem Kosztorysu (${endpoint}): ${netErr.message}`);
56
- err.exitCode = 4;
57
- throw err;
58
- }
59
-
60
- if (!res.ok) {
61
- let errBody = '';
62
- try {
63
- errBody = await res.text();
64
- } catch (_) {}
65
- if (res.status === 401) {
66
- const err = new Error(
67
- `Brak dostępu do Kosztorysu (HTTP 401). Klucz wygasł lub jest niepoprawny.\nUruchom ponowne logowanie: deertools login estimate`
68
- );
69
- err.exitCode = 3;
70
- throw err;
71
- }
72
- const err = new Error(`Serwer Kosztorysu zwrócił błąd HTTP ${res.status}: ${errBody || res.statusText}`);
73
- err.exitCode = 4;
74
- throw err;
75
- }
76
-
77
- const json = await res.json();
78
- if (json.error) {
79
- const err = new Error(`Błąd kalkulacji Kosztorysu: ${json.error.message || JSON.stringify(json.error)}`);
80
- err.exitCode = 4;
81
- throw err;
82
- }
83
-
84
- if (json.result?.structuredContent) {
85
- return json.result.structuredContent;
86
- }
87
- if (json.result?.content?.[0]?.text) {
88
- try {
89
- return JSON.parse(json.result.content[0].text);
90
- } catch (_) {
91
- return json.result.content[0].text;
92
- }
93
- }
94
-
95
- return json.result || {};
96
- }
97
-
98
- async function calculateEstimate(params, options = {}) {
99
- return callEstimateMcp('estimate_calculate_simple', params, options);
100
- }
101
-
102
- async function getEstimateMargins(params, options = {}) {
103
- return callEstimateMcp('estimate_get_margins', params, options);
104
- }
105
-
106
- module.exports = {
107
- callEstimateMcp,
108
- calculateEstimate,
109
- getEstimateMargins,
110
- };