pubz 0.4.1 → 0.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +72 -6
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -97,6 +97,7 @@ async function discoverPackages(cwd) {
97
97
  const rootPackageJson = await readPackageJson(rootPackageJsonPath);
98
98
  const workspacePatterns = getWorkspacePatterns(rootPackageJson);
99
99
  let packageDirs = [];
100
+ const rootIsPublishable = !rootPackageJson.private && rootPackageJson.name && rootPackageJson.version;
100
101
  if (workspacePatterns.length > 0) {
101
102
  for (const pattern of workspacePatterns) {
102
103
  const matches = await glob(pattern, cwd);
@@ -118,6 +119,10 @@ async function discoverPackages(cwd) {
118
119
  }
119
120
  const packages = [];
120
121
  const packageNames = new Set;
122
+ if (rootIsPublishable) {
123
+ packageNames.add(rootPackageJson.name);
124
+ packages.push(await packageFromPath(cwd, rootPackageJsonPath, rootPackageJson, []));
125
+ }
121
126
  for (const dir of packageDirs) {
122
127
  const pkgPath = resolve(cwd, dir);
123
128
  const pkgJsonPath = join2(pkgPath, "package.json");
@@ -552,6 +557,10 @@ async function pushGitTag(version, cwd, dryRun) {
552
557
  console.log(`[DRY RUN] Would push git tag: ${tagName}`);
553
558
  return { success: true };
554
559
  }
560
+ const branchResult = await run("git", ["push"], cwd);
561
+ if (branchResult.code !== 0) {
562
+ return { success: false, error: "Failed to push bump commit to origin" };
563
+ }
555
564
  const result = await run("git", ["push", "origin", tagName], cwd);
556
565
  if (result.code !== 0) {
557
566
  return { success: false, error: `Failed to push tag ${tagName}` };
@@ -621,15 +630,18 @@ async function getCommitsSince(ref, cwd) {
621
630
  function isReleaseCommit(message) {
622
631
  return /^chore: release v/.test(message);
623
632
  }
633
+ function isMergeCommit(message) {
634
+ return /^Merge /.test(message);
635
+ }
624
636
  function formatChangelogTerminal(commits) {
625
- const filtered = commits.filter((c) => !isReleaseCommit(c.message));
637
+ const filtered = commits.filter((c) => !isReleaseCommit(c.message) && !isMergeCommit(c.message));
626
638
  if (filtered.length === 0)
627
639
  return "";
628
640
  return filtered.map((c) => ` ${dim(c.sha)} ${c.message}`).join(`
629
641
  `);
630
642
  }
631
643
  function formatChangelogMarkdown(commits, repoUrl) {
632
- const filtered = commits.filter((c) => !isReleaseCommit(c.message));
644
+ const filtered = commits.filter((c) => !isReleaseCommit(c.message) && !isMergeCommit(c.message));
633
645
  if (filtered.length === 0)
634
646
  return "";
635
647
  return filtered.map((c) => {
@@ -655,6 +667,40 @@ async function generateChangelog(cwd) {
655
667
  const markdown = formatChangelogMarkdown(commits, repoUrl);
656
668
  return { commits, terminal, markdown, previousTag, repoUrl };
657
669
  }
670
+ async function isClaudeAvailable() {
671
+ const result = await runSilent("which", ["claude"], process.cwd());
672
+ return result.code === 0;
673
+ }
674
+ async function generateAIReleaseNotes(commits, version) {
675
+ const filtered = commits.filter((c) => !isReleaseCommit(c.message));
676
+ if (filtered.length === 0)
677
+ return null;
678
+ const commitList = filtered.map((c) => `- ${c.sha} ${c.message}`).join(`
679
+ `);
680
+ const prompt2 = `You are writing release notes for version ${version} of a software package.
681
+
682
+ Here are the commits included in this release:
683
+ ${commitList}
684
+
685
+ Write concise, user-friendly release notes in markdown. Group related changes under headings if appropriate (e.g. Features, Bug Fixes, Improvements). Focus on what changed and why it matters to users — not implementation details. Do not include a title or version header. Output only the markdown body.`;
686
+ return new Promise((resolve2) => {
687
+ const proc = spawn3("claude", ["-p", prompt2], {
688
+ stdio: ["ignore", "pipe", "pipe"]
689
+ });
690
+ let output = "";
691
+ proc.stdout?.on("data", (data) => {
692
+ output += data.toString();
693
+ });
694
+ proc.on("close", (code) => {
695
+ if (code === 0 && output.trim()) {
696
+ resolve2(output.trim());
697
+ } else {
698
+ resolve2(null);
699
+ }
700
+ });
701
+ proc.on("error", () => resolve2(null));
702
+ });
703
+ }
658
704
  async function createGitHubRelease(version, body, cwd, dryRun) {
659
705
  const tagName = `v${version}`;
660
706
  if (dryRun) {
@@ -1231,6 +1277,26 @@ async function main() {
1231
1277
  console.log(changelog.terminal);
1232
1278
  console.log("");
1233
1279
  }
1280
+ let releaseNotes = changelog.markdown;
1281
+ if (!options.ci && changelog.commits.length > 0) {
1282
+ const claudeAvailable = await isClaudeAvailable();
1283
+ if (claudeAvailable) {
1284
+ const useAI = await confirm("Generate release notes with AI (claude)?");
1285
+ if (useAI) {
1286
+ console.log(cyan("Generating AI release notes..."));
1287
+ const aiNotes = await generateAIReleaseNotes(changelog.commits, newVersion);
1288
+ if (aiNotes) {
1289
+ releaseNotes = aiNotes;
1290
+ console.log("");
1291
+ console.log(bold("AI-generated release notes:"));
1292
+ console.log(aiNotes);
1293
+ console.log("");
1294
+ } else {
1295
+ console.log(yellow("AI generation failed, falling back to commit list."));
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1234
1300
  if (!options.dryRun) {
1235
1301
  if (options.ci) {
1236
1302
  console.log(cyan("Creating git tag..."));
@@ -1238,9 +1304,9 @@ async function main() {
1238
1304
  if (tagResult.success) {
1239
1305
  console.log(cyan("Pushing tag to origin..."));
1240
1306
  await pushGitTag(newVersion, cwd, options.dryRun);
1241
- if (changelog.markdown) {
1307
+ if (releaseNotes) {
1242
1308
  console.log(cyan("Creating GitHub release..."));
1243
- const releaseResult = await createGitHubRelease(newVersion, changelog.markdown, cwd, options.dryRun);
1309
+ const releaseResult = await createGitHubRelease(newVersion, releaseNotes, cwd, options.dryRun);
1244
1310
  if (releaseResult.success && releaseResult.url) {
1245
1311
  console.log(` Release created: ${cyan(releaseResult.url)}`);
1246
1312
  } else if (!releaseResult.success) {
@@ -1260,10 +1326,10 @@ async function main() {
1260
1326
  const shouldPush = skipConfirms || await confirm("Push tag to origin?");
1261
1327
  if (shouldPush) {
1262
1328
  await pushGitTag(newVersion, cwd, options.dryRun);
1263
- if (changelog.markdown) {
1329
+ if (releaseNotes) {
1264
1330
  const shouldRelease = skipConfirms || await confirm("Create a GitHub release?");
1265
1331
  if (shouldRelease) {
1266
- const releaseResult = await createGitHubRelease(newVersion, changelog.markdown, cwd, options.dryRun);
1332
+ const releaseResult = await createGitHubRelease(newVersion, releaseNotes, cwd, options.dryRun);
1267
1333
  if (releaseResult.success && releaseResult.url) {
1268
1334
  console.log(` Release created: ${cyan(releaseResult.url)}`);
1269
1335
  } else if (!releaseResult.success) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubz",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Interactive CLI for publishing npm packages (single or monorepo)",
5
5
  "type": "module",
6
6
  "bin": {