@deerdesign/deertools 1.2.36 → 1.3.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deerdesign/deertools",
3
- "version": "1.2.36",
3
+ "version": "1.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -117,33 +117,54 @@ async function handleMcpMessage(msg, client = null, mode = 'catalog') {
117
117
 
118
118
  function runBaseLinkerMcpServer(options = {}) {
119
119
  const { mode = 'catalog' } = options;
120
- const rl = readline.createInterface({
121
- input: process.stdin,
122
- output: process.stdout,
123
- terminal: false
124
- });
120
+ return new Promise((resolve) => {
121
+ let pendingCount = 0;
122
+ let isClosed = false;
123
+ let closeResolve = null;
125
124
 
126
- rl.on('line', async line => {
127
- const trimmed = line.trim();
128
- if (!trimmed) return;
129
- try {
130
- const msg = JSON.parse(trimmed);
131
- const res = await handleMcpMessage(msg, null, mode);
132
- if (res) {
133
- process.stdout.write(JSON.stringify(res) + '\n');
125
+ const rl = readline.createInterface({
126
+ input: process.stdin,
127
+ output: process.stdout,
128
+ terminal: false
129
+ });
130
+
131
+ rl.on('line', async line => {
132
+ const trimmed = line.trim();
133
+ if (!trimmed) return;
134
+ pendingCount++;
135
+ try {
136
+ const msg = JSON.parse(trimmed);
137
+ const res = await handleMcpMessage(msg, null, mode);
138
+ if (res) {
139
+ process.stdout.write(JSON.stringify(res) + '\n');
140
+ }
141
+ } catch (err) {
142
+ const errRes = {
143
+ jsonrpc: '2.0',
144
+ id: null,
145
+ error: { code: -32700, message: 'Parse error: ' + err.message }
146
+ };
147
+ process.stdout.write(JSON.stringify(errRes) + '\n');
148
+ } finally {
149
+ pendingCount--;
150
+ if (isClosed && pendingCount === 0 && closeResolve) {
151
+ closeResolve();
152
+ }
134
153
  }
135
- } catch (err) {
136
- const errRes = {
137
- jsonrpc: '2.0',
138
- id: null,
139
- error: { code: -32700, message: 'Parse error: ' + err.message }
140
- };
141
- process.stdout.write(JSON.stringify(errRes) + '\n');
142
- }
143
- });
154
+ });
144
155
 
145
- process.on('SIGINT', () => process.exit(0));
146
- process.on('SIGTERM', () => process.exit(0));
156
+ rl.on('close', () => {
157
+ isClosed = true;
158
+ if (pendingCount === 0) {
159
+ resolve();
160
+ } else {
161
+ closeResolve = resolve;
162
+ }
163
+ });
164
+
165
+ process.on('SIGINT', () => { rl.close(); resolve(); });
166
+ process.on('SIGTERM', () => { rl.close(); resolve(); });
167
+ });
147
168
  }
148
169
 
149
170
  module.exports = {
@@ -79,8 +79,7 @@ Zgodnie ze standardem architektonicznym DeerTools CLI:
79
79
  }
80
80
 
81
81
  if (sub === 'mcp') {
82
- runBaseLinkerMcpServer({ mode: flags.all ? 'all' : 'catalog' });
83
- return;
82
+ return await runBaseLinkerMcpServer({ mode: flags.all ? 'all' : 'catalog' });
84
83
  }
85
84
 
86
85
  if (sub === 'sync-schema') {
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+ const { runCatalogMcpServer } = require('./mcp-server');
3
+
4
+ async function cmdCatalog(pos, flags) {
5
+ const sub = pos[0] || 'help';
6
+
7
+ if (sub === 'mcp') {
8
+ return await runCatalogMcpServer();
9
+ }
10
+
11
+ console.log(`Użycie deertools catalog:
12
+ deertools catalog mcp - Uruchamia serwer MCP Katalogu Connect (stdio)
13
+ `);
14
+ return 0;
15
+ }
16
+
17
+ module.exports = {
18
+ cmdCatalog
19
+ };
@@ -0,0 +1,178 @@
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-catalog-mcp', version: '1.3.0' };
8
+
9
+ const FALLBACK_CATALOG_TOOLS = [
10
+ {
11
+ name: 'catalog_search_products',
12
+ description: 'Wyszukuje produkty wykończeniowe w Katalogu Connect (~17 tys. pozycji) z filtrowaniem po pakiecie (Smart Life, Modern Life, High Life) i kategorii.',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ query: { type: 'string' },
17
+ package: { type: 'string' },
18
+ category: { type: 'string' },
19
+ limit: { type: 'number' }
20
+ }
21
+ }
22
+ },
23
+ {
24
+ name: 'catalog_get_product',
25
+ description: 'Pobiera szczegóły pojedynczego produktu z Katalogu Connect po ID lub SKU.',
26
+ inputSchema: {
27
+ type: 'object',
28
+ properties: {
29
+ id: { type: 'string' },
30
+ sku: { type: 'string' }
31
+ }
32
+ }
33
+ },
34
+ {
35
+ name: 'catalog_list_categories',
36
+ description: 'Pobiera drzewo kategorii Katalogu Connect.',
37
+ inputSchema: { type: 'object', properties: {} }
38
+ }
39
+ ];
40
+
41
+ function getCatalogKey() {
42
+ const p = getProduct('catalog');
43
+ const cred = credentials.getKey(p);
44
+ if (cred && cred.key) return cred.key;
45
+ if (process.env.CATALOG_API_KEY) return process.env.CATALOG_API_KEY;
46
+ if (process.env.DEERTOOLS_CATALOG_KEY) return process.env.DEERTOOLS_CATALOG_KEY;
47
+ return null;
48
+ }
49
+
50
+ function ensureCatalogAuth() {
51
+ const key = getCatalogKey();
52
+ if (!key) {
53
+ throw new Error('Brak klucza API do Katalogu Connect. Zaloguj się poleceniem: deertools login catalog');
54
+ }
55
+ return key;
56
+ }
57
+
58
+ async function forwardToCatalogMcp(req) {
59
+ const apiKey = ensureCatalogAuth();
60
+ const p = getProduct('catalog');
61
+ const endpoint = p.mcpEndpoint || 'https://ftzaobeixglnssemugjf.supabase.co/functions/v1/catalog-mcp';
62
+
63
+ const res = await fetch(endpoint, {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ 'x-api-key': apiKey,
68
+ 'Authorization': `Bearer ${apiKey}`
69
+ },
70
+ body: JSON.stringify(req)
71
+ });
72
+
73
+ const text = await res.text();
74
+ let json = null;
75
+ try { json = JSON.parse(text); } catch (_) {}
76
+
77
+ if (!res.ok) {
78
+ const errorMsg = (json && (json.message || json.error)) || text;
79
+ return {
80
+ jsonrpc: '2.0',
81
+ id: req.id || null,
82
+ error: { code: -32000, message: `Katalog Connect MCP (${res.status}): ${errorMsg}` }
83
+ };
84
+ }
85
+
86
+ return json !== null ? json : { jsonrpc: '2.0', id: req.id, result: { content: [{ type: 'text', text }] } };
87
+ }
88
+
89
+ async function handleMcpMessage(msg) {
90
+ if (!msg || typeof msg !== 'object') return null;
91
+ const { id, method } = msg;
92
+
93
+ if (method === 'initialize') {
94
+ return {
95
+ jsonrpc: '2.0',
96
+ id,
97
+ result: {
98
+ protocolVersion: PROTOCOL_VERSION,
99
+ capabilities: { tools: {} },
100
+ serverInfo: SERVER_INFO
101
+ }
102
+ };
103
+ }
104
+
105
+ if (method === 'notifications/initialized') {
106
+ return null;
107
+ }
108
+
109
+ if (method === 'tools/list') {
110
+ if (!getCatalogKey()) {
111
+ return { jsonrpc: '2.0', id, result: { tools: FALLBACK_CATALOG_TOOLS } };
112
+ }
113
+ try {
114
+ const upstream = await forwardToCatalogMcp(msg);
115
+ if (upstream && upstream.result && Array.isArray(upstream.result.tools)) {
116
+ return upstream;
117
+ }
118
+ } catch (_) {}
119
+ return { jsonrpc: '2.0', id, result: { tools: FALLBACK_CATALOG_TOOLS } };
120
+ }
121
+
122
+ // Forward tools/call to upstream
123
+ return await forwardToCatalogMcp(msg);
124
+ }
125
+
126
+ function runCatalogMcpServer() {
127
+ return new Promise((resolve) => {
128
+ let pendingCount = 0;
129
+ let isClosed = false;
130
+ let closeResolve = null;
131
+
132
+ const rl = readline.createInterface({
133
+ input: process.stdin,
134
+ output: process.stdout,
135
+ terminal: false
136
+ });
137
+
138
+ rl.on('line', async (line) => {
139
+ const trimmed = line.trim();
140
+ if (!trimmed) return;
141
+ pendingCount++;
142
+ try {
143
+ const msg = JSON.parse(trimmed);
144
+ const resp = await handleMcpMessage(msg);
145
+ if (resp) {
146
+ process.stdout.write(JSON.stringify(resp) + '\n');
147
+ }
148
+ } catch (e) {
149
+ process.stdout.write(JSON.stringify({
150
+ jsonrpc: '2.0',
151
+ id: null,
152
+ error: { code: -32700, message: `Parse error: ${e.message}` }
153
+ }) + '\n');
154
+ } finally {
155
+ pendingCount--;
156
+ if (isClosed && pendingCount === 0 && closeResolve) {
157
+ closeResolve();
158
+ }
159
+ }
160
+ });
161
+
162
+ rl.on('close', () => {
163
+ isClosed = true;
164
+ if (pendingCount === 0) {
165
+ resolve();
166
+ } else {
167
+ closeResolve = resolve;
168
+ }
169
+ });
170
+ process.on('SIGINT', () => { rl.close(); resolve(); });
171
+ process.on('SIGTERM', () => { rl.close(); resolve(); });
172
+ });
173
+ }
174
+
175
+ module.exports = {
176
+ handleMcpMessage,
177
+ runCatalogMcpServer
178
+ };
package/src/cli.js CHANGED
@@ -54,6 +54,12 @@ async function main(argv) {
54
54
  baselinker: c.cmdBaseLinker,
55
55
  rendermind: c.cmdRenderMind,
56
56
  supplier: c.cmdSupplier,
57
+ estimate: c.cmdEstimate,
58
+ kosztorys: c.cmdEstimate,
59
+ catalog: c.cmdCatalog,
60
+ katalog: c.cmdCatalog,
61
+ deliveries: c.cmdDeliveries,
62
+ dostawy: c.cmdDeliveries,
57
63
  update: require('./updater').cmdUpdate,
58
64
  // Bezpiecznik architektoniczny: blokada bezpośrednich wywołań REST API z CLI
59
65
  api: () => c.cmdBlockedDomain('api'),
@@ -72,7 +78,8 @@ async function main(argv) {
72
78
 
73
79
  const cmd = pos.shift() || 'help';
74
80
 
75
- const shouldCheck = !flags.json && cmd !== 'update' && process.env.NODE_ENV !== 'test';
81
+ const isMcp = cmd === 'mcp' || pos.includes('mcp') || pos.includes('serve');
82
+ const shouldCheck = !flags.json && !isMcp && cmd !== 'update' && process.env.NODE_ENV !== 'test';
76
83
  const updatePromise = shouldCheck ? require('./updater').checkUpdate().catch(() => null) : null;
77
84
 
78
85
  const finish = async (code = 0) => {
package/src/commands.js CHANGED
@@ -299,29 +299,47 @@ function resolveDeertoolsBin() {
299
299
  return 'deertools';
300
300
  }
301
301
 
302
- function buildMcpServersConfig(products, useFullBinPath = false) {
302
+ function buildMcpServersConfig(products, useFullBinPath = false, { remote = false } = {}) {
303
303
  const servers = {};
304
304
  const binCmd = useFullBinPath ? resolveDeertoolsBin() : 'deertools';
305
305
 
306
- // Produkty z natywnym Remote MCP w chmurze obsługującym x-api-key
307
- const remoteApiKeyProducts = new Set(['estimate', 'catalog', 'deliveries', 'supplier']);
308
-
309
- for (const p of products) {
310
- if (remoteApiKeyProducts.has(p.id) && p.mcpEndpoint) {
311
- const cred = credentials.getKey(p);
312
- const key = cred ? cred.key : `${p.keyPrefix}<twój_klucz_api>`;
313
- const srvKey = `deer-${p.id}`;
314
- servers[srvKey] = {
315
- url: p.mcpEndpoint,
316
- headers: {
317
- 'x-api-key': key,
318
- 'Authorization': `Bearer ${key}`,
319
- },
320
- };
306
+ if (remote) {
307
+ // Opcjonalny tryb Remote HTTP (--remote) z nagłówkami x-api-key
308
+ const remoteApiKeyProducts = new Set(['estimate', 'catalog', 'deliveries', 'supplier']);
309
+ for (const p of products) {
310
+ if (remoteApiKeyProducts.has(p.id) && p.mcpEndpoint) {
311
+ const cred = credentials.getKey(p);
312
+ const key = cred ? cred.key : `${p.keyPrefix}<twój_klucz_api>`;
313
+ const srvKey = `deer-${p.id}`;
314
+ servers[srvKey] = {
315
+ url: p.mcpEndpoint,
316
+ headers: {
317
+ 'x-api-key': key,
318
+ 'Authorization': `Bearer ${key}`,
319
+ },
320
+ };
321
+ }
321
322
  }
323
+ } else {
324
+ // Domyślny tryb Local stdio MCP dla wszystkich systemów DeerTools (działający lokalnie w imieniu użytkownika)
325
+ servers['deer-estimate'] = {
326
+ command: binCmd,
327
+ args: ['estimate', 'mcp'],
328
+ };
329
+ servers['deer-catalog'] = {
330
+ command: binCmd,
331
+ args: ['catalog', 'mcp'],
332
+ };
333
+ servers['deer-deliveries'] = {
334
+ command: binCmd,
335
+ args: ['deliveries', 'mcp'],
336
+ };
337
+ servers['deer-supplier'] = {
338
+ command: binCmd,
339
+ args: ['supplier', 'mcp'],
340
+ };
322
341
  }
323
342
 
324
- // Serwery MCP stdio (silniki z obsługą plików lokalnych CAD/Excel lub długich procesów AI)
325
343
  servers['deer-rendermind'] = {
326
344
  command: binCmd,
327
345
  args: ['rendermind', 'mcp'],
@@ -338,45 +356,9 @@ function buildMcpServersConfig(products, useFullBinPath = false) {
338
356
  return servers;
339
357
  }
340
358
 
341
- async function cmdMcp(pos, flags) {
342
- const sub = pos[0] || 'config';
343
- const products = resolveProducts(pos[1]);
344
-
345
- if (sub === 'list') {
346
- const servers = buildMcpServersConfig(products, false);
347
- if (flags.json) {
348
- out(JSON.stringify(servers, null, 2));
349
- return;
350
- }
351
- out('\n=== Zarejestrowane Serwery MCP Ekosystemu DeerTools ===\n');
352
- for (const [name, cfg] of Object.entries(servers)) {
353
- if (cfg.url) {
354
- out(` • ${name.padEnd(18)} [Remote HTTP] ${cfg.url}`);
355
- } else {
356
- out(` • ${name.padEnd(18)} [Local stdio] ${cfg.command} ${(cfg.args || []).join(' ')}`);
357
- }
358
- }
359
- out('\nAby zainstalować w Cursor i Antigravity: deertools mcp install\n');
360
- return;
361
- }
362
-
363
- if (sub === 'config') {
364
- const servers = buildMcpServersConfig(products, false);
365
- const config = { mcpServers: servers };
366
- if (flags.json) {
367
- out(JSON.stringify(config, null, 2));
368
- } else {
369
- out('\nKonfiguracja MCP dla DeerTools (Cursor IDE, Claude Desktop, Antigravity):');
370
- out(JSON.stringify(config, null, 2));
371
- out('\nAby zainstalować automatycznie w Cursorze i Antigravity, uruchom:');
372
- out(' deertools mcp install\n');
373
- }
374
- return;
375
- }
376
-
377
- function installMcpServers(customProducts = null, silent = false) {
359
+ function installMcpServers(customProducts = null, silent = false, { remote = false } = {}) {
378
360
  const prods = customProducts || resolveProducts('all');
379
- const servers = buildMcpServersConfig(prods, true);
361
+ const servers = buildMcpServersConfig(prods, true, { remote });
380
362
  let touched = 0;
381
363
 
382
364
  // 1. Cursor global (~/.cursor/mcp.json)
@@ -447,7 +429,24 @@ function installMcpServers(customProducts = null, silent = false) {
447
429
  }
448
430
  }
449
431
 
450
- // 5. Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)
432
+ // 5. Antigravity AppData (~/.gemini/antigravity/mcp_config.json)
433
+ const antigravityAppData = path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json');
434
+ if (fs.existsSync(path.dirname(antigravityAppData))) {
435
+ try {
436
+ let cur = { mcpServers: {} };
437
+ if (fs.existsSync(antigravityAppData)) {
438
+ try { cur = JSON.parse(fs.readFileSync(antigravityAppData, 'utf8')); } catch (_) {}
439
+ }
440
+ cur.mcpServers = Object.assign({}, cur.mcpServers, servers);
441
+ fs.writeFileSync(antigravityAppData, JSON.stringify(cur, null, 2));
442
+ if (!silent) out(`✓ Antigravity AppData: ${antigravityAppData}`);
443
+ touched++;
444
+ } catch (e) {
445
+ if (!silent) err(`✗ Antigravity AppData (${antigravityAppData}): ${e.message}`);
446
+ }
447
+ }
448
+
449
+ // 6. Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)
451
450
  const claudeDesktop = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
452
451
  if (fs.existsSync(path.dirname(claudeDesktop))) {
453
452
  try {
@@ -473,7 +472,7 @@ function installMcpServers(customProducts = null, silent = false) {
473
472
  }
474
473
  }
475
474
 
476
- // 6. Claude Code CLI (~/.claude.json)
475
+ // 7. Claude Code CLI (~/.claude.json)
477
476
  const claudeCli = path.join(os.homedir(), '.claude.json');
478
477
  if (fs.existsSync(claudeCli)) {
479
478
  try {
@@ -497,7 +496,7 @@ function installMcpServers(customProducts = null, silent = false) {
497
496
  }
498
497
  }
499
498
 
500
- // 7. Workspace .mcp.json (Claude Code / Cowork / Standard MCP workspace)
499
+ // 8. Workspace .mcp.json (Claude Code / Cowork / Standard MCP workspace)
501
500
  const workspaceMcp = path.join(process.cwd(), '.mcp.json');
502
501
  try {
503
502
  let cur = {};
@@ -524,8 +523,45 @@ function installMcpServers(customProducts = null, silent = false) {
524
523
  return touched;
525
524
  }
526
525
 
526
+ async function cmdMcp(pos, flags) {
527
+ const sub = pos[0] || 'config';
528
+ const products = resolveProducts(pos[1]);
529
+ const remote = Boolean(flags.remote);
530
+
531
+ if (sub === 'list') {
532
+ const servers = buildMcpServersConfig(products, false, { remote });
533
+ if (flags.json) {
534
+ out(JSON.stringify(servers, null, 2));
535
+ return;
536
+ }
537
+ out('\n=== Zarejestrowane Serwery MCP Ekosystemu DeerTools ===\n');
538
+ for (const [name, cfg] of Object.entries(servers)) {
539
+ if (cfg.url) {
540
+ out(` • ${name.padEnd(18)} [Remote HTTP] ${cfg.url}`);
541
+ } else {
542
+ out(` • ${name.padEnd(18)} [Local stdio] ${cfg.command} ${(cfg.args || []).join(' ')}`);
543
+ }
544
+ }
545
+ out('\nAby zainstalować w Cursor i Antigravity: deertools mcp install\n');
546
+ return;
547
+ }
548
+
549
+ if (sub === 'config') {
550
+ const servers = buildMcpServersConfig(products, false, { remote });
551
+ const config = { mcpServers: servers };
552
+ if (flags.json) {
553
+ out(JSON.stringify(config, null, 2));
554
+ } else {
555
+ out('\nKonfiguracja MCP dla DeerTools (Cursor IDE, Claude Desktop, Antigravity):');
556
+ out(JSON.stringify(config, null, 2));
557
+ out('\nAby zainstalować automatycznie w Cursorze i Antigravity, uruchom:');
558
+ out(' deertools mcp install\n');
559
+ }
560
+ return;
561
+ }
562
+
527
563
  if (sub === 'install') {
528
- const touched = installMcpServers(products, false);
564
+ const touched = installMcpServers(products, false, { remote });
529
565
  if (!touched) fail('Nie znaleziono środowiska agenta do instalacji MCP.', 4);
530
566
  out(`\nPomyślnie skonfigurowano wszystkie 7 serwerów MCP dla produktów DeerTools!`);
531
567
  return;
@@ -636,6 +672,9 @@ const { cmdCompletion } = require('./completion');
636
672
  const { cmdBaseLinker } = require('./baselinker-commands');
637
673
  const { cmdRenderMind } = require('./rendermind/cli-commands');
638
674
  const { cmdSupplier } = require('./supplier/cli-commands');
675
+ const { cmdEstimate } = require('./estimate/cli-commands');
676
+ const { cmdCatalog } = require('./catalog/cli-commands');
677
+ const { cmdDeliveries } = require('./deliveries/cli-commands');
639
678
 
640
679
  const HELP = `
641
680
  DeerTools CLI — Identity, MCP & Skills Hub Manager dla ekosystemu Deer Design
@@ -685,7 +724,8 @@ Zasada architektoniczna:
685
724
  module.exports = {
686
725
  cmdLogin, cmdLogout, cmdWhoami, cmdSkill, cmdMcp,
687
726
  cmdDoctor, cmdAudit, cmdProfile, cmdCompletion,
688
- cmdBaseLinker, cmdRenderMind, cmdSupplier,
727
+ cmdBaseLinker, cmdRenderMind, cmdSupplier, cmdEstimate,
728
+ cmdCatalog, cmdDeliveries,
689
729
  cmdAgent, cmdTeam, cmdBlockedDomain,
690
730
  HELP, parseQuery,
691
731
  };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+ const { runDeliveriesMcpServer } = require('./mcp-server');
3
+
4
+ async function cmdDeliveries(pos, flags) {
5
+ const sub = pos[0] || 'help';
6
+
7
+ if (sub === 'mcp') {
8
+ return await runDeliveriesMcpServer();
9
+ }
10
+
11
+ console.log(`Użycie deertools deliveries:
12
+ deertools deliveries mcp - Uruchamia serwer MCP Dostaw (stdio)
13
+ `);
14
+ return 0;
15
+ }
16
+
17
+ module.exports = {
18
+ cmdDeliveries
19
+ };