@deerdesign/deertools 1.2.26 → 1.2.27
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 +2 -1
- package/package.json +1 -1
- package/src/cli.js +9 -2
- package/src/commands.js +2 -1
- package/src/skill-commands.js +263 -9
- package/src/skills-hub.js +12 -2
package/README.md
CHANGED
|
@@ -162,7 +162,8 @@ deertools profile use staging
|
|
|
162
162
|
|
|
163
163
|
# 7. AI Agent Skills (DeerSkills Hub):
|
|
164
164
|
deertools skill list # Wyświetl listę skilli ze statusem zainstalowania w projekcie
|
|
165
|
-
deertools skill sync #
|
|
165
|
+
deertools skill sync # Pobiera i aktualizuje skille z Hubu (chroni lokalne nowości)
|
|
166
|
+
deertools sync --push # Pełny sync dwukierunkowy: pobiera nowości + automatycznie wypycha lokalne skille
|
|
166
167
|
deertools skill install --all # To samo co skill sync (pełna synchronizacja zespołu)
|
|
167
168
|
deertools skill install deer-kosztorys # Zainstaluj konkretny skill w ~/.gemini/antigravity/skills lub .agents
|
|
168
169
|
deertools skill install deer-kosztorys --version 1.0.0
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const c = require('./commands');
|
|
3
3
|
|
|
4
|
+
const BOOLEAN_FLAGS = new Set([
|
|
5
|
+
'write', 'json', 'raw', 'no-open', 'push', 'auto-push', 'force',
|
|
6
|
+
'all', 'all-agents', 'global', 'private', 'shared', 'unshare',
|
|
7
|
+
'major', 'minor', 'patch', 'help', 'version', 'own', 'user', 'own-space',
|
|
8
|
+
'admin', 'ro', 'rw', 'interactive', 'select', 'dry-run', 'verbose'
|
|
9
|
+
]);
|
|
10
|
+
|
|
4
11
|
function parseArgs(argv) {
|
|
5
12
|
const pos = [];
|
|
6
13
|
const flags = {};
|
|
@@ -12,7 +19,7 @@ function parseArgs(argv) {
|
|
|
12
19
|
let val = eq === -1 ? undefined : a.slice(eq + 1);
|
|
13
20
|
if (val === undefined) {
|
|
14
21
|
const next = argv[i + 1];
|
|
15
|
-
if (next !== undefined && !next.startsWith('--') && !
|
|
22
|
+
if (next !== undefined && !next.startsWith('--') && !BOOLEAN_FLAGS.has(key)) { val = next; i++; } else val = true;
|
|
16
23
|
}
|
|
17
24
|
if (flags[key] === undefined) flags[key] = val;
|
|
18
25
|
else flags[key] = [].concat(flags[key], val);
|
|
@@ -64,8 +71,8 @@ async function main(argv) {
|
|
|
64
71
|
return code;
|
|
65
72
|
};
|
|
66
73
|
|
|
74
|
+
if (cmd === '--version' || cmd === 'version' || flags.version || flags.v) { process.stdout.write(`${require('../package.json').version}\n`); return finish(0); }
|
|
67
75
|
if (cmd === 'help' || cmd === '--help' || flags.help) { process.stdout.write(c.HELP); return finish(0); }
|
|
68
|
-
if (cmd === '--version' || cmd === 'version' || flags.version) { process.stdout.write(`${require('../package.json').version}\n`); return finish(0); }
|
|
69
76
|
if (!table[cmd]) { process.stderr.write(`Nieznana komenda „${cmd}”.\n${c.HELP}`); return finish(2); }
|
|
70
77
|
try {
|
|
71
78
|
await table[cmd](pos, flags);
|
package/src/commands.js
CHANGED
|
@@ -536,7 +536,8 @@ Dostęp i autoryzacja:
|
|
|
536
536
|
Zarządzanie skillami i zespołami (DeerSkills Hub):
|
|
537
537
|
deertools agent [list|select <agenty>|reset] Wykrywanie i konfiguracja platform agentów AI na PC.
|
|
538
538
|
deertools skill list [--tag <tag>] Przegląda skille w Hubie wraz ze statusem zainstalowania.
|
|
539
|
-
deertools skill sync [--
|
|
539
|
+
deertools skill sync [--push] [--force] Instaluje skille zespołu (--push wypycha lokalne nowości do Hubu).
|
|
540
|
+
deertools sync [--push] Krótki alias dla deertools skill sync (sync dwukierunkowy).
|
|
540
541
|
deertools skill install [--all|<slug[@wersja]>] Instaluje wszystkie skille (--all) lub wskazany skill.
|
|
541
542
|
deertools skill search <fraza> Wyszukuje umiejętności po słowach kluczowych.
|
|
542
543
|
deertools skill versions <slug> Historia wydań z changelogami z commitów i datami.
|
package/src/skill-commands.js
CHANGED
|
@@ -92,6 +92,82 @@ function readSkillVersion(filePath, lock, skill) {
|
|
|
92
92
|
return '1.0.0';
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Skanuje lokalne katalogi w poszukiwaniu skilli na dysku.
|
|
97
|
+
* Zwraca mapę slug -> { slug, version, dir, filePath, summary }.
|
|
98
|
+
*/
|
|
99
|
+
function scanLocalSkills(cwd = process.cwd(), agents = [], hubSlugs = null) {
|
|
100
|
+
const localSkills = new Map();
|
|
101
|
+
|
|
102
|
+
function inspectDir(skillDir, isAgentDir = false) {
|
|
103
|
+
const skillMd = path.join(skillDir, 'SKILL.md');
|
|
104
|
+
if (!fs.existsSync(skillMd)) return;
|
|
105
|
+
try {
|
|
106
|
+
const content = fs.readFileSync(skillMd, 'utf8');
|
|
107
|
+
let slug = path.basename(skillDir);
|
|
108
|
+
let version = '1.0.0';
|
|
109
|
+
let summary = '';
|
|
110
|
+
if (content.startsWith('---')) {
|
|
111
|
+
const parts = content.split('---', 2);
|
|
112
|
+
if (parts.length >= 2) {
|
|
113
|
+
const nameMatch = parts[1].match(/^name:\s*(.+)$/m);
|
|
114
|
+
if (nameMatch) slug = nameMatch[1].trim();
|
|
115
|
+
const verMatch = parts[1].match(/^version:\s*['"]?([0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9.]+)?)/im);
|
|
116
|
+
if (verMatch) version = verMatch[1].trim();
|
|
117
|
+
const descMatch = parts[1].match(/^description:\s*(.+)$/m);
|
|
118
|
+
if (descMatch) summary = descMatch[1].trim();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Jeśli skanujemy katalog zewnętrznego agenta (~/.gemini/...): bierzemy tylko skille Deer Design lub znane w Hubie
|
|
123
|
+
if (isAgentDir) {
|
|
124
|
+
const isDeer = slug.startsWith('deer') || slug.startsWith('rendermind') || slug.startsWith('@');
|
|
125
|
+
const isKnown = hubSlugs && (hubSlugs.has(slug) || hubSlugs.has(slug.replace(/^@[^/]+\//, '')));
|
|
126
|
+
if (!isDeer && !isKnown) return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!localSkills.has(slug)) {
|
|
130
|
+
localSkills.set(slug, {
|
|
131
|
+
slug,
|
|
132
|
+
version,
|
|
133
|
+
dir: skillDir,
|
|
134
|
+
filePath: skillMd,
|
|
135
|
+
summary,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} catch (_) {}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 1. Sprawdź .agents/skills w bieżącym projekcie
|
|
142
|
+
const projectAgentsSkills = path.join(cwd, '.agents', 'skills');
|
|
143
|
+
if (fs.existsSync(projectAgentsSkills)) {
|
|
144
|
+
try {
|
|
145
|
+
const entries = fs.readdirSync(projectAgentsSkills, { withFileTypes: true });
|
|
146
|
+
for (const ent of entries) {
|
|
147
|
+
if (ent.isDirectory() && !ent.name.startsWith('.')) {
|
|
148
|
+
inspectDir(path.join(projectAgentsSkills, ent.name), false);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (_) {}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 2. Sprawdź katalogi wykrytych agentów (tylko skille powiązane z Hubem lub Deer Design)
|
|
155
|
+
for (const agent of agents) {
|
|
156
|
+
if (agent.targetDir && fs.existsSync(agent.targetDir)) {
|
|
157
|
+
try {
|
|
158
|
+
const entries = fs.readdirSync(agent.targetDir, { withFileTypes: true });
|
|
159
|
+
for (const ent of entries) {
|
|
160
|
+
if (ent.isDirectory() && !ent.name.startsWith('.')) {
|
|
161
|
+
inspectDir(path.join(agent.targetDir, ent.name), true);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch (_) {}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return localSkills;
|
|
169
|
+
}
|
|
170
|
+
|
|
95
171
|
/**
|
|
96
172
|
* Bada stan instalacji i wersję skilla u wszystkich zadanych agentów.
|
|
97
173
|
*/
|
|
@@ -101,6 +177,7 @@ function inspectSkillForAgents(agents, skill, lock) {
|
|
|
101
177
|
let upToDateCount = 0;
|
|
102
178
|
let outdatedCount = 0;
|
|
103
179
|
let missingCount = 0;
|
|
180
|
+
let aheadCount = 0;
|
|
104
181
|
|
|
105
182
|
for (const agent of agents) {
|
|
106
183
|
const filePath = findSkillFileForAgent(agent, skill);
|
|
@@ -122,7 +199,7 @@ function inspectSkillForAgents(agents, skill, lock) {
|
|
|
122
199
|
const installedVer = readSkillVersion(filePath, lock, skill);
|
|
123
200
|
const cmp = compareSemver(installedVer, skill.latest_version);
|
|
124
201
|
|
|
125
|
-
if (cmp
|
|
202
|
+
if (cmp === 0) {
|
|
126
203
|
agentStatuses[agent.id] = {
|
|
127
204
|
agentId: agent.id,
|
|
128
205
|
agentName: agent.name,
|
|
@@ -133,6 +210,19 @@ function inspectSkillForAgents(agents, skill, lock) {
|
|
|
133
210
|
badge: `\x1b[32m[✓ ${agent.id}: v${installedVer}]\x1b[0m`,
|
|
134
211
|
};
|
|
135
212
|
upToDateCount += 1;
|
|
213
|
+
} else if (cmp > 0) {
|
|
214
|
+
agentStatuses[agent.id] = {
|
|
215
|
+
agentId: agent.id,
|
|
216
|
+
agentName: agent.name,
|
|
217
|
+
installed: true,
|
|
218
|
+
version: installedVer,
|
|
219
|
+
status: 'ahead',
|
|
220
|
+
hub_version: skill.latest_version,
|
|
221
|
+
filePath,
|
|
222
|
+
badge: `\x1b[35m[★ ${agent.id}: v${installedVer} > Hub v${skill.latest_version}]\x1b[0m`,
|
|
223
|
+
};
|
|
224
|
+
upToDateCount += 1;
|
|
225
|
+
aheadCount += 1;
|
|
136
226
|
} else {
|
|
137
227
|
agentStatuses[agent.id] = {
|
|
138
228
|
agentId: agent.id,
|
|
@@ -152,7 +242,10 @@ function inspectSkillForAgents(agents, skill, lock) {
|
|
|
152
242
|
let overallBadge = '\x1b[90m[○ Niezainstalowany]\x1b[0m';
|
|
153
243
|
|
|
154
244
|
if (installedCount > 0) {
|
|
155
|
-
if (
|
|
245
|
+
if (aheadCount > 0) {
|
|
246
|
+
overallStatus = 'ahead';
|
|
247
|
+
overallBadge = `\x1b[35m[★ Nowszy lokalnie (${aheadCount})]\x1b[0m`;
|
|
248
|
+
} else if (installedCount === agents.length && outdatedCount === 0) {
|
|
156
249
|
overallStatus = 'up_to_date';
|
|
157
250
|
overallBadge = `\x1b[32m[✓ Aktualny (${installedCount}/${agents.length})]\x1b[0m`;
|
|
158
251
|
} else if (outdatedCount > 0) {
|
|
@@ -170,6 +263,7 @@ function inspectSkillForAgents(agents, skill, lock) {
|
|
|
170
263
|
upToDateCount,
|
|
171
264
|
outdatedCount,
|
|
172
265
|
missingCount,
|
|
266
|
+
aheadCount,
|
|
173
267
|
overallStatus,
|
|
174
268
|
overallBadge,
|
|
175
269
|
};
|
|
@@ -279,9 +373,17 @@ async function cmdSkillList(flags, out, err) {
|
|
|
279
373
|
out(` • ${(a.name + ' (' + a.id + '):').padEnd(36)} ${badgeText}`);
|
|
280
374
|
}
|
|
281
375
|
|
|
376
|
+
const anyAhead = items.some((s) => {
|
|
377
|
+
const insp = inspectSkillForAgents(agents, s, lock);
|
|
378
|
+
return insp.overallStatus === 'ahead';
|
|
379
|
+
});
|
|
380
|
+
|
|
282
381
|
if (anyOutdated || anyMissing) {
|
|
283
382
|
out(`\n💡 Aby zainstalować brakujące i zaktualizować wszystkie skille u agentów:`);
|
|
284
383
|
out(` \x1b[36mdeertools skill sync\x1b[0m (lub: \x1b[36mdeertools sync\x1b[0m)`);
|
|
384
|
+
} else if (anyAhead) {
|
|
385
|
+
out(`\n💡 Wykryto skille nowsze lokalnie niż w Hubie:`);
|
|
386
|
+
out(` \x1b[35mdeertools sync --push\x1b[0m (aby wypchnąć je do DeerSkills Hub)`);
|
|
285
387
|
} else {
|
|
286
388
|
out(`\n✓ Wszystkie skille zespołu (${items.length}) są zainstalowane i aktualne u wszystkich wykrytych agentów.`);
|
|
287
389
|
}
|
|
@@ -327,6 +429,18 @@ async function installSingleSkill(slug, requestedVersion, flags, out, err, { sil
|
|
|
327
429
|
|
|
328
430
|
let count = 0;
|
|
329
431
|
for (const agent of agents) {
|
|
432
|
+
// Bezpieczeństwo: jeśli lokalna wersja na dysku jest nowsza niż w Hubie, chroń przed nadpisaniem (wymagaj --force)
|
|
433
|
+
const existingFile = findSkillFileForAgent(agent, skill);
|
|
434
|
+
if (existingFile && !flags.force && !requestedVersion) {
|
|
435
|
+
const currentDiskVer = readSkillVersion(existingFile, readLock(flags.dir), skill);
|
|
436
|
+
if (currentDiskVer && compareSemver(currentDiskVer, versionData.version) > 0) {
|
|
437
|
+
if (!silentSummary) {
|
|
438
|
+
out(` \x1b[35m★ [${agent.id}]\x1b[0m Pominięto instalację ${skill.slug}: wersja na dysku (v${currentDiskVer}) jest nowsza niż w Hubie (v${versionData.version}). Użyj --force aby nadpisać.`);
|
|
439
|
+
}
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
330
444
|
try {
|
|
331
445
|
const dest = installSkillContent(agent, skill.slug, versionData.skill_md_content);
|
|
332
446
|
if (!silentSummary) {
|
|
@@ -372,8 +486,10 @@ async function installSingleSkill(slug, requestedVersion, flags, out, err, { sil
|
|
|
372
486
|
|
|
373
487
|
/**
|
|
374
488
|
* Instaluje lub synchronizuje wszystkie skille zespołu z DeerSkills Hub.
|
|
489
|
+
* Wspiera flagę --push (lub --auto-push): automatycznie publikuje nowsze lokalne wersje do Hubu.
|
|
375
490
|
*/
|
|
376
491
|
async function cmdSkillSync(flags, out, err) {
|
|
492
|
+
const shouldPush = Boolean(flags.push || flags['auto-push']);
|
|
377
493
|
out('Pobieram pełną listę skilli zespołu z DeerSkills Hub...');
|
|
378
494
|
const res = await skillsHub.listSkills({ limit: 100 });
|
|
379
495
|
if (!res.ok) {
|
|
@@ -399,14 +515,20 @@ async function cmdSkillSync(flags, out, err) {
|
|
|
399
515
|
return;
|
|
400
516
|
}
|
|
401
517
|
|
|
402
|
-
const
|
|
518
|
+
const cwd = flags.dir || process.cwd();
|
|
519
|
+
const lock = readLock(cwd);
|
|
520
|
+
const hubSlugs = new Set(items.flatMap((s) => [s.slug, s.full_slug, s.slug.replace(/^@[^/]+\//, '')]));
|
|
521
|
+
const localSkills = scanLocalSkills(cwd, agents, hubSlugs);
|
|
522
|
+
|
|
403
523
|
out(`Platformy docelowe: \x1b[36m${agents.map((a) => a.name || a.id).join(', ')}\x1b[0m`);
|
|
404
|
-
out(`Rozpoczynam synchronizację ${items.length} skilli zespołu
|
|
524
|
+
out(`Rozpoczynam synchronizację ${items.length} skilli zespołu...${shouldPush ? ' \x1b[35m[Tryb dwukierunkowy: --push]\x1b[0m' : ''}\n`);
|
|
405
525
|
|
|
406
526
|
let installedCount = 0;
|
|
407
527
|
let updatedCount = 0;
|
|
408
528
|
let unchangedCount = 0;
|
|
409
529
|
let failedCount = 0;
|
|
530
|
+
let pushedCount = 0;
|
|
531
|
+
const aheadSkills = [];
|
|
410
532
|
|
|
411
533
|
for (const s of items) {
|
|
412
534
|
const slug = s.slug;
|
|
@@ -415,16 +537,74 @@ async function cmdSkillSync(flags, out, err) {
|
|
|
415
537
|
const currentVer = current?.version;
|
|
416
538
|
const latestVer = s.latest_version || '1.0.0';
|
|
417
539
|
|
|
418
|
-
|
|
419
|
-
|
|
540
|
+
// Sprawdź wersję lokalną na dysku u agentów lub w .agents/skills
|
|
541
|
+
const localSkill = localSkills.get(slug) || localSkills.get(fullSlug) || localSkills.get(slug.replace(/^@[^/]+\//, ''));
|
|
542
|
+
let localDiskVer = localSkill?.version || null;
|
|
543
|
+
|
|
544
|
+
if (!localDiskVer) {
|
|
545
|
+
for (const agent of agents) {
|
|
546
|
+
const fp = findSkillFileForAgent(agent, s);
|
|
547
|
+
if (fp) {
|
|
548
|
+
const v = readSkillVersion(fp, lock, s);
|
|
549
|
+
if (v) { localDiskVer = v; break; }
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const effectiveLocalVer = localDiskVer || currentVer;
|
|
555
|
+
const cmpLocalToHub = effectiveLocalVer ? compareSemver(effectiveLocalVer, latestVer) : -1;
|
|
556
|
+
|
|
557
|
+
// 1. Ochrona bezpieczeństwa: Lokalna wersja jest nowsza niż w Hubie (AHEAD)
|
|
558
|
+
if (cmpLocalToHub > 0) {
|
|
559
|
+
const pushDir = localSkill?.dir || (localSkill?.filePath ? path.dirname(localSkill.filePath) : null) || path.join(cwd, '.agents', 'skills', slug);
|
|
560
|
+
aheadSkills.push({
|
|
561
|
+
slug: s.full_slug || s.slug,
|
|
562
|
+
localVersion: effectiveLocalVer,
|
|
563
|
+
hubVersion: latestVer,
|
|
564
|
+
dir: pushDir,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (shouldPush) {
|
|
568
|
+
try {
|
|
569
|
+
out(` \x1b[35m↑ PUSH\x1b[0m ${s.full_slug.padEnd(36)} v${effectiveLocalVer} → Hub (wypychanie lokalnej nowości)...`);
|
|
570
|
+
const pushRes = await skillsHub.pushSkill(pushDir, {
|
|
571
|
+
version: effectiveLocalVer,
|
|
572
|
+
changelog: flags.changelog || `Synchronizacja dwukierunkowa: wypchnięto lokalną wersję v${effectiveLocalVer}`,
|
|
573
|
+
});
|
|
574
|
+
if (pushRes.ok) {
|
|
575
|
+
out(` \x1b[32m✓ PUSHED\x1b[0m ${s.full_slug.padEnd(34)} v${effectiveLocalVer} opublikowano w DeerSkills Hub.`);
|
|
576
|
+
pushedCount += 1;
|
|
577
|
+
lock.skills[slug] = {
|
|
578
|
+
fullSlug: s.full_slug,
|
|
579
|
+
version: effectiveLocalVer,
|
|
580
|
+
installedAt: new Date().toISOString(),
|
|
581
|
+
};
|
|
582
|
+
writeLock(lock, cwd);
|
|
583
|
+
} else {
|
|
584
|
+
out(` \x1b[31m✗ PUSH BŁĄD\x1b[0m ${s.full_slug}: ${pushRes.data?.error || 'Błąd publikacji'}`);
|
|
585
|
+
}
|
|
586
|
+
} catch (pe) {
|
|
587
|
+
out(` \x1b[31m✗ PUSH BŁĄD\x1b[0m ${s.full_slug}: ${pe.message}`);
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
out(` \x1b[35m★\x1b[0m ${s.full_slug.padEnd(38)} v${effectiveLocalVer.padEnd(8)} [Lokalnie nowszy niż w Hubie (Hub: v${latestVer})]`);
|
|
591
|
+
}
|
|
592
|
+
unchangedCount += 1;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// 2. Wersje identyczne - aktualny
|
|
597
|
+
if (effectiveLocalVer && cmpLocalToHub === 0 && !flags.force) {
|
|
598
|
+
out(` \x1b[32m✓\x1b[0m ${s.full_slug.padEnd(38)} v${effectiveLocalVer.padEnd(8)} [Aktualny]`);
|
|
420
599
|
unchangedCount += 1;
|
|
421
600
|
continue;
|
|
422
601
|
}
|
|
423
602
|
|
|
603
|
+
// 3. Wersja w Hubie nowsza niż lokalna - pobieramy downstream
|
|
424
604
|
try {
|
|
425
605
|
await installSingleSkill(s.full_slug || s.slug, latestVer, flags, out, err, { silentSummary: true });
|
|
426
|
-
if (
|
|
427
|
-
out(` \x1b[33m↑\x1b[0m ${s.full_slug.padEnd(38)} v${latestVer.padEnd(8)} [Zaktualizowano z v${
|
|
606
|
+
if (effectiveLocalVer) {
|
|
607
|
+
out(` \x1b[33m↑\x1b[0m ${s.full_slug.padEnd(38)} v${latestVer.padEnd(8)} [Zaktualizowano z v${effectiveLocalVer}]`);
|
|
428
608
|
updatedCount += 1;
|
|
429
609
|
} else {
|
|
430
610
|
out(` \x1b[36m+\x1b[0m ${s.full_slug.padEnd(38)} v${latestVer.padEnd(8)} [Zainstalowano]`);
|
|
@@ -436,17 +616,89 @@ async function cmdSkillSync(flags, out, err) {
|
|
|
436
616
|
}
|
|
437
617
|
}
|
|
438
618
|
|
|
619
|
+
// 4. Sprawdź lokalne skille, których w ogóle nie ma w Hubie
|
|
620
|
+
const unpushedLocalSkills = [];
|
|
621
|
+
for (const [lSlug, lk] of localSkills.entries()) {
|
|
622
|
+
if (!hubSlugs.has(lSlug)) {
|
|
623
|
+
unpushedLocalSkills.push(lk);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (unpushedLocalSkills.length > 0) {
|
|
628
|
+
for (const ul of unpushedLocalSkills) {
|
|
629
|
+
if (shouldPush) {
|
|
630
|
+
try {
|
|
631
|
+
out(` \x1b[35m↑ PUSH\x1b[0m ${ul.slug.padEnd(36)} v${ul.version} → Hub (nowy skill lokalny)...`);
|
|
632
|
+
const pushRes = await skillsHub.pushSkill(ul.dir, {
|
|
633
|
+
version: ul.version,
|
|
634
|
+
changelog: flags.changelog || `Inicjalna publikacja skilla lokalnego v${ul.version}`,
|
|
635
|
+
});
|
|
636
|
+
if (pushRes.ok) {
|
|
637
|
+
out(` \x1b[32m✓ PUSHED\x1b[0m ${ul.slug.padEnd(34)} v${ul.version} opublikowano w DeerSkills Hub.`);
|
|
638
|
+
pushedCount += 1;
|
|
639
|
+
} else {
|
|
640
|
+
out(` \x1b[31m✗ PUSH BŁĄD\x1b[0m ${ul.slug}: ${pushRes.data?.error || 'Błąd publikacji'}`);
|
|
641
|
+
}
|
|
642
|
+
} catch (pe) {
|
|
643
|
+
out(` \x1b[31m✗ PUSH BŁĄD\x1b[0m ${ul.slug}: ${pe.message}`);
|
|
644
|
+
}
|
|
645
|
+
} else {
|
|
646
|
+
out(` \x1b[90m○\x1b[0m ${ul.slug.padEnd(38)} v${ul.version.padEnd(8)} [Tylko lokalnie (brak w Hubie)]`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
439
651
|
out('\n' + '─'.repeat(72));
|
|
440
652
|
out(`\x1b[1m✓ Podsumowanie synchronizacji ekosystemu Deer Design:\x1b[0m`);
|
|
441
653
|
out(` • Nowo zainstalowane: \x1b[36m${installedCount}\x1b[0m`);
|
|
442
654
|
out(` • Zaktualizowane: \x1b[33m${updatedCount}\x1b[0m`);
|
|
443
655
|
out(` • Aktualne bez zmian: \x1b[32m${unchangedCount}\x1b[0m`);
|
|
656
|
+
if (pushedCount > 0) {
|
|
657
|
+
out(` • Wypchnięte do Hubu: \x1b[35m${pushedCount}\x1b[0m`);
|
|
658
|
+
}
|
|
444
659
|
if (failedCount > 0) {
|
|
445
660
|
out(` • Błędy instalacji: \x1b[31m${failedCount}\x1b[0m`);
|
|
446
661
|
}
|
|
447
662
|
out(` • Agenci docelowi: ${agents.map((a) => a.id).join(', ')}`);
|
|
448
663
|
out(` • Zapisano w rejestrze: ${LOCK_FILE}`);
|
|
449
|
-
out('─'.repeat(72)
|
|
664
|
+
out('─'.repeat(72));
|
|
665
|
+
|
|
666
|
+
// Alert i komunikat o możliwości wypchnięcia (jeśli nie użyto --push, a wykryto lokalne nowości)
|
|
667
|
+
if (!shouldPush && (aheadSkills.length > 0 || unpushedLocalSkills.length > 0)) {
|
|
668
|
+
out('\n\x1b[35m┌─────────────────────────────────────────────────────────────────────────────┐\x1b[0m');
|
|
669
|
+
out('\x1b[35m│\x1b[0m \x1b[1m💡 WYKRYTO SKILLE Z LOKALNYMI ZMIANAMI GOTOWYMI DO WYPCHNIĘCIA (AHEAD)\x1b[0m \x1b[35m│\x1b[0m');
|
|
670
|
+
out('\x1b[35m├─────────────────────────────────────────────────────────────────────────────┤\x1b[0m');
|
|
671
|
+
out('\x1b[35m│\x1b[0m Domyślny \x1b[36msync\x1b[0m pobiera nowości z Hubu i chroni Twoje lokalne pliki. \x1b[35m│\x1b[0m');
|
|
672
|
+
out('\x1b[35m│\x1b[0m Posiadasz skille nowsze niż w rejestrze firmowym DeerSkills Hub: \x1b[35m│\x1b[0m');
|
|
673
|
+
|
|
674
|
+
const allNotices = [];
|
|
675
|
+
for (const a of aheadSkills) {
|
|
676
|
+
allNotices.push(` • ${a.slug}: lokalnie v${a.localVersion} > w Hubie v${a.hubVersion}`);
|
|
677
|
+
}
|
|
678
|
+
for (const u of unpushedLocalSkills) {
|
|
679
|
+
allNotices.push(` • ${u.slug}: lokalnie v${u.version} (nowy, brak w Hubie)`);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const maxToShow = 6;
|
|
683
|
+
for (let i = 0; i < Math.min(allNotices.length, maxToShow); i++) {
|
|
684
|
+
out(`\x1b[35m│\x1b[0m \x1b[33m${allNotices[i].padEnd(73)}\x1b[0m\x1b[35m│\x1b[0m`);
|
|
685
|
+
}
|
|
686
|
+
if (allNotices.length > maxToShow) {
|
|
687
|
+
const moreText = ` ... i jeszcze ${allNotices.length - maxToShow} innych skilli z lokalnymi zmianami`;
|
|
688
|
+
out(`\x1b[35m│\x1b[0m \x1b[90m${moreText.padEnd(73)}\x1b[0m\x1b[35m│\x1b[0m`);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
out('\x1b[35m├─────────────────────────────────────────────────────────────────────────────┤\x1b[0m');
|
|
692
|
+
out('\x1b[35m│\x1b[0m \x1b[1mAby wypchnąć zmiany do Hubu dla zespołu / firmy:\x1b[0m \x1b[35m│\x1b[0m');
|
|
693
|
+
if (aheadSkills.length > 0) {
|
|
694
|
+
out(`\x1b[35m│\x1b[0m ▶ Pojedynczo: \x1b[36mdeertools skill push ${aheadSkills[0].dir}\x1b[0m`);
|
|
695
|
+
}
|
|
696
|
+
out('\x1b[35m│\x1b[0m ▶ Automatycznie (dwukierunkowo): \x1b[35m│\x1b[0m');
|
|
697
|
+
out('\x1b[35m│\x1b[0m \x1b[32mdeertools sync --push\x1b[0m (lub: \x1b[32mdeertools skill sync --push\x1b[0m) \x1b[35m│\x1b[0m');
|
|
698
|
+
out('\x1b[35m└─────────────────────────────────────────────────────────────────────────────┘\x1b[0m\n');
|
|
699
|
+
} else {
|
|
700
|
+
out('');
|
|
701
|
+
}
|
|
450
702
|
}
|
|
451
703
|
|
|
452
704
|
async function cmdSkillInstall(target, flags, out, err) {
|
|
@@ -985,4 +1237,6 @@ module.exports = {
|
|
|
985
1237
|
cmdSkillShare,
|
|
986
1238
|
checkSkillUpdates,
|
|
987
1239
|
updateSkills,
|
|
1240
|
+
scanLocalSkills,
|
|
1241
|
+
inspectSkillForAgents,
|
|
988
1242
|
};
|
package/src/skills-hub.js
CHANGED
|
@@ -5,6 +5,7 @@ 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';
|
|
10
11
|
const SECRET_PATTERNS = [
|
|
@@ -272,6 +273,7 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
272
273
|
let title = slug;
|
|
273
274
|
let description = '';
|
|
274
275
|
let frontmatterTags = [];
|
|
276
|
+
let frontmatterVersion = null;
|
|
275
277
|
if (skillContent.startsWith('---')) {
|
|
276
278
|
const parts = skillContent.split('---', 2);
|
|
277
279
|
if (parts.length >= 2) {
|
|
@@ -282,6 +284,8 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
282
284
|
}
|
|
283
285
|
const descMatch = parts[1].match(/^description:\s*(.+)$/m);
|
|
284
286
|
if (descMatch) description = descMatch[1].trim();
|
|
287
|
+
const verMatch = parts[1].match(/^version:\s*['"]?([0-9]+\.[0-9]+(\.[0-9]+)?(-[a-zA-Z0-9.]+)?)/im);
|
|
288
|
+
if (verMatch) frontmatterVersion = verMatch[1].trim();
|
|
285
289
|
frontmatterTags = extractFrontmatterTags(parts[1]);
|
|
286
290
|
}
|
|
287
291
|
}
|
|
@@ -301,9 +305,15 @@ async function pushSkill(skillDir, { version = null, bump = 'patch', changelog =
|
|
|
301
305
|
try {
|
|
302
306
|
const existing = await getSkill(slug);
|
|
303
307
|
const latestVer = existing?.data?.skill?.latest_version;
|
|
304
|
-
|
|
308
|
+
if (frontmatterVersion && latestVer && compareSemver(frontmatterVersion, latestVer) > 0) {
|
|
309
|
+
targetVersion = frontmatterVersion;
|
|
310
|
+
} else if (frontmatterVersion && !latestVer) {
|
|
311
|
+
targetVersion = frontmatterVersion;
|
|
312
|
+
} else {
|
|
313
|
+
targetVersion = latestVer ? bumpVersion(latestVer, bump) : (frontmatterVersion || '1.0.0');
|
|
314
|
+
}
|
|
305
315
|
} catch (_) {
|
|
306
|
-
targetVersion = '1.0.0';
|
|
316
|
+
targetVersion = frontmatterVersion || '1.0.0';
|
|
307
317
|
}
|
|
308
318
|
}
|
|
309
319
|
|