relizy 1.3.0-beta.2 → 1.3.0-beta.3

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/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import process from 'node:process';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { printBanner, logger } from '@maz-ui/node';
7
7
  import { Command } from 'commander';
8
- import { ag as isInCI, ah as getCIName, b as bump, c as changelog, g as publish, e as providerRelease, h as social, p as prComment, r as release } from './shared/relizy.CupzvopJ.mjs';
8
+ import { al as isInCI, am as getCIName, b as bump, c as changelog, g as publish, e as providerRelease, h as social, p as prComment, r as release } from './shared/relizy.BHnDdNQq.mjs';
9
9
  import '@maz-ui/utils';
10
10
  import 'c12';
11
11
  import 'changelogen';
@@ -124,7 +124,7 @@ program.command("publish").description("Publish packages to registry").option("-
124
124
  process.exit(1);
125
125
  }
126
126
  });
127
- program.command("provider-release").description("Publish release to git provider (github or gitlab)").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").option("--token <token>", "Provider token").option("--provider <provider>", "Git provider (github or gitlab)").action(async (options) => {
127
+ program.command("provider-release").description("Publish release to git provider (github or gitlab)").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").option("--token <token>", "Provider token").option("--provider <provider>", "Git provider (github or gitlab)").option("--ai", "Force-enable AI for release notes").option("--no-ai", "Force-disable AI for release notes").action(async (options) => {
128
128
  try {
129
129
  await providerRelease({
130
130
  token: options.token,
@@ -134,14 +134,15 @@ program.command("provider-release").description("Publish release to git provider
134
134
  dryRun: program.opts().dryRun,
135
135
  logLevel: program.opts().logLevel,
136
136
  configName: program.opts().config,
137
- safetyCheck: hasCliFlag("--no-safety-check") ? false : void 0
137
+ safetyCheck: hasCliFlag("--no-safety-check") ? false : void 0,
138
+ ai: hasCliFlag("--ai") ? true : hasCliFlag("--no-ai") ? false : void 0
138
139
  });
139
140
  } catch (error) {
140
141
  logger.error("Failed to publish release -", error);
141
142
  process.exit(1);
142
143
  }
143
144
  });
144
- program.command("social").description("Post release announcements to social media platforms").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").action(async (options) => {
145
+ program.command("social").description("Post release announcements to social media platforms").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").option("--ai", "Force-enable AI for social posts").option("--no-ai", "Force-disable AI for social posts").action(async (options) => {
145
146
  try {
146
147
  await social({
147
148
  from: options.from,
@@ -149,7 +150,8 @@ program.command("social").description("Post release announcements to social medi
149
150
  dryRun: program.opts().dryRun,
150
151
  logLevel: program.opts().logLevel,
151
152
  configName: program.opts().config,
152
- safetyCheck: hasCliFlag("--no-safety-check") ? false : void 0
153
+ safetyCheck: hasCliFlag("--no-safety-check") ? false : void 0,
154
+ ai: hasCliFlag("--ai") ? true : hasCliFlag("--no-ai") ? false : void 0
153
155
  });
154
156
  } catch {
155
157
  process.exit(1);
@@ -168,7 +170,7 @@ program.command("pr-comment").description("Post or re-post a PR comment with rel
168
170
  process.exit(1);
169
171
  }
170
172
  });
171
- program.command("release").description("Complete release workflow (bump + changelog + commit + tag + push to remote + publish release)").option("--major", "Bump major version").option("--minor", "Bump minor version").option("--patch", "Bump patch version").option("--prerelease", "Bump prerelease version").option("--premajor", "Bump premajor version").option("--preminor", "Bump preminor version").option("--prepatch", "Bump prepatch version").option("--preid <id>", "Prerelease identifier (alpha, beta, rc, etc.)").option("--suffix <suffix>", "Custom suffix for prerelease versions - replace the last .X with .suffix (e.g. 1.0.0-beta.0 -> 1.0.0-beta.suffix)").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").option("--no-push", "Skip push changes and tags to remote").option("--no-provider-release", "Skip release creation (GitHub/GitLab)").option("--no-publish", "Skip npm publish").option("--registry <url>", "Custom npm registry URL").option("--tag <tag>", "Publish with specific tag (default: latest for stable, next for prerelease)").option("--access <type>", "Package access level (public or restricted)").option("--otp <code>", "One-time password for 2FA").option("--no-verify", "Skip git hooks during commit").option("--format-cmd <cmd>", 'Command to format CHANGELOG files after generation (e.g. "pnpm lint")').option("--build-cmd <cmd>", 'Command to build packages before publish (e.g. "pnpm build")').option("--no-root-changelog", "Skip generation of root changelog file").option("--token <token>", "Git token (github or gitlab)").option("--force", "Bump even if there are no commits").option("--no-clean", "Skip check if the working directory is clean").option("--no-commit", "Skip commit and tag").option("--no-git-tag", "Skip tag creation").option("--no-changelog", "Skip changelog generation files").option("--provider <provider>", "Git provider (github or gitlab)").option("--no-social", "Skip social media posting").option("--no-pr-comment", "Skip PR comment posting").option("--yes", "Skip confirmation prompt about bumping packages").option("--publish-token <token>", 'NPM token (e.g. "123456") - only supported for pnpm and npm').option("--canary", "Publish a canary release from the current commit").option("--include-private", "Include private packages in bump and changelog phases").action(async (options) => {
173
+ program.command("release").description("Complete release workflow (bump + changelog + commit + tag + push to remote + publish release)").option("--major", "Bump major version").option("--minor", "Bump minor version").option("--patch", "Bump patch version").option("--prerelease", "Bump prerelease version").option("--premajor", "Bump premajor version").option("--preminor", "Bump preminor version").option("--prepatch", "Bump prepatch version").option("--preid <id>", "Prerelease identifier (alpha, beta, rc, etc.)").option("--suffix <suffix>", "Custom suffix for prerelease versions - replace the last .X with .suffix (e.g. 1.0.0-beta.0 -> 1.0.0-beta.suffix)").option("--from <ref>", "Start commit reference").option("--to <ref>", "End commit reference").option("--no-push", "Skip push changes and tags to remote").option("--no-provider-release", "Skip release creation (GitHub/GitLab)").option("--no-publish", "Skip npm publish").option("--registry <url>", "Custom npm registry URL").option("--tag <tag>", "Publish with specific tag (default: latest for stable, next for prerelease)").option("--access <type>", "Package access level (public or restricted)").option("--otp <code>", "One-time password for 2FA").option("--no-verify", "Skip git hooks during commit").option("--format-cmd <cmd>", 'Command to format CHANGELOG files after generation (e.g. "pnpm lint")').option("--build-cmd <cmd>", 'Command to build packages before publish (e.g. "pnpm build")').option("--no-root-changelog", "Skip generation of root changelog file").option("--token <token>", "Git token (github or gitlab)").option("--force", "Bump even if there are no commits").option("--no-clean", "Skip check if the working directory is clean").option("--no-commit", "Skip commit and tag").option("--no-git-tag", "Skip tag creation").option("--no-changelog", "Skip changelog generation files").option("--provider <provider>", "Git provider (github or gitlab)").option("--no-social", "Skip social media posting").option("--no-pr-comment", "Skip PR comment posting").option("--yes", "Skip confirmation prompt about bumping packages").option("--publish-token <token>", 'NPM token (e.g. "123456") - only supported for pnpm and npm').option("--canary", "Publish a canary release from the current commit").option("--include-private", "Include private packages in bump and changelog phases").option("--ai", "Force-enable AI for release notes and social posts").option("--no-ai", "Force-disable AI for release notes and social posts").action(async (options) => {
172
174
  try {
173
175
  await release({
174
176
  type: getReleaseType(options),
@@ -203,7 +205,8 @@ program.command("release").description("Complete release workflow (bump + change
203
205
  safetyCheck: hasCliFlag("--no-safety-check") ? false : void 0,
204
206
  social: hasCliFlag("--no-social") ? false : void 0,
205
207
  prComment: hasCliFlag("--no-pr-comment") ? false : void 0,
206
- prNumber: program.opts().prNumber
208
+ prNumber: program.opts().prNumber,
209
+ ai: hasCliFlag("--ai") ? true : hasCliFlag("--no-ai") ? false : void 0
207
210
  });
208
211
  } catch (error) {
209
212
  logger.error("Failed to release -", error);
package/dist/index.d.mts CHANGED
@@ -42,6 +42,12 @@ declare function getDefaultConfig(): {
42
42
  accessTokenSecret: string | undefined;
43
43
  };
44
44
  slack: string | undefined;
45
+ ai: {
46
+ 'claude-code': {
47
+ apiKey: string | undefined;
48
+ oauthToken: string | undefined;
49
+ };
50
+ };
45
51
  };
46
52
  scopeMap: {};
47
53
  release: Required<ReleaseConfig>;
@@ -57,6 +63,7 @@ declare function getDefaultConfig(): {
57
63
  };
58
64
  };
59
65
  prComment: Required<PrCommentConfig>;
66
+ ai: AIConfig;
60
67
  logLevel: LogLevel;
61
68
  safetyCheck: boolean;
62
69
  };
@@ -205,6 +212,40 @@ declare function createGitlabRelease({ config, release, dryRun, }: {
205
212
  }): Promise<GitlabReleaseResponse>;
206
213
  declare function gitlab(options?: Partial<ProviderReleaseOptions>): Promise<PostedRelease[]>;
207
214
 
215
+ interface Reference {
216
+ type: 'hash' | 'issue' | 'pull-request';
217
+ value: string;
218
+ }
219
+ declare function buildCompareLink({ config, from, to, isFirstCommit }: {
220
+ config: ResolvedRelizyConfig;
221
+ from: string;
222
+ to: string;
223
+ isFirstCommit: boolean;
224
+ }): string;
225
+ declare function buildChangelogBody({ commits, config, minify }: {
226
+ commits: GitCommit[];
227
+ config: ResolvedRelizyConfig;
228
+ minify?: boolean;
229
+ }): string;
230
+ declare function buildContributors({ commits, config }: {
231
+ commits: GitCommit[];
232
+ config: ResolvedRelizyConfig;
233
+ }): Promise<string>;
234
+ declare function generateMarkDown({ commits, config, from, to, isFirstCommit, minify, }: {
235
+ commits: GitCommit[];
236
+ config: ResolvedRelizyConfig;
237
+ from: string;
238
+ to: string;
239
+ isFirstCommit: boolean;
240
+ minify?: boolean;
241
+ }): Promise<string>;
242
+ declare function parseChangelogMarkdown(contents: string): {
243
+ releases: {
244
+ version?: string;
245
+ body: string;
246
+ }[];
247
+ };
248
+
208
249
  declare function detectPackageManager(cwd?: string): PackageManager;
209
250
  declare function determinePublishTag(version: string, configTag?: string): string;
210
251
  declare function getPackagesToPublishInSelectiveMode(sortedPackages: PackageBase[], rootVersion: string | undefined): PackageBase[];
@@ -937,6 +978,11 @@ interface ProviderReleaseOptions {
937
978
  * Custom suffix for prerelease versions - replace the last .X with .suffix (e.g. 1.0.0-beta.0 -> 1.0.0-beta.suffix)
938
979
  */
939
980
  suffix?: string;
981
+ /**
982
+ * Override AI configuration for this run
983
+ * true = force-enable AI, false = force-disable AI, undefined = use config
984
+ */
985
+ ai?: boolean;
940
986
  }
941
987
  interface SocialOptions {
942
988
  /**
@@ -974,6 +1020,11 @@ interface SocialOptions {
974
1020
  * @default true
975
1021
  */
976
1022
  safetyCheck?: boolean;
1023
+ /**
1024
+ * Override AI configuration for this run
1025
+ * true = force-enable AI, false = force-disable AI, undefined = use config
1026
+ */
1027
+ ai?: boolean;
977
1028
  }
978
1029
  type PublishConfig = ChangelogConfig$1['publish'] & {
979
1030
  /**
@@ -1158,6 +1209,11 @@ interface ReleaseOptions extends ReleaseConfig, BumpConfig, ChangelogConfig, Pub
1158
1209
  * @default false
1159
1210
  */
1160
1211
  includePrivates?: boolean;
1212
+ /**
1213
+ * Override AI configuration for this run
1214
+ * true = force-enable AI, false = force-disable AI, undefined = use config
1215
+ */
1216
+ ai?: boolean;
1161
1217
  }
1162
1218
  interface TwitterCredentials {
1163
1219
  /**
@@ -1238,6 +1294,86 @@ interface SlackSocialConfig {
1238
1294
  */
1239
1295
  credentials?: SlackCredentials;
1240
1296
  }
1297
+ type AIProviderName = 'claude-code';
1298
+ interface ClaudeCodeProviderOptions {
1299
+ /**
1300
+ * Anthropic API key.
1301
+ * Fallback env: `RELIZY_ANTHROPIC_API_KEY`, `ANTHROPIC_API_KEY`.
1302
+ */
1303
+ apiKey?: string;
1304
+ /**
1305
+ * Claude Code OAuth token.
1306
+ * Fallback env: `RELIZY_CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`.
1307
+ */
1308
+ oauthToken?: string;
1309
+ /**
1310
+ * Model id or alias (e.g. `haiku`, `sonnet`, `opus`, or a versioned id).
1311
+ * @default 'haiku'
1312
+ */
1313
+ model?: string;
1314
+ }
1315
+ type AIPromptTarget = 'providerRelease' | 'twitter' | 'slack';
1316
+ type AISystemPromptOverrides = Partial<Record<AIPromptTarget, string>>;
1317
+ interface AITargetConfig {
1318
+ /**
1319
+ * Enable AI for this target
1320
+ * @default false
1321
+ */
1322
+ enabled?: boolean;
1323
+ }
1324
+ interface AISocialConfig {
1325
+ /**
1326
+ * Twitter-specific AI activation
1327
+ */
1328
+ twitter?: AITargetConfig;
1329
+ /**
1330
+ * Slack-specific AI activation
1331
+ */
1332
+ slack?: AITargetConfig;
1333
+ }
1334
+ interface AIConfig {
1335
+ /**
1336
+ * AI provider name
1337
+ * @default 'claude-code'
1338
+ */
1339
+ provider?: AIProviderName;
1340
+ /**
1341
+ * Provider-specific options, keyed by provider name.
1342
+ */
1343
+ providers?: {
1344
+ 'claude-code'?: ClaudeCodeProviderOptions;
1345
+ };
1346
+ /**
1347
+ * Output language (ISO 639-1 code or plain English name).
1348
+ * @default 'en'
1349
+ */
1350
+ language?: string;
1351
+ /**
1352
+ * Behavior when the provider call fails.
1353
+ * - `raw`: fall back to the unmodified changelog body.
1354
+ * - `fail`: re-throw the error.
1355
+ * @default 'raw'
1356
+ */
1357
+ fallback?: 'raw' | 'fail';
1358
+ /**
1359
+ * Extra directives appended to every built-in system prompt.
1360
+ * @example 'Always use emojis in the changelog'
1361
+ */
1362
+ extraGuidelines?: string;
1363
+ /**
1364
+ * Full replacement of the built-in system prompts.
1365
+ * When set, the extraGuidelines and base prompts are ignored for that target.
1366
+ */
1367
+ systemPromptOverrides?: AISystemPromptOverrides;
1368
+ /**
1369
+ * Enable AI rewriting for GitHub/GitLab release notes.
1370
+ */
1371
+ providerRelease?: AITargetConfig;
1372
+ /**
1373
+ * Enable AI rewriting for social media posts.
1374
+ */
1375
+ social?: AISocialConfig;
1376
+ }
1241
1377
  interface SocialConfig {
1242
1378
  /**
1243
1379
  * Twitter configuration
@@ -1453,6 +1589,15 @@ interface TokensConfig {
1453
1589
  * Environment variables: SLACK_TOKEN, RELIZY_SLACK_TOKEN
1454
1590
  */
1455
1591
  slack?: string;
1592
+ /**
1593
+ * AI provider credentials
1594
+ */
1595
+ ai?: {
1596
+ 'claude-code'?: {
1597
+ apiKey?: string;
1598
+ oauthToken?: string;
1599
+ };
1600
+ };
1456
1601
  }
1457
1602
  /**
1458
1603
  * Hooks configuration
@@ -1533,6 +1678,10 @@ interface RelizyConfig extends Partial<Omit<ChangelogConfig$1, 'output' | 'templ
1533
1678
  * API tokens configuration
1534
1679
  */
1535
1680
  tokens?: TokensConfig;
1681
+ /**
1682
+ * AI-powered changelog and release notes configuration
1683
+ */
1684
+ ai?: AIConfig;
1536
1685
  /**
1537
1686
  * Hooks config
1538
1687
  */
@@ -1582,7 +1731,7 @@ declare function prComment(options?: PrCommentOptions): Promise<boolean>;
1582
1731
  declare function providerReleaseSafetyCheck({ config, provider }: {
1583
1732
  config: ResolvedRelizyConfig;
1584
1733
  provider?: GitProvider | null;
1585
- }): void;
1734
+ }): Promise<void>;
1586
1735
  declare function providerRelease(options?: Partial<ProviderReleaseOptions>): Promise<ProviderReleaseResult>;
1587
1736
 
1588
1737
  declare function publishSafetyCheck({ config }: {
@@ -1599,5 +1748,5 @@ declare function socialSafetyCheck({ config }: {
1599
1748
  }): Promise<void>;
1600
1749
  declare function social(options?: Partial<SocialOptions>): Promise<SocialResult>;
1601
1750
 
1602
- export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildCommentBody, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1603
- export type { BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
1751
+ export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1752
+ export type { AIConfig, AIPromptTarget, AIProviderName, AISocialConfig, AISystemPromptOverrides, AITargetConfig, BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ClaudeCodeProviderOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, Reference, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
package/dist/index.d.ts CHANGED
@@ -42,6 +42,12 @@ declare function getDefaultConfig(): {
42
42
  accessTokenSecret: string | undefined;
43
43
  };
44
44
  slack: string | undefined;
45
+ ai: {
46
+ 'claude-code': {
47
+ apiKey: string | undefined;
48
+ oauthToken: string | undefined;
49
+ };
50
+ };
45
51
  };
46
52
  scopeMap: {};
47
53
  release: Required<ReleaseConfig>;
@@ -57,6 +63,7 @@ declare function getDefaultConfig(): {
57
63
  };
58
64
  };
59
65
  prComment: Required<PrCommentConfig>;
66
+ ai: AIConfig;
60
67
  logLevel: LogLevel;
61
68
  safetyCheck: boolean;
62
69
  };
@@ -205,6 +212,40 @@ declare function createGitlabRelease({ config, release, dryRun, }: {
205
212
  }): Promise<GitlabReleaseResponse>;
206
213
  declare function gitlab(options?: Partial<ProviderReleaseOptions>): Promise<PostedRelease[]>;
207
214
 
215
+ interface Reference {
216
+ type: 'hash' | 'issue' | 'pull-request';
217
+ value: string;
218
+ }
219
+ declare function buildCompareLink({ config, from, to, isFirstCommit }: {
220
+ config: ResolvedRelizyConfig;
221
+ from: string;
222
+ to: string;
223
+ isFirstCommit: boolean;
224
+ }): string;
225
+ declare function buildChangelogBody({ commits, config, minify }: {
226
+ commits: GitCommit[];
227
+ config: ResolvedRelizyConfig;
228
+ minify?: boolean;
229
+ }): string;
230
+ declare function buildContributors({ commits, config }: {
231
+ commits: GitCommit[];
232
+ config: ResolvedRelizyConfig;
233
+ }): Promise<string>;
234
+ declare function generateMarkDown({ commits, config, from, to, isFirstCommit, minify, }: {
235
+ commits: GitCommit[];
236
+ config: ResolvedRelizyConfig;
237
+ from: string;
238
+ to: string;
239
+ isFirstCommit: boolean;
240
+ minify?: boolean;
241
+ }): Promise<string>;
242
+ declare function parseChangelogMarkdown(contents: string): {
243
+ releases: {
244
+ version?: string;
245
+ body: string;
246
+ }[];
247
+ };
248
+
208
249
  declare function detectPackageManager(cwd?: string): PackageManager;
209
250
  declare function determinePublishTag(version: string, configTag?: string): string;
210
251
  declare function getPackagesToPublishInSelectiveMode(sortedPackages: PackageBase[], rootVersion: string | undefined): PackageBase[];
@@ -937,6 +978,11 @@ interface ProviderReleaseOptions {
937
978
  * Custom suffix for prerelease versions - replace the last .X with .suffix (e.g. 1.0.0-beta.0 -> 1.0.0-beta.suffix)
938
979
  */
939
980
  suffix?: string;
981
+ /**
982
+ * Override AI configuration for this run
983
+ * true = force-enable AI, false = force-disable AI, undefined = use config
984
+ */
985
+ ai?: boolean;
940
986
  }
941
987
  interface SocialOptions {
942
988
  /**
@@ -974,6 +1020,11 @@ interface SocialOptions {
974
1020
  * @default true
975
1021
  */
976
1022
  safetyCheck?: boolean;
1023
+ /**
1024
+ * Override AI configuration for this run
1025
+ * true = force-enable AI, false = force-disable AI, undefined = use config
1026
+ */
1027
+ ai?: boolean;
977
1028
  }
978
1029
  type PublishConfig = ChangelogConfig$1['publish'] & {
979
1030
  /**
@@ -1158,6 +1209,11 @@ interface ReleaseOptions extends ReleaseConfig, BumpConfig, ChangelogConfig, Pub
1158
1209
  * @default false
1159
1210
  */
1160
1211
  includePrivates?: boolean;
1212
+ /**
1213
+ * Override AI configuration for this run
1214
+ * true = force-enable AI, false = force-disable AI, undefined = use config
1215
+ */
1216
+ ai?: boolean;
1161
1217
  }
1162
1218
  interface TwitterCredentials {
1163
1219
  /**
@@ -1238,6 +1294,86 @@ interface SlackSocialConfig {
1238
1294
  */
1239
1295
  credentials?: SlackCredentials;
1240
1296
  }
1297
+ type AIProviderName = 'claude-code';
1298
+ interface ClaudeCodeProviderOptions {
1299
+ /**
1300
+ * Anthropic API key.
1301
+ * Fallback env: `RELIZY_ANTHROPIC_API_KEY`, `ANTHROPIC_API_KEY`.
1302
+ */
1303
+ apiKey?: string;
1304
+ /**
1305
+ * Claude Code OAuth token.
1306
+ * Fallback env: `RELIZY_CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`.
1307
+ */
1308
+ oauthToken?: string;
1309
+ /**
1310
+ * Model id or alias (e.g. `haiku`, `sonnet`, `opus`, or a versioned id).
1311
+ * @default 'haiku'
1312
+ */
1313
+ model?: string;
1314
+ }
1315
+ type AIPromptTarget = 'providerRelease' | 'twitter' | 'slack';
1316
+ type AISystemPromptOverrides = Partial<Record<AIPromptTarget, string>>;
1317
+ interface AITargetConfig {
1318
+ /**
1319
+ * Enable AI for this target
1320
+ * @default false
1321
+ */
1322
+ enabled?: boolean;
1323
+ }
1324
+ interface AISocialConfig {
1325
+ /**
1326
+ * Twitter-specific AI activation
1327
+ */
1328
+ twitter?: AITargetConfig;
1329
+ /**
1330
+ * Slack-specific AI activation
1331
+ */
1332
+ slack?: AITargetConfig;
1333
+ }
1334
+ interface AIConfig {
1335
+ /**
1336
+ * AI provider name
1337
+ * @default 'claude-code'
1338
+ */
1339
+ provider?: AIProviderName;
1340
+ /**
1341
+ * Provider-specific options, keyed by provider name.
1342
+ */
1343
+ providers?: {
1344
+ 'claude-code'?: ClaudeCodeProviderOptions;
1345
+ };
1346
+ /**
1347
+ * Output language (ISO 639-1 code or plain English name).
1348
+ * @default 'en'
1349
+ */
1350
+ language?: string;
1351
+ /**
1352
+ * Behavior when the provider call fails.
1353
+ * - `raw`: fall back to the unmodified changelog body.
1354
+ * - `fail`: re-throw the error.
1355
+ * @default 'raw'
1356
+ */
1357
+ fallback?: 'raw' | 'fail';
1358
+ /**
1359
+ * Extra directives appended to every built-in system prompt.
1360
+ * @example 'Always use emojis in the changelog'
1361
+ */
1362
+ extraGuidelines?: string;
1363
+ /**
1364
+ * Full replacement of the built-in system prompts.
1365
+ * When set, the extraGuidelines and base prompts are ignored for that target.
1366
+ */
1367
+ systemPromptOverrides?: AISystemPromptOverrides;
1368
+ /**
1369
+ * Enable AI rewriting for GitHub/GitLab release notes.
1370
+ */
1371
+ providerRelease?: AITargetConfig;
1372
+ /**
1373
+ * Enable AI rewriting for social media posts.
1374
+ */
1375
+ social?: AISocialConfig;
1376
+ }
1241
1377
  interface SocialConfig {
1242
1378
  /**
1243
1379
  * Twitter configuration
@@ -1453,6 +1589,15 @@ interface TokensConfig {
1453
1589
  * Environment variables: SLACK_TOKEN, RELIZY_SLACK_TOKEN
1454
1590
  */
1455
1591
  slack?: string;
1592
+ /**
1593
+ * AI provider credentials
1594
+ */
1595
+ ai?: {
1596
+ 'claude-code'?: {
1597
+ apiKey?: string;
1598
+ oauthToken?: string;
1599
+ };
1600
+ };
1456
1601
  }
1457
1602
  /**
1458
1603
  * Hooks configuration
@@ -1533,6 +1678,10 @@ interface RelizyConfig extends Partial<Omit<ChangelogConfig$1, 'output' | 'templ
1533
1678
  * API tokens configuration
1534
1679
  */
1535
1680
  tokens?: TokensConfig;
1681
+ /**
1682
+ * AI-powered changelog and release notes configuration
1683
+ */
1684
+ ai?: AIConfig;
1536
1685
  /**
1537
1686
  * Hooks config
1538
1687
  */
@@ -1582,7 +1731,7 @@ declare function prComment(options?: PrCommentOptions): Promise<boolean>;
1582
1731
  declare function providerReleaseSafetyCheck({ config, provider }: {
1583
1732
  config: ResolvedRelizyConfig;
1584
1733
  provider?: GitProvider | null;
1585
- }): void;
1734
+ }): Promise<void>;
1586
1735
  declare function providerRelease(options?: Partial<ProviderReleaseOptions>): Promise<ProviderReleaseResult>;
1587
1736
 
1588
1737
  declare function publishSafetyCheck({ config }: {
@@ -1599,5 +1748,5 @@ declare function socialSafetyCheck({ config }: {
1599
1748
  }): Promise<void>;
1600
1749
  declare function social(options?: Partial<SocialOptions>): Promise<SocialResult>;
1601
1750
 
1602
- export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildCommentBody, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1603
- export type { BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
1751
+ export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1752
+ export type { AIConfig, AIPromptTarget, AIProviderName, AISocialConfig, AISystemPromptOverrides, AITargetConfig, BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ClaudeCodeProviderOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, Reference, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { aa as NEW_PACKAGE_MARKER, T as PR_COMMENT_MARKER, a as buildCommentBody, b as bump, ao as capReleaseTypeForZeroMajor, c as changelog, u as checkGitStatusIfDirty, aC as confirmBump, A as createCommitAndTags, I as createGitlabRelease, k as defineConfig, x as detectGitProvider, K as detectPackageManager, S as detectPullRequest, L as determinePublishTag, aq as determineReleaseType, ap as determineSemverChange, aj as executeBuildCmd, ai as executeFormatCmd, af as executeHook, o as expandPackagesToBumpWithDependents, a3 as extractChangelogSummary, au as extractVersionFromPackageTag, aF as extractVersionFromTag, v as fetchGitTags, al as filterOutPrivatePackages, Q as findGitHubPR, R as findGitLabMR, a0 as formatChangelogForSlack, a1 as formatSlackMessage, ad as formatTweetMessage, i as generateChangelog, O as getAuthCommand, aD as getBumpedIndependentPackages, aB as getBumpedPackageIndependently, ah as getCIName, aG as getCanaryVersion, E as getCurrentGitBranch, F as getCurrentGitRef, j as getDefaultConfig, n as getDependentsOf, D as getFirstCommit, q as getGitStatus, a5 as getIndependentTag, a9 as getLastPackageTag, a8 as getLastRepoTag, a6 as getLastStableTag, a7 as getLastTag, z as getModifiedReleaseFilePatterns, Z as getPackageCommits, m as getPackageDependencies, as as getPackageNewVersion, Y as getPackages, am as getPackagesOrBumpedPackages, N as getPackagesToPublishInIndependentMode, M as getPackagesToPublishInSelectiveMode, az as getPreid, a4 as getReleaseUrl, W as getRootPackage, G as getShortCommitSha, $ as getSlackToken, ac as getTwitterCredentials, H as github, J as gitlab, _ as hasLernaJson, ak as isBumpedPackage, aA as isChangedPreid, ay as isGraduating, an as isGraduatingToStableBetweenVersion, ag as isInCI, av as isPrerelease, ax as isPrereleaseReleaseType, aw as isStableReleaseType, aH as isTagVersionCompatibleWithCurrent, l as loadRelizyConfig, y as parseGitRemoteUrl, U as postPrComment, a2 as postReleaseToSlack, ae as postReleaseToTwitter, p as prComment, e as providerRelease, d as providerReleaseSafetyCheck, g as publish, P as publishPackage, f as publishSafetyCheck, B as pushCommitAndTags, V as readPackageJson, X as readPackages, r as release, ab as resolveTags, C as rollbackModifiedFiles, aE as shouldFilterPrereleaseTags, h as social, s as socialSafetyCheck, t as topologicalSort, at as updateLernaVersion, w as writeChangelogToFile, ar as writeVersion } from './shared/relizy.CupzvopJ.mjs';
1
+ export { af as NEW_PACKAGE_MARKER, Y as PR_COMMENT_MARKER, L as buildChangelogBody, a as buildCommentBody, K as buildCompareLink, M as buildContributors, b as bump, at as capReleaseTypeForZeroMajor, c as changelog, u as checkGitStatusIfDirty, aH as confirmBump, A as createCommitAndTags, I as createGitlabRelease, k as defineConfig, x as detectGitProvider, P as detectPackageManager, X as detectPullRequest, Q as determinePublishTag, av as determineReleaseType, au as determineSemverChange, ao as executeBuildCmd, an as executeFormatCmd, ak as executeHook, o as expandPackagesToBumpWithDependents, a8 as extractChangelogSummary, az as extractVersionFromPackageTag, aK as extractVersionFromTag, v as fetchGitTags, aq as filterOutPrivatePackages, V as findGitHubPR, W as findGitLabMR, a5 as formatChangelogForSlack, a6 as formatSlackMessage, ai as formatTweetMessage, i as generateChangelog, N as generateMarkDown, T as getAuthCommand, aI as getBumpedIndependentPackages, aG as getBumpedPackageIndependently, am as getCIName, aL as getCanaryVersion, E as getCurrentGitBranch, F as getCurrentGitRef, j as getDefaultConfig, n as getDependentsOf, D as getFirstCommit, q as getGitStatus, aa as getIndependentTag, ae as getLastPackageTag, ad as getLastRepoTag, ab as getLastStableTag, ac as getLastTag, z as getModifiedReleaseFilePatterns, a2 as getPackageCommits, m as getPackageDependencies, ax as getPackageNewVersion, a1 as getPackages, ar as getPackagesOrBumpedPackages, S as getPackagesToPublishInIndependentMode, R as getPackagesToPublishInSelectiveMode, aE as getPreid, a9 as getReleaseUrl, $ as getRootPackage, G as getShortCommitSha, a4 as getSlackToken, ah as getTwitterCredentials, H as github, J as gitlab, a3 as hasLernaJson, ap as isBumpedPackage, aF as isChangedPreid, aD as isGraduating, as as isGraduatingToStableBetweenVersion, al as isInCI, aA as isPrerelease, aC as isPrereleaseReleaseType, aB as isStableReleaseType, aM as isTagVersionCompatibleWithCurrent, l as loadRelizyConfig, O as parseChangelogMarkdown, y as parseGitRemoteUrl, Z as postPrComment, a7 as postReleaseToSlack, aj as postReleaseToTwitter, p as prComment, e as providerRelease, d as providerReleaseSafetyCheck, g as publish, U as publishPackage, f as publishSafetyCheck, B as pushCommitAndTags, _ as readPackageJson, a0 as readPackages, r as release, ag as resolveTags, C as rollbackModifiedFiles, aJ as shouldFilterPrereleaseTags, h as social, s as socialSafetyCheck, t as topologicalSort, ay as updateLernaVersion, w as writeChangelogToFile, aw as writeVersion } from './shared/relizy.BHnDdNQq.mjs';
2
2
  import '@maz-ui/node';
3
3
  import 'node:process';
4
4
  import '@maz-ui/utils';
@@ -1885,7 +1885,13 @@ function getDefaultConfig() {
1885
1885
  accessToken: process$1.env.RELIZY_TWITTER_ACCESS_TOKEN || process$1.env.TWITTER_ACCESS_TOKEN,
1886
1886
  accessTokenSecret: process$1.env.RELIZY_TWITTER_ACCESS_TOKEN_SECRET || process$1.env.TWITTER_ACCESS_TOKEN_SECRET
1887
1887
  },
1888
- slack: process$1.env.RELIZY_SLACK_TOKEN || process$1.env.SLACK_TOKEN
1888
+ slack: process$1.env.RELIZY_SLACK_TOKEN || process$1.env.SLACK_TOKEN,
1889
+ ai: {
1890
+ "claude-code": {
1891
+ apiKey: process$1.env.RELIZY_ANTHROPIC_API_KEY || process$1.env.ANTHROPIC_API_KEY,
1892
+ oauthToken: process$1.env.RELIZY_CLAUDE_CODE_OAUTH_TOKEN || process$1.env.CLAUDE_CODE_OAUTH_TOKEN
1893
+ }
1894
+ }
1889
1895
  },
1890
1896
  scopeMap: {},
1891
1897
  release: {
@@ -1914,6 +1920,21 @@ function getDefaultConfig() {
1914
1920
  prComment: {
1915
1921
  mode: "append"
1916
1922
  },
1923
+ ai: {
1924
+ provider: "claude-code",
1925
+ language: "en",
1926
+ fallback: "raw",
1927
+ providers: {
1928
+ "claude-code": {
1929
+ model: "haiku"
1930
+ }
1931
+ },
1932
+ providerRelease: { enabled: false },
1933
+ social: {
1934
+ twitter: { enabled: false },
1935
+ slack: { enabled: false }
1936
+ }
1937
+ },
1917
1938
  logLevel: "default",
1918
1939
  safetyCheck: true
1919
1940
  };
@@ -2305,45 +2326,35 @@ function getShortCommitSha(cwd, length = 7) {
2305
2326
  return execSync(`git rev-parse --short=${length} HEAD`, { cwd, encoding: "utf8" }).trim();
2306
2327
  }
2307
2328
 
2308
- async function generateMarkDown({
2309
- commits,
2310
- config,
2311
- from,
2312
- to,
2313
- isFirstCommit,
2314
- minify
2315
- }) {
2329
+ const CHANGELOG_RELEASE_HEAD_REGEX = /^#{2,}\s+(?:\S.*)?(v?(\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?)).*$/gm;
2330
+ const VERSION_REGEX = /^v?(\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?)$/;
2331
+ function buildCompareLink({ config, from, to, isFirstCommit }) {
2332
+ if (!config.repo || !from || !to) {
2333
+ return "";
2334
+ }
2335
+ return formatCompareChanges(to, {
2336
+ ...config,
2337
+ from: isFirstCommit ? getFirstCommit(config.cwd) : from,
2338
+ to
2339
+ });
2340
+ }
2341
+ function buildChangelogBody({ commits, config, minify }) {
2316
2342
  const typeGroups = groupBy(commits, "type");
2317
2343
  const markdown = [];
2318
2344
  const breakingChanges = [];
2319
- const updatedConfig = {
2320
- ...config,
2321
- from,
2322
- to
2323
- };
2324
- const versionTitle = updatedConfig.to;
2325
- const changelogTitle = (updatedConfig.templates?.changelogTitle || "{{oldVersion}}...{{newVersion}}").replace("{{oldVersion}}", updatedConfig.from).replace("{{newVersion}}", updatedConfig.to).replace("{{date}}", (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
2326
- markdown.push("", `## ${changelogTitle}`, "");
2327
- if (updatedConfig.repo && updatedConfig.from && versionTitle && !minify) {
2328
- const formattedCompareLink = formatCompareChanges(versionTitle, {
2329
- ...updatedConfig,
2330
- from: isFirstCommit ? getFirstCommit(updatedConfig.cwd) : updatedConfig.from
2331
- });
2332
- markdown.push(formattedCompareLink);
2333
- }
2334
- for (const type in updatedConfig.types) {
2345
+ for (const type in config.types) {
2335
2346
  const group = typeGroups[type];
2336
2347
  if (!group || group.length === 0) {
2337
2348
  continue;
2338
2349
  }
2339
- if (typeof updatedConfig.types[type] === "boolean") {
2350
+ if (typeof config.types[type] === "boolean") {
2340
2351
  continue;
2341
2352
  }
2342
- markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
2353
+ markdown.push("", `### ${config.types[type]?.title}`, "");
2343
2354
  for (const commit of group.reverse()) {
2344
2355
  const line = formatCommit({
2345
2356
  commit,
2346
- config: updatedConfig,
2357
+ config,
2347
2358
  minify
2348
2359
  });
2349
2360
  markdown.push(line);
@@ -2355,16 +2366,22 @@ async function generateMarkDown({
2355
2366
  if (breakingChanges.length > 0) {
2356
2367
  markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
2357
2368
  }
2369
+ return convert(markdown.join("\n").trim(), true);
2370
+ }
2371
+ async function buildContributors({ commits, config }) {
2372
+ if (config.noAuthors) {
2373
+ return "";
2374
+ }
2358
2375
  const _authors = /* @__PURE__ */ new Map();
2359
2376
  for (const commit of commits) {
2360
- if (!commit.author || minify) {
2377
+ if (!commit.author) {
2361
2378
  continue;
2362
2379
  }
2363
2380
  const name = formatName(commit.author.name);
2364
2381
  if (!name || name.includes("[bot]")) {
2365
2382
  continue;
2366
2383
  }
2367
- if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
2384
+ if (config.excludeAuthors && config.excludeAuthors.some(
2368
2385
  (v) => name.includes(v) || commit.author.email?.includes(v)
2369
2386
  )) {
2370
2387
  continue;
@@ -2376,7 +2393,7 @@ async function generateMarkDown({
2376
2393
  _authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
2377
2394
  }
2378
2395
  }
2379
- if (updatedConfig.repo?.provider === "github") {
2396
+ if (config.repo?.provider === "github") {
2380
2397
  await Promise.all(
2381
2398
  Array.from(_authors.keys(), async (authorName) => {
2382
2399
  const meta = _authors.get(authorName);
@@ -2397,22 +2414,63 @@ async function generateMarkDown({
2397
2414
  name: e[0],
2398
2415
  ...e[1]
2399
2416
  }));
2400
- if (authors.length > 0 && !updatedConfig.noAuthors) {
2401
- markdown.push(
2402
- "",
2403
- "### \u2764\uFE0F Contributors",
2404
- "",
2405
- ...authors.map((i) => {
2406
- const _email = [...i.email].find(
2407
- (e) => !e.includes("noreply.github.com")
2408
- );
2409
- const email = updatedConfig.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
2410
- const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
2411
- return `- ${i.name}${github || email || ""}`;
2412
- })
2413
- );
2417
+ if (authors.length === 0) {
2418
+ return "";
2414
2419
  }
2415
- return convert(markdown.join("\n").trim(), true);
2420
+ const lines = [
2421
+ "### \u2764\uFE0F Contributors",
2422
+ "",
2423
+ ...authors.map((i) => {
2424
+ const _email = [...i.email].find(
2425
+ (e) => !e.includes("noreply.github.com")
2426
+ );
2427
+ const email = config.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
2428
+ const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
2429
+ return `- ${i.name}${github || email || ""}`;
2430
+ })
2431
+ ];
2432
+ return lines.join("\n");
2433
+ }
2434
+ async function generateMarkDown({
2435
+ commits,
2436
+ config,
2437
+ from,
2438
+ to,
2439
+ isFirstCommit,
2440
+ minify
2441
+ }) {
2442
+ const updatedConfig = {
2443
+ ...config,
2444
+ from,
2445
+ to
2446
+ };
2447
+ const changelogTitle = (updatedConfig.templates?.changelogTitle || "{{oldVersion}}...{{newVersion}}").replace("{{oldVersion}}", updatedConfig.from).replace("{{newVersion}}", updatedConfig.to).replace("{{date}}", (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
2448
+ const title = `## ${changelogTitle}`;
2449
+ const compareLink = minify ? "" : buildCompareLink({ config: updatedConfig, from, to, isFirstCommit });
2450
+ const body = buildChangelogBody({ commits, config: updatedConfig, minify });
2451
+ const contributors = minify ? "" : await buildContributors({ commits, config: updatedConfig });
2452
+ return [title, compareLink, body, contributors].filter(Boolean).join("\n\n").trim();
2453
+ }
2454
+ function parseChangelogMarkdown(contents) {
2455
+ const headings = [...contents.matchAll(CHANGELOG_RELEASE_HEAD_REGEX)];
2456
+ const releases = [];
2457
+ for (let i = 0; i < headings.length; i++) {
2458
+ const heading = headings[i];
2459
+ const nextHeading = headings[i + 1];
2460
+ const [, title] = heading;
2461
+ const version = title?.match(VERSION_REGEX);
2462
+ const release = {
2463
+ version: version ? version[1] : void 0,
2464
+ body: contents.slice(
2465
+ heading?.index + (heading?.[0]?.length || 0),
2466
+ nextHeading?.index ?? contents.length
2467
+ ).trim()
2468
+ };
2469
+ releases.push(release);
2470
+ }
2471
+ return {
2472
+ releases
2473
+ };
2416
2474
  }
2417
2475
  function getCommitBody(commit) {
2418
2476
  if (!commit.body) {
@@ -2561,6 +2619,205 @@ ${existingChangelog}`;
2561
2619
  }
2562
2620
  }
2563
2621
 
2622
+ const BASE_PROMPT = `You are a release-notes rewriter.
2623
+ The user message contains a markdown changelog built from conventional commits, wrapped in a <changelog> tag. Your job: rewrite the content inside that tag.
2624
+ Never ask for clarification. Never reply with questions, greetings, or meta-commentary. Your only output is the rewritten changelog content.
2625
+ Never invent changes that are not in the input \u2014 if something is not mentioned, it does not exist.
2626
+ Never include compare links, contributor lists, or release metadata \u2014 only the content provided.
2627
+ Preserve exactly as given: PR and issue references like #123, commit hashes, commit scopes like **auth:**, and all markdown links.
2628
+ Respond with the rewritten content only \u2014 no preamble, no explanation, no surrounding code fence, no <changelog> tag.
2629
+ Output language: {{language}}.`;
2630
+ const PROVIDER_RELEASE_PROMPT = `Format the output as markdown with "### <Type>" sections matching the input (Features, Bug Fixes, etc.).
2631
+ Merge redundant items that describe the same change.
2632
+ Rewrite each bullet for end-user clarity \u2014 not "fix: typo in var name" but "Fixed X".
2633
+ Preserve the \u26A0\uFE0F marker on breaking items and keep the "#### \u26A0\uFE0F Breaking Changes" section if present.
2634
+ Purely internal commits (chore, refactor with no user-visible impact) may be dropped unless they carry meaningful information.
2635
+ Tone: professional, concise, public-facing release notes.`;
2636
+ const TWITTER_PROMPT = `Output plain text \u2014 no markdown. A leading "#" becomes a hashtag on Twitter, so avoid it.
2637
+ Hard maximum: {{maxLength}} characters. Never exceed it. Don't count characters \u2014 just stay concise.
2638
+ If the input has one change, write one substantive sentence about it (ground it in the input, never invent).
2639
+ If the input has multiple changes, surface 2 to 4 highlights.
2640
+ You produce ONLY the changelog content \u2014 the outer template will add the project name, version, and URLs around it.
2641
+ Tone: enthusiastic but not cringy. At most 1-2 emojis total.
2642
+ Do not add hashtags unless the user explicitly supplies them.`;
2643
+ const SLACK_PROMPT = `Format with Slack-compatible markdown: *bold*, _italic_, \`code\`, and "-" bullet lists.
2644
+ Keep it synthetic \u2014 3 to 8 bullets maximum.
2645
+ If any breaking changes are present, lead with them.
2646
+ Tone: factual, oriented toward an internal team audience.`;
2647
+
2648
+ function resolveAuth(config) {
2649
+ const providerOpts = config.ai?.providers?.["claude-code"];
2650
+ const tokenOpts = config.tokens?.ai?.["claude-code"];
2651
+ return {
2652
+ apiKey: providerOpts?.apiKey ?? tokenOpts?.apiKey,
2653
+ oauthToken: providerOpts?.oauthToken ?? tokenOpts?.oauthToken
2654
+ };
2655
+ }
2656
+ const claudeCodeProvider = {
2657
+ name: "claude-code",
2658
+ async safetyCheck(config) {
2659
+ const { apiKey, oauthToken } = resolveAuth(config);
2660
+ if (!apiKey && !oauthToken) {
2661
+ throw new Error(
2662
+ "No authentication credential found for claude-code provider. Set one of: ANTHROPIC_API_KEY, RELIZY_ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, RELIZY_CLAUDE_CODE_OAUTH_TOKEN, or configure ai.providers['claude-code'].apiKey / oauthToken."
2663
+ );
2664
+ }
2665
+ try {
2666
+ await import('@yoloship/claude-sdk');
2667
+ } catch {
2668
+ throw new Error(
2669
+ "@yoloship/claude-sdk is not installed. Install it with: pnpm add -D @yoloship/claude-sdk"
2670
+ );
2671
+ }
2672
+ },
2673
+ async generate(config, request) {
2674
+ const { claudeRun } = await import('@yoloship/claude-sdk');
2675
+ const auth = resolveAuth(config);
2676
+ const model = config.ai?.providers?.["claude-code"]?.model;
2677
+ const wrappedPrompt = `<changelog>
2678
+ ${request.prompt}
2679
+ </changelog>
2680
+
2681
+ Rewrite the content inside the <changelog> tag per the rules in the system prompt. Output ONLY the rewritten content, with no preamble, no explanation, no surrounding tags.`;
2682
+ const hasAuth = !!(auth.apiKey || auth.oauthToken);
2683
+ const result = await claudeRun(
2684
+ {
2685
+ systemPrompt: request.systemPrompt,
2686
+ prompt: wrappedPrompt,
2687
+ maxTurns: 1,
2688
+ effort: "low",
2689
+ disableSlashCommands: true,
2690
+ noSessionPersistence: true,
2691
+ allowedTools: [],
2692
+ settingSources: ["project"],
2693
+ ...model && { model }
2694
+ },
2695
+ hasAuth ? auth : void 0
2696
+ );
2697
+ logger.verbose("Claude SDK events:", result.events?.map((e) => e.type).join(", "));
2698
+ return (result.output ?? "").trim();
2699
+ }
2700
+ };
2701
+
2702
+ const providers = {
2703
+ "claude-code": claudeCodeProvider
2704
+ };
2705
+ function getAIProvider(config) {
2706
+ const name = config.ai?.provider ?? "claude-code";
2707
+ const provider = providers[name];
2708
+ if (!provider) {
2709
+ const available = Object.keys(providers).join(", ");
2710
+ throw new Error(`Unknown AI provider "${name}". Available providers: ${available}`);
2711
+ }
2712
+ return provider;
2713
+ }
2714
+
2715
+ const PLATFORM_PROMPTS = {
2716
+ providerRelease: PROVIDER_RELEASE_PROMPT,
2717
+ twitter: TWITTER_PROMPT,
2718
+ slack: SLACK_PROMPT
2719
+ };
2720
+ function substitutePlaceholders(prompt, vars) {
2721
+ let result = prompt;
2722
+ for (const [key, value] of Object.entries(vars)) {
2723
+ if (value !== void 0) {
2724
+ result = result.replaceAll(`{{${key}}}`, value);
2725
+ }
2726
+ }
2727
+ return result;
2728
+ }
2729
+ function assemblePrompt(config, target, maxLength) {
2730
+ const vars = {
2731
+ language: config.ai?.language ?? "en",
2732
+ maxLength: maxLength?.toString()
2733
+ };
2734
+ const override = config.ai?.systemPromptOverrides?.[target];
2735
+ if (override) {
2736
+ return substitutePlaceholders(override, vars);
2737
+ }
2738
+ const parts = [BASE_PROMPT, PLATFORM_PROMPTS[target]];
2739
+ if (config.ai?.extraGuidelines) {
2740
+ parts.push(config.ai.extraGuidelines);
2741
+ }
2742
+ return substitutePlaceholders(parts.join("\n\n"), vars);
2743
+ }
2744
+ function applyAIOverride(config, ai) {
2745
+ if (ai === void 0)
2746
+ return;
2747
+ if (!config.ai) {
2748
+ config.ai = {};
2749
+ }
2750
+ const aiConfig = config.ai;
2751
+ aiConfig.providerRelease = { enabled: ai };
2752
+ aiConfig.social = {
2753
+ twitter: { enabled: ai },
2754
+ slack: { enabled: ai }
2755
+ };
2756
+ }
2757
+ function isAIProviderReleaseEnabled(config) {
2758
+ return !!config.ai?.providerRelease?.enabled;
2759
+ }
2760
+ function isAISocialEnabled(config, platform) {
2761
+ return !!config.ai?.social?.[platform]?.enabled;
2762
+ }
2763
+ async function aiSafetyCheck({ config }) {
2764
+ const provider = getAIProvider(config);
2765
+ await provider.safetyCheck(config);
2766
+ }
2767
+ async function generateAIProviderReleaseBody({ config, rawBody }) {
2768
+ if (!rawBody.trim()) {
2769
+ logger.debug("AI skipped: empty changelog body");
2770
+ return rawBody;
2771
+ }
2772
+ const provider = getAIProvider(config);
2773
+ const systemPrompt = assemblePrompt(config, "providerRelease");
2774
+ logger.info(`\u2728 Rewriting release notes with AI (provider: ${provider.name})`);
2775
+ logger.verbose("AI system prompt:", systemPrompt);
2776
+ logger.verbose("AI input body:", rawBody);
2777
+ try {
2778
+ const started = Date.now();
2779
+ const output = await provider.generate(config, { systemPrompt, prompt: rawBody });
2780
+ const elapsed = Date.now() - started;
2781
+ logger.info(`\u2705 AI rewrite done in ${elapsed}ms (${rawBody.length} \u2192 ${output.length} chars)`);
2782
+ logger.verbose("AI output body:", output);
2783
+ return output;
2784
+ } catch (error) {
2785
+ return handleFallback(config, rawBody, error);
2786
+ }
2787
+ }
2788
+ async function generateAISocialChangelog({ config, rawBody, fallbackBody, platform, maxLength }) {
2789
+ const fallbackValue = fallbackBody ?? rawBody;
2790
+ if (!rawBody.trim()) {
2791
+ logger.debug(`AI skipped for ${platform}: empty changelog body`);
2792
+ return fallbackValue;
2793
+ }
2794
+ const provider = getAIProvider(config);
2795
+ const systemPrompt = assemblePrompt(config, platform, maxLength);
2796
+ const maxLengthHint = maxLength ? `, max ${maxLength} chars` : "";
2797
+ logger.info(`\u2728 Rewriting ${platform} post with AI (provider: ${provider.name}${maxLengthHint})`);
2798
+ logger.verbose(`AI system prompt (${platform}):`, systemPrompt);
2799
+ logger.verbose(`AI input body (${platform}):`, rawBody);
2800
+ try {
2801
+ const started = Date.now();
2802
+ const output = await provider.generate(config, { systemPrompt, prompt: rawBody, maxLength });
2803
+ const elapsed = Date.now() - started;
2804
+ logger.info(`\u2705 AI rewrite done for ${platform} in ${elapsed}ms (${rawBody.length} \u2192 ${output.length} chars)`);
2805
+ logger.verbose(`AI output body (${platform}):`, output);
2806
+ return output;
2807
+ } catch (error) {
2808
+ return handleFallback(config, fallbackValue, error);
2809
+ }
2810
+ }
2811
+ function handleFallback(config, rawBody, error) {
2812
+ const fallback = config.ai?.fallback ?? "raw";
2813
+ const message = error instanceof Error ? error.message : String(error);
2814
+ if (fallback === "fail") {
2815
+ throw new Error(`AI generation failed: ${message}`, { cause: error });
2816
+ }
2817
+ logger.warn(`AI generation failed, falling back to raw body: ${message}`);
2818
+ return rawBody;
2819
+ }
2820
+
2564
2821
  async function githubIndependentMode({
2565
2822
  config,
2566
2823
  dryRun,
@@ -2594,13 +2851,14 @@ async function githubIndependentMode({
2594
2851
  }
2595
2852
  const toTag = dryRun ? "HEAD" : to;
2596
2853
  logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
2597
- const changelog = await generateChangelog({
2598
- pkg,
2599
- config,
2600
- dryRun,
2601
- newVersion
2602
- });
2603
- const releaseBody = changelog.split("\n").slice(2).join("\n");
2854
+ const isFirstCommit = from === getFirstCommit(config.cwd);
2855
+ const compareLink = buildCompareLink({ config, from, to, isFirstCommit });
2856
+ let body = buildChangelogBody({ commits: pkg.commits, config });
2857
+ const contributors = await buildContributors({ commits: pkg.commits, config });
2858
+ if (isAIProviderReleaseEnabled(config)) {
2859
+ body = await generateAIProviderReleaseBody({ config, rawBody: body });
2860
+ }
2861
+ const releaseBody = [compareLink, body, contributors].filter(Boolean).join("\n\n").trim();
2604
2862
  const release = {
2605
2863
  tag_name: to,
2606
2864
  name: to,
@@ -2658,13 +2916,16 @@ async function githubUnified({
2658
2916
  }
2659
2917
  const newVersion = bumpResult?.newVersion || rootPackage.version;
2660
2918
  const to = config.to || config.templates.tagBody.replace("{{newVersion}}", newVersion);
2661
- const changelog = await generateChangelog({
2662
- pkg: rootPackage,
2663
- config,
2664
- dryRun,
2665
- newVersion
2666
- });
2667
- const releaseBody = changelog.split("\n").slice(2).join("\n");
2919
+ const firstCommit = getFirstCommit(config.cwd);
2920
+ const from = config.from || rootPackage.fromTag || firstCommit;
2921
+ const isFirstCommit = from === firstCommit;
2922
+ const compareLink = buildCompareLink({ config, from, to, isFirstCommit });
2923
+ let body = buildChangelogBody({ commits: rootPackage.commits, config });
2924
+ const contributors = await buildContributors({ commits: rootPackage.commits, config });
2925
+ if (isAIProviderReleaseEnabled(config)) {
2926
+ body = await generateAIProviderReleaseBody({ config, rawBody: body });
2927
+ }
2928
+ const releaseBody = [compareLink, body, contributors].filter(Boolean).join("\n\n").trim();
2668
2929
  const release = {
2669
2930
  tag_name: to,
2670
2931
  name: to,
@@ -2850,17 +3111,18 @@ async function gitlabIndependentMode({
2850
3111
  continue;
2851
3112
  }
2852
3113
  logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${to}`);
2853
- const changelog = await generateChangelog({
2854
- pkg,
2855
- config,
2856
- dryRun,
2857
- newVersion
2858
- });
2859
- if (!changelog) {
3114
+ const isFirstCommit = from === getFirstCommit(config.cwd);
3115
+ const compareLink = buildCompareLink({ config, from, to, isFirstCommit });
3116
+ let body = buildChangelogBody({ commits: pkg.commits, config });
3117
+ const contributors = await buildContributors({ commits: pkg.commits, config });
3118
+ if (!body) {
2860
3119
  logger.warn(`No changelog found for ${pkg.name}`);
2861
3120
  continue;
2862
3121
  }
2863
- const releaseBody = changelog.split("\n").slice(2).join("\n");
3122
+ if (isAIProviderReleaseEnabled(config)) {
3123
+ body = await generateAIProviderReleaseBody({ config, rawBody: body });
3124
+ }
3125
+ const releaseBody = [compareLink, body, contributors].filter(Boolean).join("\n\n").trim();
2864
3126
  const release = {
2865
3127
  tag_name: to,
2866
3128
  name: to,
@@ -2904,13 +3166,16 @@ async function gitlabUnified({
2904
3166
  logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
2905
3167
  const newVersion = bumpResult?.newVersion || rootPackage.newVersion || rootPackage.version;
2906
3168
  const to = config.templates.tagBody.replace("{{newVersion}}", newVersion);
2907
- const changelog = await generateChangelog({
2908
- pkg: rootPackage,
2909
- config,
2910
- dryRun,
2911
- newVersion
2912
- });
2913
- const releaseBody = changelog.split("\n").slice(2).join("\n");
3169
+ const firstCommit = getFirstCommit(config.cwd);
3170
+ const from = config.from || rootPackage.fromTag || firstCommit;
3171
+ const isFirstCommit = from === firstCommit;
3172
+ const compareLink = buildCompareLink({ config, from, to, isFirstCommit });
3173
+ let body = buildChangelogBody({ commits: rootPackage.commits, config });
3174
+ const contributors = await buildContributors({ commits: rootPackage.commits, config });
3175
+ if (isAIProviderReleaseEnabled(config)) {
3176
+ body = await generateAIProviderReleaseBody({ config, rawBody: body });
3177
+ }
3178
+ const releaseBody = [compareLink, body, contributors].filter(Boolean).join("\n\n").trim();
2914
3179
  logger.debug("Getting current branch...");
2915
3180
  const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
2916
3181
  noSuccess: true,
@@ -4591,7 +4856,7 @@ ${body}`
4591
4856
  return await postPrComment({ config, pr, body });
4592
4857
  }
4593
4858
 
4594
- function providerReleaseSafetyCheck({ config, provider }) {
4859
+ async function providerReleaseSafetyCheck({ config, provider }) {
4595
4860
  if (!config.safetyCheck || !config.release.providerRelease) {
4596
4861
  logger.debug("Safety check disabled or provider release disabled");
4597
4862
  return;
@@ -4614,6 +4879,9 @@ function providerReleaseSafetyCheck({ config, provider }) {
4614
4879
  if (!token) {
4615
4880
  throw new Error(`No token provided for ${internalProvider} - The release will not be published - Please refer to the documentation: https://louismazel.github.io/relizy/guide/installation#environment-setup`);
4616
4881
  }
4882
+ if (isAIProviderReleaseEnabled(config)) {
4883
+ await aiSafetyCheck({ config });
4884
+ }
4617
4885
  logger.info("provider release config checked successfully");
4618
4886
  }
4619
4887
  async function providerRelease(options = {}) {
@@ -4631,13 +4899,14 @@ async function providerRelease(options = {}) {
4631
4899
  safetyCheck: options.safetyCheck
4632
4900
  }
4633
4901
  });
4902
+ applyAIOverride(config, options.ai);
4634
4903
  const dryRun = options.dryRun ?? false;
4635
4904
  logger.debug(`Dry run: ${dryRun}`);
4636
4905
  logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
4637
4906
  let detectedProvider = null;
4638
4907
  try {
4639
4908
  detectedProvider = options.provider || detectGitProvider();
4640
- providerReleaseSafetyCheck({ config, provider: detectedProvider });
4909
+ await providerReleaseSafetyCheck({ config, provider: detectedProvider });
4641
4910
  await executeHook("before:provider-release", config, dryRun);
4642
4911
  logger.start("Start provider release");
4643
4912
  if (!detectedProvider) {
@@ -4824,6 +5093,17 @@ async function publish(options = {}) {
4824
5093
  }
4825
5094
  }
4826
5095
 
5096
+ function computeTwitterChangelogBudget({
5097
+ template,
5098
+ projectName,
5099
+ version,
5100
+ releaseUrl,
5101
+ changelogUrl,
5102
+ postMaxLength
5103
+ }) {
5104
+ const overhead = template.replace("{{projectName}}", projectName).replace("{{newVersion}}", version).replace("{{releaseUrl}}", releaseUrl ?? "").replace("{{changelogUrl}}", changelogUrl ?? "").replace("{{changelog}}", "").length;
5105
+ return Math.max(0, postMaxLength - overhead);
5106
+ }
4827
5107
  async function socialSafetyCheck({ config }) {
4828
5108
  try {
4829
5109
  const socialMediaDisabled = !config.release.social && !config.social.twitter.enabled && !config.social.slack.enabled;
@@ -4873,6 +5153,9 @@ async function socialSafetyCheck({ config }) {
4873
5153
  throw new Error("Slack channel not found");
4874
5154
  }
4875
5155
  }
5156
+ if (isAISocialEnabled(config, "twitter") || isAISocialEnabled(config, "slack")) {
5157
+ await aiSafetyCheck({ config });
5158
+ }
4876
5159
  logger.info("Social config checked successfully");
4877
5160
  } catch (error) {
4878
5161
  logger.error("Error during social safety check:", getErrorMessage(error));
@@ -5031,6 +5314,7 @@ async function social(options = {}) {
5031
5314
  logLevel: options.logLevel
5032
5315
  }
5033
5316
  });
5317
+ applyAIOverride(config, options.ai);
5034
5318
  logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
5035
5319
  await socialSafetyCheck({ config });
5036
5320
  if (!config.release.social && !config.social?.twitter?.enabled && !config.social?.slack?.enabled) {
@@ -5059,23 +5343,60 @@ async function social(options = {}) {
5059
5343
  from: fromTag,
5060
5344
  to
5061
5345
  });
5062
- const changelog = await generateChangelog({
5063
- pkg: rootPackage,
5064
- config,
5065
- dryRun,
5066
- newVersion,
5067
- minify: true
5068
- });
5346
+ const minifiedBody = buildChangelogBody({ commits: rootPackage.commits, config, minify: true });
5347
+ const richBody = buildChangelogBody({ commits: rootPackage.commits, config, minify: false });
5348
+ const hasContent = !!minifiedBody.trim();
5349
+ const twitterReleaseUrl = getReleaseUrl(config, to);
5350
+ const twitterChangelogUrl = config.social?.changelogUrl;
5351
+ const prerelease = isPrerelease(newVersion);
5352
+ const twitterWillPost = !!config.social?.twitter?.enabled && !(config.social.twitter.onlyStable && prerelease);
5353
+ const slackWillPost = !!config.social?.slack?.enabled && !((config.social.slack.onlyStable ?? true) && prerelease);
5354
+ let twitterChangelog = minifiedBody;
5355
+ if (hasContent && twitterWillPost && isAISocialEnabled(config, "twitter")) {
5356
+ const twitterTemplate = config.social.twitter.template || config.templates.twitterMessage;
5357
+ const twitterBudget = computeTwitterChangelogBudget({
5358
+ template: twitterTemplate,
5359
+ projectName: config.projectName || rootPackageRead.name,
5360
+ version: newVersion,
5361
+ releaseUrl: twitterReleaseUrl,
5362
+ changelogUrl: twitterChangelogUrl,
5363
+ postMaxLength: config.social.twitter.postMaxLength
5364
+ });
5365
+ twitterChangelog = await generateAISocialChangelog({
5366
+ config,
5367
+ rawBody: richBody,
5368
+ fallbackBody: minifiedBody,
5369
+ platform: "twitter",
5370
+ maxLength: twitterBudget
5371
+ });
5372
+ if (dryRun) {
5373
+ logger.box("[dry-run] AI Twitter preview", `
5374
+
5375
+ ${twitterChangelog}`);
5376
+ }
5377
+ }
5378
+ let slackChangelog = minifiedBody;
5379
+ if (hasContent && slackWillPost && isAISocialEnabled(config, "slack")) {
5380
+ slackChangelog = await generateAISocialChangelog({
5381
+ config,
5382
+ rawBody: richBody,
5383
+ fallbackBody: minifiedBody,
5384
+ platform: "slack"
5385
+ });
5386
+ if (dryRun) {
5387
+ logger.box("[dry-run] AI Slack preview", slackChangelog);
5388
+ }
5389
+ }
5069
5390
  const twitterResponse = await handleTwitterPost({
5070
5391
  config,
5071
- changelog,
5392
+ changelog: twitterChangelog,
5072
5393
  dryRun,
5073
5394
  newVersion,
5074
5395
  tag: to
5075
5396
  });
5076
5397
  const slackResponse = await handleSlackPost({
5077
5398
  config,
5078
- changelog,
5399
+ changelog: slackChangelog,
5079
5400
  dryRun,
5080
5401
  newVersion,
5081
5402
  tag: to
@@ -5226,6 +5547,7 @@ async function release(options = {}) {
5226
5547
  const force = options.force ?? false;
5227
5548
  logger.debug(`Force bump: ${force}`);
5228
5549
  const config = await getReleaseConfig(options);
5550
+ applyAIOverride(config, options.ai);
5229
5551
  logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
5230
5552
  logger.debug(`Push: ${config.release.push}, Publish: ${config.release.publish}, Provider Release: ${config.release.providerRelease}`);
5231
5553
  await releaseSafetyCheck({ config, provider: options.provider });
@@ -5447,4 +5769,4 @@ Git provider: ${provider}`);
5447
5769
  }
5448
5770
  }
5449
5771
 
5450
- export { getSlackToken as $, createCommitAndTags as A, pushCommitAndTags as B, rollbackModifiedFiles as C, getFirstCommit as D, getCurrentGitBranch as E, getCurrentGitRef as F, getShortCommitSha as G, github as H, createGitlabRelease as I, gitlab as J, detectPackageManager as K, determinePublishTag as L, getPackagesToPublishInSelectiveMode as M, getPackagesToPublishInIndependentMode as N, getAuthCommand as O, publishPackage as P, findGitHubPR as Q, findGitLabMR as R, detectPullRequest as S, PR_COMMENT_MARKER as T, postPrComment as U, readPackageJson as V, getRootPackage as W, readPackages as X, getPackages as Y, getPackageCommits as Z, hasLernaJson as _, buildCommentBody as a, formatChangelogForSlack as a0, formatSlackMessage as a1, postReleaseToSlack as a2, extractChangelogSummary as a3, getReleaseUrl as a4, getIndependentTag as a5, getLastStableTag as a6, getLastTag as a7, getLastRepoTag as a8, getLastPackageTag as a9, isChangedPreid as aA, getBumpedPackageIndependently as aB, confirmBump as aC, getBumpedIndependentPackages as aD, shouldFilterPrereleaseTags as aE, extractVersionFromTag as aF, getCanaryVersion as aG, isTagVersionCompatibleWithCurrent as aH, NEW_PACKAGE_MARKER as aa, resolveTags as ab, getTwitterCredentials as ac, formatTweetMessage as ad, postReleaseToTwitter as ae, executeHook as af, isInCI as ag, getCIName as ah, executeFormatCmd as ai, executeBuildCmd as aj, isBumpedPackage as ak, filterOutPrivatePackages as al, getPackagesOrBumpedPackages as am, isGraduatingToStableBetweenVersion as an, capReleaseTypeForZeroMajor as ao, determineSemverChange as ap, determineReleaseType as aq, writeVersion as ar, getPackageNewVersion as as, updateLernaVersion as at, extractVersionFromPackageTag as au, isPrerelease as av, isStableReleaseType as aw, isPrereleaseReleaseType as ax, isGraduating as ay, getPreid as az, bump as b, changelog as c, providerReleaseSafetyCheck as d, providerRelease as e, publishSafetyCheck as f, publish as g, social as h, generateChangelog as i, getDefaultConfig as j, defineConfig as k, loadRelizyConfig as l, getPackageDependencies as m, getDependentsOf as n, expandPackagesToBumpWithDependents as o, prComment as p, getGitStatus as q, release as r, socialSafetyCheck as s, topologicalSort as t, checkGitStatusIfDirty as u, fetchGitTags as v, writeChangelogToFile as w, detectGitProvider as x, parseGitRemoteUrl as y, getModifiedReleaseFilePatterns as z };
5772
+ export { getRootPackage as $, createCommitAndTags as A, pushCommitAndTags as B, rollbackModifiedFiles as C, getFirstCommit as D, getCurrentGitBranch as E, getCurrentGitRef as F, getShortCommitSha as G, github as H, createGitlabRelease as I, gitlab as J, buildCompareLink as K, buildChangelogBody as L, buildContributors as M, generateMarkDown as N, parseChangelogMarkdown as O, detectPackageManager as P, determinePublishTag as Q, getPackagesToPublishInSelectiveMode as R, getPackagesToPublishInIndependentMode as S, getAuthCommand as T, publishPackage as U, findGitHubPR as V, findGitLabMR as W, detectPullRequest as X, PR_COMMENT_MARKER as Y, postPrComment as Z, readPackageJson as _, buildCommentBody as a, readPackages as a0, getPackages as a1, getPackageCommits as a2, hasLernaJson as a3, getSlackToken as a4, formatChangelogForSlack as a5, formatSlackMessage as a6, postReleaseToSlack as a7, extractChangelogSummary as a8, getReleaseUrl as a9, isPrerelease as aA, isStableReleaseType as aB, isPrereleaseReleaseType as aC, isGraduating as aD, getPreid as aE, isChangedPreid as aF, getBumpedPackageIndependently as aG, confirmBump as aH, getBumpedIndependentPackages as aI, shouldFilterPrereleaseTags as aJ, extractVersionFromTag as aK, getCanaryVersion as aL, isTagVersionCompatibleWithCurrent as aM, getIndependentTag as aa, getLastStableTag as ab, getLastTag as ac, getLastRepoTag as ad, getLastPackageTag as ae, NEW_PACKAGE_MARKER as af, resolveTags as ag, getTwitterCredentials as ah, formatTweetMessage as ai, postReleaseToTwitter as aj, executeHook as ak, isInCI as al, getCIName as am, executeFormatCmd as an, executeBuildCmd as ao, isBumpedPackage as ap, filterOutPrivatePackages as aq, getPackagesOrBumpedPackages as ar, isGraduatingToStableBetweenVersion as as, capReleaseTypeForZeroMajor as at, determineSemverChange as au, determineReleaseType as av, writeVersion as aw, getPackageNewVersion as ax, updateLernaVersion as ay, extractVersionFromPackageTag as az, bump as b, changelog as c, providerReleaseSafetyCheck as d, providerRelease as e, publishSafetyCheck as f, publish as g, social as h, generateChangelog as i, getDefaultConfig as j, defineConfig as k, loadRelizyConfig as l, getPackageDependencies as m, getDependentsOf as n, expandPackagesToBumpWithDependents as o, prComment as p, getGitStatus as q, release as r, socialSafetyCheck as s, topologicalSort as t, checkGitStatusIfDirty as u, fetchGitTags as v, writeChangelogToFile as w, detectGitProvider as x, parseGitRemoteUrl as y, getModifiedReleaseFilePatterns as z };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "relizy",
3
3
  "type": "module",
4
- "version": "1.3.0-beta.2",
4
+ "version": "1.3.0-beta.3",
5
5
  "description": "Changelogen adapter for monorepo management with unified and independent versioning",
6
6
  "author": "Louis Mazel <me@loicmazuel.com>",
7
7
  "license": "MIT",
@@ -56,12 +56,16 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@slack/web-api": "^7.0.0",
59
+ "@yoloship/claude-sdk": "^0.1.0",
59
60
  "twitter-api-v2": "^1.20.0"
60
61
  },
61
62
  "peerDependenciesMeta": {
62
63
  "@slack/web-api": {
63
64
  "optional": true
64
65
  },
66
+ "@yoloship/claude-sdk": {
67
+ "optional": true
68
+ },
65
69
  "twitter-api-v2": {
66
70
  "optional": true
67
71
  }
@@ -89,6 +93,7 @@
89
93
  "@types/node": "^25.6.0",
90
94
  "@types/semver": "^7.7.1",
91
95
  "@vitest/coverage-v8": "^4.1.4",
96
+ "@yoloship/claude-sdk": "0.1.0-beta.3",
92
97
  "cross-env": "10.1.0",
93
98
  "eslint": "^10.2.0",
94
99
  "husky": "9.1.7",
@@ -121,7 +126,7 @@
121
126
  "test:unit": "vitest run",
122
127
  "test:unit:watch": "vitest watch",
123
128
  "test:unit:coverage": "vitest run --coverage",
124
- "health": "pnpm typecheck && pnpm lint && pnpm test:unit:coverage",
129
+ "health": "pnpm typecheck && pnpm lint:all && pnpm test:unit:coverage && pnpm build && pnpm -F docs build",
125
130
  "pre-commit": "lint-staged"
126
131
  }
127
132
  }