@releasekit/notes 0.2.0-next.5 → 0.2.0-next.7
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/dist/{chunk-ADZJSWND.js → chunk-BLWJTLRD.js} +81 -63
- package/dist/cli.cjs +81 -63
- package/dist/cli.js +1 -1
- package/dist/index.cjs +81 -63
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -520,15 +520,26 @@ Entries:
|
|
|
520
520
|
Output only valid JSON, nothing else:`;
|
|
521
521
|
function buildCustomCategorizePrompt(categories) {
|
|
522
522
|
const categoryList = categories.map((c) => `- "${c.name}": ${c.description}`).join("\n");
|
|
523
|
+
const developerCategory = categories.find((c) => c.name === "Developer");
|
|
524
|
+
let scopeInstructions = "";
|
|
525
|
+
if (developerCategory) {
|
|
526
|
+
const scopeMatch = developerCategory.description.match(/from:\s*([^.]+)/);
|
|
527
|
+
if (scopeMatch?.[1]) {
|
|
528
|
+
const scopes = scopeMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
529
|
+
if (scopes.length > 0) {
|
|
530
|
+
scopeInstructions = `
|
|
531
|
+
|
|
532
|
+
For the "Developer" category, you MUST assign a scope from this exact list: ${scopes.join(", ")}.
|
|
533
|
+
`;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
523
537
|
return `You are categorizing changelog entries for a software release.
|
|
524
538
|
|
|
525
|
-
Given the following entries, group them into the specified categories. Only use the categories listed below
|
|
539
|
+
Given the following entries, group them into the specified categories. Only use the categories listed below in this exact order:
|
|
526
540
|
|
|
527
541
|
Categories:
|
|
528
|
-
${categoryList}
|
|
529
|
-
|
|
530
|
-
For entries in categories that involve internal/developer changes, set a "scope" field on those entries with a short subcategory label (e.g., "CI", "Dependencies", "Testing", "Code Quality", "Build System").
|
|
531
|
-
|
|
542
|
+
${categoryList}${scopeInstructions}
|
|
532
543
|
Output a JSON object with two fields:
|
|
533
544
|
- "categories": an object where keys are category names and values are arrays of entry indices (0-based)
|
|
534
545
|
- "scopes": an object where keys are entry indices (as strings) and values are scope labels
|
|
@@ -630,6 +641,33 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
630
641
|
|
|
631
642
|
// src/llm/tasks/enhance-and-categorize.ts
|
|
632
643
|
import { warn as warn2 } from "@releasekit/core";
|
|
644
|
+
|
|
645
|
+
// src/utils/retry.ts
|
|
646
|
+
function sleep(ms) {
|
|
647
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
648
|
+
}
|
|
649
|
+
async function withRetry(fn, options = {}) {
|
|
650
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
651
|
+
const initialDelay = options.initialDelay ?? 1e3;
|
|
652
|
+
const maxDelay = options.maxDelay ?? 3e4;
|
|
653
|
+
const backoffFactor = options.backoffFactor ?? 2;
|
|
654
|
+
let lastError;
|
|
655
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
656
|
+
try {
|
|
657
|
+
return await fn();
|
|
658
|
+
} catch (error) {
|
|
659
|
+
lastError = error;
|
|
660
|
+
if (attempt < maxAttempts - 1) {
|
|
661
|
+
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
662
|
+
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
663
|
+
await sleep(Math.max(0, base + jitter));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
throw lastError;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
633
671
|
function buildPrompt(entries, categories, style) {
|
|
634
672
|
const entriesText = entries.map((e, i) => `${i}. [${e.type}]${e.scope ? ` (${e.scope})` : ""}: ${e.description}`).join("\n");
|
|
635
673
|
const styleText = style || 'Use present tense ("Add feature" not "Added feature"). Be concise.';
|
|
@@ -661,41 +699,46 @@ async function enhanceAndCategorize(provider, entries, context) {
|
|
|
661
699
|
if (entries.length === 0) {
|
|
662
700
|
return { enhancedEntries: [], categories: [] };
|
|
663
701
|
}
|
|
664
|
-
const
|
|
702
|
+
const retryOpts = LLM_DEFAULTS.retry;
|
|
665
703
|
try {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const result = parsed.entries[i];
|
|
674
|
-
if (!result) return original;
|
|
675
|
-
return {
|
|
676
|
-
...original,
|
|
677
|
-
description: result.description || original.description,
|
|
678
|
-
scope: result.scope || original.scope
|
|
679
|
-
};
|
|
680
|
-
});
|
|
681
|
-
const categoryMap = /* @__PURE__ */ new Map();
|
|
682
|
-
for (let i = 0; i < parsed.entries.length; i++) {
|
|
683
|
-
const result = parsed.entries[i];
|
|
684
|
-
const category = result?.category || "General";
|
|
685
|
-
const entry = enhancedEntries[i];
|
|
686
|
-
if (!entry) continue;
|
|
687
|
-
if (!categoryMap.has(category)) {
|
|
688
|
-
categoryMap.set(category, []);
|
|
704
|
+
return await withRetry(async () => {
|
|
705
|
+
const prompt = buildPrompt(entries, context.categories, context.style);
|
|
706
|
+
const response = await provider.complete(prompt);
|
|
707
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
708
|
+
const parsed = JSON.parse(cleaned);
|
|
709
|
+
if (!Array.isArray(parsed.entries)) {
|
|
710
|
+
throw new Error('Response missing "entries" array');
|
|
689
711
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
712
|
+
const enhancedEntries = entries.map((original, i) => {
|
|
713
|
+
const result = parsed.entries[i];
|
|
714
|
+
if (!result) return original;
|
|
715
|
+
return {
|
|
716
|
+
...original,
|
|
717
|
+
description: result.description || original.description,
|
|
718
|
+
scope: result.scope || original.scope
|
|
719
|
+
};
|
|
720
|
+
});
|
|
721
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
722
|
+
for (let i = 0; i < parsed.entries.length; i++) {
|
|
723
|
+
const result = parsed.entries[i];
|
|
724
|
+
const category = result?.category || "General";
|
|
725
|
+
const entry = enhancedEntries[i];
|
|
726
|
+
if (!entry) continue;
|
|
727
|
+
if (!categoryMap.has(category)) {
|
|
728
|
+
categoryMap.set(category, []);
|
|
729
|
+
}
|
|
730
|
+
categoryMap.get(category).push(entry);
|
|
731
|
+
}
|
|
732
|
+
const categories = [];
|
|
733
|
+
for (const [category, catEntries] of categoryMap) {
|
|
734
|
+
categories.push({ category, entries: catEntries });
|
|
735
|
+
}
|
|
736
|
+
return { enhancedEntries, categories };
|
|
737
|
+
}, retryOpts);
|
|
697
738
|
} catch (error) {
|
|
698
|
-
warn2(
|
|
739
|
+
warn2(
|
|
740
|
+
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error instanceof Error ? error.message : String(error)}`
|
|
741
|
+
);
|
|
699
742
|
return {
|
|
700
743
|
enhancedEntries: entries,
|
|
701
744
|
categories: [{ category: "General", entries }]
|
|
@@ -1170,31 +1213,6 @@ function renderTemplate(templatePath, context, engine) {
|
|
|
1170
1213
|
return renderComposable(templatePath, context, engine);
|
|
1171
1214
|
}
|
|
1172
1215
|
|
|
1173
|
-
// src/utils/retry.ts
|
|
1174
|
-
function sleep(ms) {
|
|
1175
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1176
|
-
}
|
|
1177
|
-
async function withRetry(fn, options = {}) {
|
|
1178
|
-
const maxAttempts = options.maxAttempts ?? 3;
|
|
1179
|
-
const initialDelay = options.initialDelay ?? 1e3;
|
|
1180
|
-
const maxDelay = options.maxDelay ?? 3e4;
|
|
1181
|
-
const backoffFactor = options.backoffFactor ?? 2;
|
|
1182
|
-
let lastError;
|
|
1183
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1184
|
-
try {
|
|
1185
|
-
return await fn();
|
|
1186
|
-
} catch (error) {
|
|
1187
|
-
lastError = error;
|
|
1188
|
-
if (attempt < maxAttempts - 1) {
|
|
1189
|
-
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
1190
|
-
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
1191
|
-
await sleep(Math.max(0, base + jitter));
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
throw lastError;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
1216
|
// src/core/pipeline.ts
|
|
1199
1217
|
function generateCompareUrl(repoUrl, from, to) {
|
|
1200
1218
|
if (/gitlab\.com/i.test(repoUrl)) {
|
|
@@ -1263,7 +1281,7 @@ async function processWithLLM(context, config) {
|
|
|
1263
1281
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1264
1282
|
info4(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1265
1283
|
if (tasks.enhance && tasks.categorize) {
|
|
1266
|
-
info4("Enhancing and categorizing entries with LLM
|
|
1284
|
+
info4("Enhancing and categorizing entries with LLM...");
|
|
1267
1285
|
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1268
1286
|
enhanced.entries = result.enhancedEntries;
|
|
1269
1287
|
enhanced.categories = {};
|
package/dist/cli.cjs
CHANGED
|
@@ -366,15 +366,26 @@ Entries:
|
|
|
366
366
|
Output only valid JSON, nothing else:`;
|
|
367
367
|
function buildCustomCategorizePrompt(categories) {
|
|
368
368
|
const categoryList = categories.map((c) => `- "${c.name}": ${c.description}`).join("\n");
|
|
369
|
+
const developerCategory = categories.find((c) => c.name === "Developer");
|
|
370
|
+
let scopeInstructions = "";
|
|
371
|
+
if (developerCategory) {
|
|
372
|
+
const scopeMatch = developerCategory.description.match(/from:\s*([^.]+)/);
|
|
373
|
+
if (scopeMatch?.[1]) {
|
|
374
|
+
const scopes = scopeMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
375
|
+
if (scopes.length > 0) {
|
|
376
|
+
scopeInstructions = `
|
|
377
|
+
|
|
378
|
+
For the "Developer" category, you MUST assign a scope from this exact list: ${scopes.join(", ")}.
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
369
383
|
return `You are categorizing changelog entries for a software release.
|
|
370
384
|
|
|
371
|
-
Given the following entries, group them into the specified categories. Only use the categories listed below
|
|
385
|
+
Given the following entries, group them into the specified categories. Only use the categories listed below in this exact order:
|
|
372
386
|
|
|
373
387
|
Categories:
|
|
374
|
-
${categoryList}
|
|
375
|
-
|
|
376
|
-
For entries in categories that involve internal/developer changes, set a "scope" field on those entries with a short subcategory label (e.g., "CI", "Dependencies", "Testing", "Code Quality", "Build System").
|
|
377
|
-
|
|
388
|
+
${categoryList}${scopeInstructions}
|
|
378
389
|
Output a JSON object with two fields:
|
|
379
390
|
- "categories": an object where keys are category names and values are arrays of entry indices (0-based)
|
|
380
391
|
- "scopes": an object where keys are entry indices (as strings) and values are scope labels
|
|
@@ -476,6 +487,33 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
476
487
|
|
|
477
488
|
// src/llm/tasks/enhance-and-categorize.ts
|
|
478
489
|
var import_core4 = require("@releasekit/core");
|
|
490
|
+
|
|
491
|
+
// src/utils/retry.ts
|
|
492
|
+
function sleep(ms) {
|
|
493
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
494
|
+
}
|
|
495
|
+
async function withRetry(fn, options = {}) {
|
|
496
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
497
|
+
const initialDelay = options.initialDelay ?? 1e3;
|
|
498
|
+
const maxDelay = options.maxDelay ?? 3e4;
|
|
499
|
+
const backoffFactor = options.backoffFactor ?? 2;
|
|
500
|
+
let lastError;
|
|
501
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
502
|
+
try {
|
|
503
|
+
return await fn();
|
|
504
|
+
} catch (error2) {
|
|
505
|
+
lastError = error2;
|
|
506
|
+
if (attempt < maxAttempts - 1) {
|
|
507
|
+
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
508
|
+
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
509
|
+
await sleep(Math.max(0, base + jitter));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
throw lastError;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
479
517
|
function buildPrompt(entries, categories, style) {
|
|
480
518
|
const entriesText = entries.map((e, i) => `${i}. [${e.type}]${e.scope ? ` (${e.scope})` : ""}: ${e.description}`).join("\n");
|
|
481
519
|
const styleText = style || 'Use present tense ("Add feature" not "Added feature"). Be concise.';
|
|
@@ -507,41 +545,46 @@ async function enhanceAndCategorize(provider, entries, context) {
|
|
|
507
545
|
if (entries.length === 0) {
|
|
508
546
|
return { enhancedEntries: [], categories: [] };
|
|
509
547
|
}
|
|
510
|
-
const
|
|
548
|
+
const retryOpts = LLM_DEFAULTS.retry;
|
|
511
549
|
try {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const result = parsed.entries[i];
|
|
520
|
-
if (!result) return original;
|
|
521
|
-
return {
|
|
522
|
-
...original,
|
|
523
|
-
description: result.description || original.description,
|
|
524
|
-
scope: result.scope || original.scope
|
|
525
|
-
};
|
|
526
|
-
});
|
|
527
|
-
const categoryMap = /* @__PURE__ */ new Map();
|
|
528
|
-
for (let i = 0; i < parsed.entries.length; i++) {
|
|
529
|
-
const result = parsed.entries[i];
|
|
530
|
-
const category = result?.category || "General";
|
|
531
|
-
const entry = enhancedEntries[i];
|
|
532
|
-
if (!entry) continue;
|
|
533
|
-
if (!categoryMap.has(category)) {
|
|
534
|
-
categoryMap.set(category, []);
|
|
550
|
+
return await withRetry(async () => {
|
|
551
|
+
const prompt = buildPrompt(entries, context.categories, context.style);
|
|
552
|
+
const response = await provider.complete(prompt);
|
|
553
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
554
|
+
const parsed = JSON.parse(cleaned);
|
|
555
|
+
if (!Array.isArray(parsed.entries)) {
|
|
556
|
+
throw new Error('Response missing "entries" array');
|
|
535
557
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
558
|
+
const enhancedEntries = entries.map((original, i) => {
|
|
559
|
+
const result = parsed.entries[i];
|
|
560
|
+
if (!result) return original;
|
|
561
|
+
return {
|
|
562
|
+
...original,
|
|
563
|
+
description: result.description || original.description,
|
|
564
|
+
scope: result.scope || original.scope
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
568
|
+
for (let i = 0; i < parsed.entries.length; i++) {
|
|
569
|
+
const result = parsed.entries[i];
|
|
570
|
+
const category = result?.category || "General";
|
|
571
|
+
const entry = enhancedEntries[i];
|
|
572
|
+
if (!entry) continue;
|
|
573
|
+
if (!categoryMap.has(category)) {
|
|
574
|
+
categoryMap.set(category, []);
|
|
575
|
+
}
|
|
576
|
+
categoryMap.get(category).push(entry);
|
|
577
|
+
}
|
|
578
|
+
const categories = [];
|
|
579
|
+
for (const [category, catEntries] of categoryMap) {
|
|
580
|
+
categories.push({ category, entries: catEntries });
|
|
581
|
+
}
|
|
582
|
+
return { enhancedEntries, categories };
|
|
583
|
+
}, retryOpts);
|
|
543
584
|
} catch (error2) {
|
|
544
|
-
(0, import_core4.warn)(
|
|
585
|
+
(0, import_core4.warn)(
|
|
586
|
+
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
587
|
+
);
|
|
545
588
|
return {
|
|
546
589
|
enhancedEntries: entries,
|
|
547
590
|
categories: [{ category: "General", entries }]
|
|
@@ -1181,31 +1224,6 @@ function renderTemplate(templatePath, context, engine) {
|
|
|
1181
1224
|
return renderComposable(templatePath, context, engine);
|
|
1182
1225
|
}
|
|
1183
1226
|
|
|
1184
|
-
// src/utils/retry.ts
|
|
1185
|
-
function sleep(ms) {
|
|
1186
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1187
|
-
}
|
|
1188
|
-
async function withRetry(fn, options = {}) {
|
|
1189
|
-
const maxAttempts = options.maxAttempts ?? 3;
|
|
1190
|
-
const initialDelay = options.initialDelay ?? 1e3;
|
|
1191
|
-
const maxDelay = options.maxDelay ?? 3e4;
|
|
1192
|
-
const backoffFactor = options.backoffFactor ?? 2;
|
|
1193
|
-
let lastError;
|
|
1194
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1195
|
-
try {
|
|
1196
|
-
return await fn();
|
|
1197
|
-
} catch (error2) {
|
|
1198
|
-
lastError = error2;
|
|
1199
|
-
if (attempt < maxAttempts - 1) {
|
|
1200
|
-
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
1201
|
-
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
1202
|
-
await sleep(Math.max(0, base + jitter));
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
throw lastError;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
1227
|
// src/core/pipeline.ts
|
|
1210
1228
|
var import_meta = {};
|
|
1211
1229
|
function generateCompareUrl(repoUrl, from, to) {
|
|
@@ -1275,7 +1293,7 @@ async function processWithLLM(context, config) {
|
|
|
1275
1293
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1276
1294
|
(0, import_core8.info)(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1277
1295
|
if (tasks.enhance && tasks.categorize) {
|
|
1278
|
-
(0, import_core8.info)("Enhancing and categorizing entries with LLM
|
|
1296
|
+
(0, import_core8.info)("Enhancing and categorizing entries with LLM...");
|
|
1279
1297
|
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1280
1298
|
enhanced.entries = result.enhancedEntries;
|
|
1281
1299
|
enhanced.categories = {};
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -416,15 +416,26 @@ Entries:
|
|
|
416
416
|
Output only valid JSON, nothing else:`;
|
|
417
417
|
function buildCustomCategorizePrompt(categories) {
|
|
418
418
|
const categoryList = categories.map((c) => `- "${c.name}": ${c.description}`).join("\n");
|
|
419
|
+
const developerCategory = categories.find((c) => c.name === "Developer");
|
|
420
|
+
let scopeInstructions = "";
|
|
421
|
+
if (developerCategory) {
|
|
422
|
+
const scopeMatch = developerCategory.description.match(/from:\s*([^.]+)/);
|
|
423
|
+
if (scopeMatch?.[1]) {
|
|
424
|
+
const scopes = scopeMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
425
|
+
if (scopes.length > 0) {
|
|
426
|
+
scopeInstructions = `
|
|
427
|
+
|
|
428
|
+
For the "Developer" category, you MUST assign a scope from this exact list: ${scopes.join(", ")}.
|
|
429
|
+
`;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
419
433
|
return `You are categorizing changelog entries for a software release.
|
|
420
434
|
|
|
421
|
-
Given the following entries, group them into the specified categories. Only use the categories listed below
|
|
435
|
+
Given the following entries, group them into the specified categories. Only use the categories listed below in this exact order:
|
|
422
436
|
|
|
423
437
|
Categories:
|
|
424
|
-
${categoryList}
|
|
425
|
-
|
|
426
|
-
For entries in categories that involve internal/developer changes, set a "scope" field on those entries with a short subcategory label (e.g., "CI", "Dependencies", "Testing", "Code Quality", "Build System").
|
|
427
|
-
|
|
438
|
+
${categoryList}${scopeInstructions}
|
|
428
439
|
Output a JSON object with two fields:
|
|
429
440
|
- "categories": an object where keys are category names and values are arrays of entry indices (0-based)
|
|
430
441
|
- "scopes": an object where keys are entry indices (as strings) and values are scope labels
|
|
@@ -526,6 +537,33 @@ async function enhanceEntries(provider, entries, context, concurrency = LLM_DEFA
|
|
|
526
537
|
|
|
527
538
|
// src/llm/tasks/enhance-and-categorize.ts
|
|
528
539
|
var import_core4 = require("@releasekit/core");
|
|
540
|
+
|
|
541
|
+
// src/utils/retry.ts
|
|
542
|
+
function sleep(ms) {
|
|
543
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
544
|
+
}
|
|
545
|
+
async function withRetry(fn, options = {}) {
|
|
546
|
+
const maxAttempts = options.maxAttempts ?? 3;
|
|
547
|
+
const initialDelay = options.initialDelay ?? 1e3;
|
|
548
|
+
const maxDelay = options.maxDelay ?? 3e4;
|
|
549
|
+
const backoffFactor = options.backoffFactor ?? 2;
|
|
550
|
+
let lastError;
|
|
551
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
552
|
+
try {
|
|
553
|
+
return await fn();
|
|
554
|
+
} catch (error) {
|
|
555
|
+
lastError = error;
|
|
556
|
+
if (attempt < maxAttempts - 1) {
|
|
557
|
+
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
558
|
+
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
559
|
+
await sleep(Math.max(0, base + jitter));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
throw lastError;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/llm/tasks/enhance-and-categorize.ts
|
|
529
567
|
function buildPrompt(entries, categories, style) {
|
|
530
568
|
const entriesText = entries.map((e, i) => `${i}. [${e.type}]${e.scope ? ` (${e.scope})` : ""}: ${e.description}`).join("\n");
|
|
531
569
|
const styleText = style || 'Use present tense ("Add feature" not "Added feature"). Be concise.';
|
|
@@ -557,41 +595,46 @@ async function enhanceAndCategorize(provider, entries, context) {
|
|
|
557
595
|
if (entries.length === 0) {
|
|
558
596
|
return { enhancedEntries: [], categories: [] };
|
|
559
597
|
}
|
|
560
|
-
const
|
|
598
|
+
const retryOpts = LLM_DEFAULTS.retry;
|
|
561
599
|
try {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const result = parsed.entries[i];
|
|
570
|
-
if (!result) return original;
|
|
571
|
-
return {
|
|
572
|
-
...original,
|
|
573
|
-
description: result.description || original.description,
|
|
574
|
-
scope: result.scope || original.scope
|
|
575
|
-
};
|
|
576
|
-
});
|
|
577
|
-
const categoryMap = /* @__PURE__ */ new Map();
|
|
578
|
-
for (let i = 0; i < parsed.entries.length; i++) {
|
|
579
|
-
const result = parsed.entries[i];
|
|
580
|
-
const category = result?.category || "General";
|
|
581
|
-
const entry = enhancedEntries[i];
|
|
582
|
-
if (!entry) continue;
|
|
583
|
-
if (!categoryMap.has(category)) {
|
|
584
|
-
categoryMap.set(category, []);
|
|
600
|
+
return await withRetry(async () => {
|
|
601
|
+
const prompt = buildPrompt(entries, context.categories, context.style);
|
|
602
|
+
const response = await provider.complete(prompt);
|
|
603
|
+
const cleaned = response.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "").trim();
|
|
604
|
+
const parsed = JSON.parse(cleaned);
|
|
605
|
+
if (!Array.isArray(parsed.entries)) {
|
|
606
|
+
throw new Error('Response missing "entries" array');
|
|
585
607
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
608
|
+
const enhancedEntries = entries.map((original, i) => {
|
|
609
|
+
const result = parsed.entries[i];
|
|
610
|
+
if (!result) return original;
|
|
611
|
+
return {
|
|
612
|
+
...original,
|
|
613
|
+
description: result.description || original.description,
|
|
614
|
+
scope: result.scope || original.scope
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
const categoryMap = /* @__PURE__ */ new Map();
|
|
618
|
+
for (let i = 0; i < parsed.entries.length; i++) {
|
|
619
|
+
const result = parsed.entries[i];
|
|
620
|
+
const category = result?.category || "General";
|
|
621
|
+
const entry = enhancedEntries[i];
|
|
622
|
+
if (!entry) continue;
|
|
623
|
+
if (!categoryMap.has(category)) {
|
|
624
|
+
categoryMap.set(category, []);
|
|
625
|
+
}
|
|
626
|
+
categoryMap.get(category).push(entry);
|
|
627
|
+
}
|
|
628
|
+
const categories = [];
|
|
629
|
+
for (const [category, catEntries] of categoryMap) {
|
|
630
|
+
categories.push({ category, entries: catEntries });
|
|
631
|
+
}
|
|
632
|
+
return { enhancedEntries, categories };
|
|
633
|
+
}, retryOpts);
|
|
593
634
|
} catch (error) {
|
|
594
|
-
(0, import_core4.warn)(
|
|
635
|
+
(0, import_core4.warn)(
|
|
636
|
+
`Combined enhance+categorize failed after ${retryOpts.maxAttempts} attempts: ${error instanceof Error ? error.message : String(error)}`
|
|
637
|
+
);
|
|
595
638
|
return {
|
|
596
639
|
enhancedEntries: entries,
|
|
597
640
|
categories: [{ category: "General", entries }]
|
|
@@ -1231,31 +1274,6 @@ function renderTemplate(templatePath, context, engine) {
|
|
|
1231
1274
|
return renderComposable(templatePath, context, engine);
|
|
1232
1275
|
}
|
|
1233
1276
|
|
|
1234
|
-
// src/utils/retry.ts
|
|
1235
|
-
function sleep(ms) {
|
|
1236
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1237
|
-
}
|
|
1238
|
-
async function withRetry(fn, options = {}) {
|
|
1239
|
-
const maxAttempts = options.maxAttempts ?? 3;
|
|
1240
|
-
const initialDelay = options.initialDelay ?? 1e3;
|
|
1241
|
-
const maxDelay = options.maxDelay ?? 3e4;
|
|
1242
|
-
const backoffFactor = options.backoffFactor ?? 2;
|
|
1243
|
-
let lastError;
|
|
1244
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1245
|
-
try {
|
|
1246
|
-
return await fn();
|
|
1247
|
-
} catch (error) {
|
|
1248
|
-
lastError = error;
|
|
1249
|
-
if (attempt < maxAttempts - 1) {
|
|
1250
|
-
const base = Math.min(initialDelay * backoffFactor ** attempt, maxDelay);
|
|
1251
|
-
const jitter = base * 0.2 * (Math.random() * 2 - 1);
|
|
1252
|
-
await sleep(Math.max(0, base + jitter));
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
throw lastError;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
1277
|
// src/core/pipeline.ts
|
|
1260
1278
|
var import_meta = {};
|
|
1261
1279
|
function generateCompareUrl(repoUrl, from, to) {
|
|
@@ -1325,7 +1343,7 @@ async function processWithLLM(context, config) {
|
|
|
1325
1343
|
const activeTasks = Object.entries(tasks).filter(([, enabled]) => enabled).map(([name]) => name);
|
|
1326
1344
|
(0, import_core8.info)(`Running LLM tasks: ${activeTasks.join(", ")}`);
|
|
1327
1345
|
if (tasks.enhance && tasks.categorize) {
|
|
1328
|
-
(0, import_core8.info)("Enhancing and categorizing entries with LLM
|
|
1346
|
+
(0, import_core8.info)("Enhancing and categorizing entries with LLM...");
|
|
1329
1347
|
const result = await enhanceAndCategorize(provider, context.entries, llmContext);
|
|
1330
1348
|
enhanced.entries = result.enhancedEntries;
|
|
1331
1349
|
enhanced.categories = {};
|
package/dist/index.js
CHANGED