@cullit/core 1.9.2 → 1.10.0

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.0";
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.0";
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}`
@@ -417,15 +418,15 @@ function formatNotes(notes, format) {
417
418
  const fn = formatters.get(format) || formatters.get("markdown");
418
419
  return fn(notes);
419
420
  }
420
- function escapeMarkdown(text) {
421
+ function sanitizeForMarkdown(text) {
421
422
  return escapeHtml(text).replace(/\r?\n/g, " ");
422
423
  }
423
424
  function formatMarkdown(notes) {
424
425
  const lines = [];
425
- lines.push(`## ${escapeMarkdown(notes.version)} \u2014 ${escapeMarkdown(notes.date)}`);
426
+ lines.push(`## ${sanitizeForMarkdown(notes.version)} \u2014 ${sanitizeForMarkdown(notes.date)}`);
426
427
  lines.push("");
427
428
  if (notes.summary) {
428
- lines.push(escapeMarkdown(notes.summary));
429
+ lines.push(sanitizeForMarkdown(notes.summary));
429
430
  lines.push("");
430
431
  }
431
432
  const grouped = groupByCategory(notes);
@@ -435,8 +436,8 @@ function formatMarkdown(notes) {
435
436
  lines.push(`### ${CATEGORY_LABELS[category]}`);
436
437
  lines.push("");
437
438
  for (const entry of entries) {
438
- let line = `- ${escapeMarkdown(entry.description)}`;
439
- if (entry.ticketKey) line += ` (${escapeMarkdown(entry.ticketKey)})`;
439
+ let line = `- ${sanitizeForMarkdown(entry.description)}`;
440
+ if (entry.ticketKey) line += ` (${sanitizeForMarkdown(entry.ticketKey)})`;
440
441
  lines.push(line);
441
442
  }
442
443
  lines.push("");
@@ -444,7 +445,7 @@ function formatMarkdown(notes) {
444
445
  if (notes.contributors?.length) {
445
446
  lines.push("### Contributors");
446
447
  lines.push("");
447
- lines.push(notes.contributors.map((c) => `@${escapeMarkdown(c)}`).join(", "));
448
+ lines.push(notes.contributors.map((c) => `@${sanitizeForMarkdown(c)}`).join(", "));
448
449
  lines.push("");
449
450
  }
450
451
  if (notes.metadata) {
@@ -772,7 +773,7 @@ async function validateLicense() {
772
773
  if (cachedValidation && cachedValidation.key === key) {
773
774
  return cachedValidation.status;
774
775
  }
775
- return { tier: "pro", valid: true, message: "Offline validation \u2014 using cached license." };
776
+ return { tier: "free", valid: true, message: "License validation unavailable offline. Run while connected to activate your Pro license." };
776
777
  }
777
778
  }
778
779
  function isProviderAllowed(provider, license) {
@@ -977,7 +978,10 @@ async function runPipeline(from, to, config, options = {}) {
977
978
  const log = options.logger || createLogger("normal");
978
979
  const license = await validateLicense();
979
980
  if (!license.valid) {
980
- throw new Error(license.message || "Invalid CULLIT_API_KEY");
981
+ if (!isProviderAllowed(config.ai.provider, license)) {
982
+ throw new Error(license.message || "Invalid CULLIT_API_KEY");
983
+ }
984
+ log.warn(`\u26A0 ${license.message || "Invalid CULLIT_API_KEY \u2014 running in free mode."}`);
981
985
  }
982
986
  if (!isProviderAllowed(config.ai.provider, license)) {
983
987
  throw new Error(upgradeMessage(`AI provider "${config.ai.provider}"`));
@@ -985,7 +989,7 @@ async function runPipeline(from, to, config, options = {}) {
985
989
  const collectorFactory = getCollector(config.source.type);
986
990
  if (!collectorFactory) {
987
991
  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")
992
+ `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
993
  );
990
994
  }
991
995
  const sourceLabel = config.source.type === "local" ? `commits between ${from}..${to}` : `items from ${config.source.type}`;
@@ -1007,7 +1011,7 @@ async function runPipeline(from, to, config, options = {}) {
1007
1011
  }
1008
1012
  const enricherFactory = getEnricher(source);
1009
1013
  if (!enricherFactory) {
1010
- log.info(`\xBB Skipping ${source} enrichment \u2014 install @cullit/pro to enable`);
1014
+ log.info(`\xBB Skipping ${source} enrichment \u2014 install @cullit/licensed to enable`);
1011
1015
  continue;
1012
1016
  }
1013
1017
  log.info(`\xBB Enriching from ${source}...`);
@@ -1031,7 +1035,7 @@ async function runPipeline(from, to, config, options = {}) {
1031
1035
  const generatorFactory = getGenerator(config.ai.provider);
1032
1036
  if (!generatorFactory) {
1033
1037
  throw new Error(
1034
- `AI provider "${config.ai.provider}" is not available. ` + (config.ai.provider !== "none" ? "Install @cullit/pro to use AI providers." : "")
1038
+ `AI provider "${config.ai.provider}" is not available. ` + (config.ai.provider !== "none" ? "Install @cullit/licensed (private distribution) to use AI providers." : "")
1035
1039
  );
1036
1040
  }
1037
1041
  let generator;
@@ -1061,7 +1065,7 @@ async function runPipeline(from, to, config, options = {}) {
1061
1065
  }
1062
1066
  const publisherFactory = getPublisher(target.type);
1063
1067
  if (!publisherFactory) {
1064
- log.info(`\xBB Skipping ${target.type} \u2014 install @cullit/pro to enable`);
1068
+ log.info(`\xBB Skipping ${target.type} \u2014 install @cullit/licensed to enable`);
1065
1069
  continue;
1066
1070
  }
1067
1071
  const publisher = publisherFactory(target);
@@ -1129,6 +1133,7 @@ export {
1129
1133
  VERSION,
1130
1134
  analyzeReleaseReadiness,
1131
1135
  createLogger,
1136
+ escapeHtml,
1132
1137
  fetchWithTimeout,
1133
1138
  formatNotes,
1134
1139
  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.0",
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.0"
35
42
  },
36
43
  "scripts": {
37
44
  "build": "tsup src/index.ts --format esm --dts --clean",