@deerdesign/deertools 1.2.11 → 1.2.13
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 +1 -1
- package/src/commands.js +9 -2
- package/src/login.js +13 -3
- package/src/mcp-stdio.js +22 -1
- package/src/skill-commands.js +61 -0
package/package.json
CHANGED
package/src/commands.js
CHANGED
|
@@ -57,7 +57,14 @@ async function cmdLogin(pos, flags) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const products = resolveProducts(pos[0]);
|
|
60
|
-
const
|
|
60
|
+
const requestedScope = flags.admin
|
|
61
|
+
? 'admin'
|
|
62
|
+
: (flags.ro || flags['read-only'])
|
|
63
|
+
? 'read'
|
|
64
|
+
: (flags.rw || flags.write)
|
|
65
|
+
? 'read write'
|
|
66
|
+
: (flags.scope || 'read');
|
|
67
|
+
const scope = normalizeScope(requestedScope);
|
|
61
68
|
let failures = 0;
|
|
62
69
|
for (const product of products) {
|
|
63
70
|
out(`\n=== ${product.name} ===`);
|
|
@@ -514,7 +521,7 @@ DeerTools CLI — ekosystem narzędzi dla architektów i agentów AI Deer Design
|
|
|
514
521
|
Produkty: ${ALL_IDS.join(', ')}
|
|
515
522
|
|
|
516
523
|
Dostęp i autoryzacja:
|
|
517
|
-
deertools login <produkt|all> [--
|
|
524
|
+
deertools login <produkt|all> [--ro|--rw|--admin] Logowanie OAuth (RO: tylko odczyt, RW: zapis, Admin).
|
|
518
525
|
deertools logout [produkt|all] Usuwa lokalny klucz.
|
|
519
526
|
deertools whoami [produkt|all] [--json] Status logowania, zakres, ważność.
|
|
520
527
|
deertools profile [list|use <nazwa>|current] Przełączanie profili poświadczeń.
|
package/src/login.js
CHANGED
|
@@ -7,7 +7,7 @@ const credentials = require('./credentials');
|
|
|
7
7
|
|
|
8
8
|
const CLIENT_ID = 'deertools-cli';
|
|
9
9
|
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
10
|
-
const VALID_SCOPES = ['read', 'write'];
|
|
10
|
+
const VALID_SCOPES = ['read', 'write', 'admin'];
|
|
11
11
|
|
|
12
12
|
const b64url = (buf) => Buffer.from(buf).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
13
13
|
|
|
@@ -21,14 +21,24 @@ function escapeHtml(s) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function normalizeScope(input) {
|
|
24
|
-
|
|
24
|
+
if (!input) return 'read';
|
|
25
|
+
const raw = String(input).toLowerCase().trim();
|
|
26
|
+
if (raw === 'ro' || raw === 'read-only') return 'read';
|
|
27
|
+
if (raw === 'rw' || raw === 'read-write') return 'read write';
|
|
28
|
+
if (raw === 'admin') return 'read write admin';
|
|
29
|
+
|
|
30
|
+
const list = raw.split(/[\s,]+/).filter(Boolean);
|
|
25
31
|
const bad = list.filter((s) => !VALID_SCOPES.includes(s));
|
|
26
32
|
if (bad.length) {
|
|
27
|
-
const err = new Error(`Nieznany zakres: ${bad.join(', ')}. Dozwolone: ${VALID_SCOPES.join(', ')}.`);
|
|
33
|
+
const err = new Error(`Nieznany zakres: ${bad.join(', ')}. Dozwolone: ${VALID_SCOPES.join(', ')} (lub aliasy: ro, rw, admin).`);
|
|
28
34
|
err.exitCode = 2;
|
|
29
35
|
throw err;
|
|
30
36
|
}
|
|
31
37
|
const set = new Set(list.length ? list : ['read']);
|
|
38
|
+
if (set.has('admin')) {
|
|
39
|
+
set.add('write');
|
|
40
|
+
set.add('read');
|
|
41
|
+
}
|
|
32
42
|
if (set.has('write')) set.add('read'); // zapis zakłada odczyt
|
|
33
43
|
return VALID_SCOPES.filter((s) => set.has(s)).join(' ');
|
|
34
44
|
}
|
package/src/mcp-stdio.js
CHANGED
|
@@ -103,12 +103,14 @@ async function cmdMcpServe(pos, flags) {
|
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
name: 'deer_update_skills',
|
|
106
|
-
description: 'Autonomiczna aktualizacja skilli Deer Design — pobiera najnowsze wersje paczek z DeerSkills Hub i odświeża plik blokady.',
|
|
106
|
+
description: 'Autonomiczna aktualizacja skilli Deer Design — pobiera najnowsze wersje paczek z DeerSkills Hub i odświeża plik blokady. WYMAGA POTWIERDZENIA CZŁOWIEKA (Human-in-the-Loop) — parametr confirmed=true po zatwierdzeniu przez użytkownika.',
|
|
107
107
|
inputSchema: {
|
|
108
108
|
type: 'object',
|
|
109
109
|
properties: {
|
|
110
110
|
slug: { type: 'string', description: 'Opcjonalny slug konkretnego skilla do zaktualizowania (np. deer-kosztorys). Domyślnie aktualizuje wszystkie przestarzałe skille.' },
|
|
111
|
+
confirmed: { type: 'boolean', description: 'Wymagany bezpiecznik HITL: true tylko po uprzednim zapytaniu człowieka o zgodę w ankiecie (ask_question).' },
|
|
111
112
|
},
|
|
113
|
+
required: ['confirmed'],
|
|
112
114
|
},
|
|
113
115
|
},
|
|
114
116
|
];
|
|
@@ -227,6 +229,25 @@ async function cmdMcpServe(pos, flags) {
|
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
if (toolName === 'deer_update_skills') {
|
|
232
|
+
if (args.confirmed !== true) {
|
|
233
|
+
send({
|
|
234
|
+
jsonrpc: '2.0',
|
|
235
|
+
id,
|
|
236
|
+
result: {
|
|
237
|
+
content: [{
|
|
238
|
+
type: 'text',
|
|
239
|
+
text: JSON.stringify({
|
|
240
|
+
status: 'requires_human_confirmation',
|
|
241
|
+
message: 'BEZPIECZNIK HITL (Human-in-the-Loop): Operacja aktualizacji skilli modyfikuje pliki w środowisku użytkownika. Agent AI NIE MOŻE sam podjąć tej decyzji ani pominąć człowieka.',
|
|
242
|
+
instruction_for_agent: 'Wyświetl użytkownikowi interaktywną ankietę (np. narzędziem ask_question w Antigravity) pytając o zgodę na aktualizację: „Czy zatwierdzasz aktualizację skilli: ' + (args.slug || 'wszystkie przestarzałe skille') + '?”. Dopiero po kliknięciu potwierdzenia przez człowieka wywołaj deer_update_skills ponownie z argumentem confirmed: true.',
|
|
243
|
+
target_skills: args.slug || 'all',
|
|
244
|
+
}, null, 2),
|
|
245
|
+
}],
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
230
251
|
const res = await updateSkills(args.slug || null);
|
|
231
252
|
send({
|
|
232
253
|
jsonrpc: '2.0',
|
package/src/skill-commands.js
CHANGED
|
@@ -156,6 +156,67 @@ async function cmdSkillInstall(target, flags, out, err) {
|
|
|
156
156
|
};
|
|
157
157
|
writeLock(lock);
|
|
158
158
|
out(`✓ Zaktualizowano ${LOCK_FILE} (wersja ${versionData.version}).`);
|
|
159
|
+
|
|
160
|
+
// Podsumowanie możliwości skilla dla użytkownika i Agenta AI
|
|
161
|
+
printSkillSummary(skill, versionData, out);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function extractCapabilities(content, summary = '') {
|
|
165
|
+
const text = String(content || '');
|
|
166
|
+
const lines = text.split('\n');
|
|
167
|
+
const bullets = [];
|
|
168
|
+
let inWhen = false;
|
|
169
|
+
|
|
170
|
+
for (let i = 0; i < lines.length; i++) {
|
|
171
|
+
const line = lines[i].trim();
|
|
172
|
+
if (/^##\s+(Kiedy używać|Możliwości|Zastosowanie|Co potrafi)/i.test(line)) {
|
|
173
|
+
inWhen = true;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (inWhen) {
|
|
177
|
+
if (/^##\s+/.test(line)) break;
|
|
178
|
+
if (line.startsWith('- ') || line.startsWith('* ') || /^\d+\.\s+/.test(line)) {
|
|
179
|
+
const clean = line.replace(/^[-*]\s+|\d+\.\s+/, '').replace(/\.$/, '').trim();
|
|
180
|
+
if (clean.length > 5 && !clean.toLowerCase().startsWith('nie używaj')) {
|
|
181
|
+
bullets.push(clean);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const desc = summary || (text.match(/description:\s*["']?([^"'\n]+)/i)?.[1]) || '';
|
|
188
|
+
return { desc, bullets: bullets.slice(0, 4) };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function printSkillSummary(skill, versionData, out) {
|
|
192
|
+
const { desc, bullets } = extractCapabilities(versionData.skill_md_content, skill.summary);
|
|
193
|
+
const manifest = versionData.files_manifest || [];
|
|
194
|
+
|
|
195
|
+
out('\n\x1b[36m┌─────────────────────────────────────────────────────────────────────────────┐\x1b[0m');
|
|
196
|
+
out(`\x1b[36m│\x1b[0m \x1b[1m🚀 NOWA UMIEJĘTNOŚĆ AGENTA: ${skill.full_slug || skill.slug} (v${versionData.version})\x1b[0m`);
|
|
197
|
+
out('\x1b[36m├─────────────────────────────────────────────────────────────────────────────┤\x1b[0m');
|
|
198
|
+
|
|
199
|
+
if (desc) {
|
|
200
|
+
const shortDesc = desc.length > 180 ? `${desc.slice(0, 177)}...` : desc;
|
|
201
|
+
out(`\x1b[36m│\x1b[0m \x1b[33mOpis roli:\x1b[0m ${shortDesc}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (bullets.length > 0) {
|
|
205
|
+
out(`\x1b[36m│\x1b[0m \x1b[32mCo potrafi ten skill (Kiedy używać):\x1b[0m`);
|
|
206
|
+
for (const b of bullets) {
|
|
207
|
+
out(`\x1b[36m│\x1b[0m • ${b}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (manifest.length > 0) {
|
|
212
|
+
const fileList = manifest.map((f) => path.basename(f.path)).slice(0, 5).join(' · ');
|
|
213
|
+
const more = manifest.length > 5 ? ` (+${manifest.length - 5} więcej)` : '';
|
|
214
|
+
out(`\x1b[36m│\x1b[0m \x1b[34mDołączone referencje w paczce (${manifest.length} plików):\x1b[0m ${fileList}${more}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
out('\x1b[36m├─────────────────────────────────────────────────────────────────────────────┤\x1b[0m');
|
|
218
|
+
out('\x1b[36m│\x1b[0m \x1b[90m💡 Informacja dla Agenta AI: Twój asystent może natychmiast korzystać z tych reguł.\x1b[0m');
|
|
219
|
+
out('\x1b[36m└─────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n');
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
async function cmdSkillPush(dir, flags, out, err) {
|