@cullit/core 1.9.2 → 1.10.1

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/index.d.ts CHANGED
@@ -71,7 +71,7 @@ interface PipelineResult {
71
71
  duration: number;
72
72
  }
73
73
 
74
- declare const VERSION = "1.9.2";
74
+ declare const VERSION = "1.10.1";
75
75
  declare const DEFAULT_CATEGORIES: string[];
76
76
  declare const DEFAULT_MODELS: Record<string, string>;
77
77
  declare const AI_PROVIDERS: readonly ["anthropic", "openai", "gemini", "ollama", "openclaw", "none"];
@@ -159,6 +159,7 @@ declare function registerFormatter(format: string, fn: FormatterFn): void;
159
159
  declare function getFormatter(format: string): FormatterFn | undefined;
160
160
  declare function listFormatters(): string[];
161
161
  declare function formatNotes(notes: ReleaseNotes, format: OutputFormat): string;
162
+ declare function escapeHtml(str: string): string;
162
163
 
163
164
  /**
164
165
  * Outputs release notes to stdout (default).
@@ -281,7 +282,7 @@ declare function reportUsage(project?: string): Promise<void>;
281
282
  * Core registers: git collector, template generator, stdout/file publishers.
282
283
  * Pro registers: AI generator, Jira/Linear collectors + enrichers, Slack/Discord/GitHub publishers.
283
284
  *
284
- * The CLI calls `await import('@cullit/pro')` to load pro plugins if installed.
285
+ * Paid plugin registration is preloaded by the licensed distribution package.
285
286
  */
286
287
 
287
288
  type CollectorFactory = (...args: any[]) => Collector;
@@ -321,4 +322,4 @@ declare function runPipeline(from: string, to: string, config: CullConfig, optio
321
322
  templateProfile?: string;
322
323
  }): Promise<PipelineResult>;
323
324
 
324
- export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher, TONES, type TeamFeature, TemplateGenerator, type UsageLimits, VERSION, analyzeReleaseReadiness, createLogger, fetchWithTimeout, formatNotes, getCollector, getEnricher, getFeatureGating, getFormatter, getGenerator, getLatestTag, getPublisher, getRecentTags, getTierLimits, hasCollector, hasEnricher, hasGenerator, hasPublisher, isEnrichmentAllowed, isFeatureAllowed, isProviderAllowed, isPublisherAllowed, listCollectors, listEnrichers, listFormatters, listGenerators, listPublishers, registerCollector, registerEnricher, registerFormatter, registerGenerator, registerPublisher, reportUsage, resolveLicense, runPipeline, upgradeMessage, validateLicense };
325
+ export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher, TONES, type TeamFeature, TemplateGenerator, type UsageLimits, VERSION, analyzeReleaseReadiness, createLogger, escapeHtml, fetchWithTimeout, formatNotes, getCollector, getEnricher, getFeatureGating, getFormatter, getGenerator, getLatestTag, getPublisher, getRecentTags, getTierLimits, hasCollector, hasEnricher, hasGenerator, hasPublisher, isEnrichmentAllowed, isFeatureAllowed, isProviderAllowed, isPublisherAllowed, listCollectors, listEnrichers, listFormatters, listGenerators, listPublishers, registerCollector, registerEnricher, registerFormatter, registerGenerator, registerPublisher, reportUsage, resolveLicense, runPipeline, upgradeMessage, validateLicense };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/constants.ts
2
- var VERSION = "1.9.2";
2
+ var VERSION = "1.10.1";
3
3
  var DEFAULT_CATEGORIES = ["features", "fixes", "breaking", "improvements", "chores"];
4
4
  var DEFAULT_MODELS = {
5
5
  anthropic: "claude-sonnet-4-20250514",
@@ -72,7 +72,8 @@ var GitCollector = class {
72
72
  { cwd: this.cwd, encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
73
73
  );
74
74
  } catch (error) {
75
- const stderr = error?.stderr?.toString?.() || "";
75
+ const errWithStderr = typeof error === "object" && error !== null && "stderr" in error ? error : void 0;
76
+ const stderr = errWithStderr?.stderr?.toString?.() || "";
76
77
  const hint = stderr.includes("unknown revision") ? 'Check that both refs exist (run "cullit tags" to see tags).' : stderr.includes("not a git repository") ? "Run this command inside a git repository." : `Make sure both refs exist and you're in a git repository.`;
77
78
  throw new Error(
78
79
  `Failed to read git log between ${from} and ${to}. ${hint}`
@@ -280,9 +281,10 @@ var TemplateGenerator = class {
280
281
  const categoryOrder = ["breaking", "features", "improvements", "fixes", "chores", "other"];
281
282
  deduped.sort((a, b) => categoryOrder.indexOf(a.category) - categoryOrder.indexOf(b.category));
282
283
  const contributors = [...new Set(diff.commits.map((c) => c.author))];
284
+ const tagDate = diff.commits.length > 0 ? new Date(diff.commits[0].date).toISOString().split("T")[0] : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
283
285
  return {
284
286
  version: diff.to,
285
- date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
287
+ date: tagDate,
286
288
  summary: this.buildSummary(deduped, diff.commits.length, config.tone),
287
289
  changes: deduped.slice(0, 20),
288
290
  contributors,
@@ -417,15 +419,15 @@ function formatNotes(notes, format) {
417
419
  const fn = formatters.get(format) || formatters.get("markdown");
418
420
  return fn(notes);
419
421
  }
420
- function escapeMarkdown(text) {
422
+ function sanitizeForMarkdown(text) {
421
423
  return escapeHtml(text).replace(/\r?\n/g, " ");
422
424
  }
423
425
  function formatMarkdown(notes) {
424
426
  const lines = [];
425
- lines.push(`## ${escapeMarkdown(notes.version)} \u2014 ${escapeMarkdown(notes.date)}`);
427
+ lines.push(`## ${sanitizeForMarkdown(notes.version)} \u2014 ${sanitizeForMarkdown(notes.date)}`);
426
428
  lines.push("");
427
429
  if (notes.summary) {
428
- lines.push(escapeMarkdown(notes.summary));
430
+ lines.push(sanitizeForMarkdown(notes.summary));
429
431
  lines.push("");
430
432
  }
431
433
  const grouped = groupByCategory(notes);
@@ -435,8 +437,8 @@ function formatMarkdown(notes) {
435
437
  lines.push(`### ${CATEGORY_LABELS[category]}`);
436
438
  lines.push("");
437
439
  for (const entry of entries) {
438
- let line = `- ${escapeMarkdown(entry.description)}`;
439
- if (entry.ticketKey) line += ` (${escapeMarkdown(entry.ticketKey)})`;
440
+ let line = `- ${sanitizeForMarkdown(entry.description)}`;
441
+ if (entry.ticketKey) line += ` (${sanitizeForMarkdown(entry.ticketKey)})`;
440
442
  lines.push(line);
441
443
  }
442
444
  lines.push("");
@@ -444,7 +446,7 @@ function formatMarkdown(notes) {
444
446
  if (notes.contributors?.length) {
445
447
  lines.push("### Contributors");
446
448
  lines.push("");
447
- lines.push(notes.contributors.map((c) => `@${escapeMarkdown(c)}`).join(", "));
449
+ lines.push(notes.contributors.map((c) => `@${sanitizeForMarkdown(c)}`).join(", "));
448
450
  lines.push("");
449
451
  }
450
452
  if (notes.metadata) {
@@ -772,7 +774,7 @@ async function validateLicense() {
772
774
  if (cachedValidation && cachedValidation.key === key) {
773
775
  return cachedValidation.status;
774
776
  }
775
- return { tier: "pro", valid: true, message: "Offline validation \u2014 using cached license." };
777
+ return { tier: "free", valid: true, message: "License validation unavailable offline. Run while connected to activate your Pro license." };
776
778
  }
777
779
  }
778
780
  function isProviderAllowed(provider, license) {
@@ -977,7 +979,10 @@ async function runPipeline(from, to, config, options = {}) {
977
979
  const log = options.logger || createLogger("normal");
978
980
  const license = await validateLicense();
979
981
  if (!license.valid) {
980
- throw new Error(license.message || "Invalid CULLIT_API_KEY");
982
+ if (!isProviderAllowed(config.ai.provider, license)) {
983
+ throw new Error(license.message || "Invalid CULLIT_API_KEY");
984
+ }
985
+ log.warn(`\u26A0 ${license.message || "Invalid CULLIT_API_KEY \u2014 running in free mode."}`);
981
986
  }
982
987
  if (!isProviderAllowed(config.ai.provider, license)) {
983
988
  throw new Error(upgradeMessage(`AI provider "${config.ai.provider}"`));
@@ -985,7 +990,7 @@ async function runPipeline(from, to, config, options = {}) {
985
990
  const collectorFactory = getCollector(config.source.type);
986
991
  if (!collectorFactory) {
987
992
  throw new Error(
988
- `Source type "${config.source.type}" is not available. ` + (config.source.type !== "local" ? "Install @cullit/pro to use this source." : "Valid sources: local")
993
+ `Source type "${config.source.type}" is not available. ` + (config.source.type !== "local" ? "Install @cullit/licensed (private distribution) to use this source." : "Valid sources: local")
989
994
  );
990
995
  }
991
996
  const sourceLabel = config.source.type === "local" ? `commits between ${from}..${to}` : `items from ${config.source.type}`;
@@ -1007,7 +1012,7 @@ async function runPipeline(from, to, config, options = {}) {
1007
1012
  }
1008
1013
  const enricherFactory = getEnricher(source);
1009
1014
  if (!enricherFactory) {
1010
- log.info(`\xBB Skipping ${source} enrichment \u2014 install @cullit/pro to enable`);
1015
+ log.info(`\xBB Skipping ${source} enrichment \u2014 install @cullit/licensed to enable`);
1011
1016
  continue;
1012
1017
  }
1013
1018
  log.info(`\xBB Enriching from ${source}...`);
@@ -1031,7 +1036,7 @@ async function runPipeline(from, to, config, options = {}) {
1031
1036
  const generatorFactory = getGenerator(config.ai.provider);
1032
1037
  if (!generatorFactory) {
1033
1038
  throw new Error(
1034
- `AI provider "${config.ai.provider}" is not available. ` + (config.ai.provider !== "none" ? "Install @cullit/pro to use AI providers." : "")
1039
+ `AI provider "${config.ai.provider}" is not available. ` + (config.ai.provider !== "none" ? "Install @cullit/licensed (private distribution) to use AI providers." : "")
1035
1040
  );
1036
1041
  }
1037
1042
  let generator;
@@ -1061,7 +1066,7 @@ async function runPipeline(from, to, config, options = {}) {
1061
1066
  }
1062
1067
  const publisherFactory = getPublisher(target.type);
1063
1068
  if (!publisherFactory) {
1064
- log.info(`\xBB Skipping ${target.type} \u2014 install @cullit/pro to enable`);
1069
+ log.info(`\xBB Skipping ${target.type} \u2014 install @cullit/licensed to enable`);
1065
1070
  continue;
1066
1071
  }
1067
1072
  const publisher = publisherFactory(target);
@@ -1129,6 +1134,7 @@ export {
1129
1134
  VERSION,
1130
1135
  analyzeReleaseReadiness,
1131
1136
  createLogger,
1137
+ escapeHtml,
1132
1138
  fetchWithTimeout,
1133
1139
  formatNotes,
1134
1140
  getCollector,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cullit/core",
3
- "version": "1.9.2",
3
+ "version": "1.10.1",
4
4
  "type": "module",
5
5
  "description": "Core engine for Cullit — AI-powered release note generation.",
6
6
  "license": "MIT",
@@ -16,6 +16,10 @@
16
16
  "ai",
17
17
  "automation"
18
18
  ],
19
+ "homepage": "https://cullit.io",
20
+ "bugs": {
21
+ "url": "https://github.com/mttaylor/cullit/issues"
22
+ },
19
23
  "main": "./dist/index.js",
20
24
  "types": "./dist/index.d.ts",
21
25
  "exports": {
@@ -28,10 +32,13 @@
28
32
  "dist"
29
33
  ],
30
34
  "engines": {
31
- "node": ">=18"
35
+ "node": ">=22"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
32
39
  },
33
40
  "dependencies": {
34
- "@cullit/config": "1.9.2"
41
+ "@cullit/config": "1.10.1"
35
42
  },
36
43
  "scripts": {
37
44
  "build": "tsup src/index.ts --format esm --dts --clean",