@deerdesign/deertools 1.2.25 → 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.
@@ -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 >= 0) {
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 (installedCount === agents.length && outdatedCount === 0) {
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 lock = readLock(flags.dir);
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...\n`);
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
- if (currentVer && currentVer === latestVer && !flags.force) {
419
- out(` \x1b[32m✓\x1b[0m ${s.full_slug.padEnd(38)} v${currentVer.padEnd(8)} [Aktualny]`);
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 (currentVer) {
427
- out(` \x1b[33m↑\x1b[0m ${s.full_slug.padEnd(38)} v${latestVer.padEnd(8)} [Zaktualizowano z v${currentVer}]`);
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) + '\n');
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
- targetVersion = latestVer ? bumpVersion(latestVer, bump) : '1.0.0';
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