@wipcomputer/wip-release 1.9.26 → 1.9.28

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/core.mjs +60 -25
  2. package/package.json +1 -1
package/core.mjs CHANGED
@@ -225,32 +225,69 @@ function categorizeCommit(subject) {
225
225
  */
226
226
  function checkReleaseNotes(notes, notesSource, level) {
227
227
  const issues = [];
228
- const isMinorOrMajor = level === 'minor' || level === 'major';
229
228
 
230
229
  if (!notes) {
231
- issues.push('No release notes provided. Write a RELEASE-NOTES-v{version}.md file.');
232
- return { ok: false, issues };
230
+ issues.push('No release notes provided. Write a RELEASE-NOTES-v{version}.md or ai/dev-updates/ file.');
231
+ return { ok: false, issues, block: true };
233
232
  }
234
233
 
235
- // Bare --notes flag is not acceptable for minor/major.
236
- // Agents must write a file, not pass a one-liner.
237
- if (notesSource === 'flag') {
238
- if (isMinorOrMajor) {
239
- issues.push('Release notes came from --notes flag, not a file.');
240
- issues.push('Write RELEASE-NOTES-v{version}.md (dashes not dots) and commit it.');
241
- issues.push('wip-release auto-detects the file. No --notes flag needed.');
242
- } else if (notes.length < 50) {
243
- issues.push('Release notes are very short. Consider writing a RELEASE-NOTES file.');
244
- }
234
+ // Notes too short. All levels blocked.
235
+ if (notes.length < 50) {
236
+ issues.push('Release notes are too short (under 50 chars). Explain what changed and why.');
237
+ issues.push('Write a RELEASE-NOTES-v{version}.md or ai/dev-updates/ file.');
238
+ }
239
+
240
+ // Bare --notes flag for minor/major is never acceptable.
241
+ if (notesSource === 'flag' && (level === 'minor' || level === 'major')) {
242
+ issues.push('Minor/major releases require a file, not --notes flag.');
243
+ issues.push('Write RELEASE-NOTES-v{version}.md (dashes not dots) and commit it.');
245
244
  }
246
245
 
247
246
  // Check for changelog-style one-liners regardless of source
248
247
  const looksLikeChangelog = /^(fix|add|update|remove|bump|chore|refactor|docs?)[\s:]/i.test(notes);
249
248
  if (looksLikeChangelog && notes.length < 100) {
250
- issues.push('Notes look like a changelog entry, not a narrative.');
249
+ issues.push('Notes look like a changelog entry, not a narrative. Explain the impact.');
251
250
  }
252
251
 
253
- return { ok: issues.length === 0, issues };
252
+ return { ok: issues.length === 0, issues, block: issues.length > 0 };
253
+ }
254
+
255
+ /**
256
+ * Scaffold a RELEASE-NOTES-v{version}.md template if one doesn't exist.
257
+ * Called when the release notes gate blocks. Gives the agent a file to fill in.
258
+ */
259
+ export function scaffoldReleaseNotes(repoPath, version) {
260
+ const dashed = version.replace(/\./g, '-');
261
+ const notesPath = join(repoPath, `RELEASE-NOTES-v${dashed}.md`);
262
+ if (existsSync(notesPath)) return notesPath;
263
+
264
+ const pkg = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
265
+ const name = pkg.name?.replace(/^@[^/]+\//, '') || basename(repoPath);
266
+
267
+ const template = `# Release Notes: ${name} v${version}
268
+
269
+ **One-line summary of what this release does**
270
+
271
+ ## What changed
272
+
273
+ Describe the changes. Not a commit list. Explain:
274
+ - What was built or fixed
275
+ - Why it matters
276
+ - What the user should know
277
+
278
+ ## Why
279
+
280
+ What problem does this solve? What was broken or missing?
281
+
282
+ ## How to verify
283
+
284
+ \`\`\`bash
285
+ # Commands to test the changes
286
+ \`\`\`
287
+ `;
288
+
289
+ writeFileSync(notesPath, template);
290
+ return notesPath;
254
291
  }
255
292
 
256
293
  /**
@@ -781,17 +818,15 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
781
818
  const sourceLabel = notesSource === 'file' ? 'from file' : notesSource === 'dev-update' ? 'from dev update' : 'from --notes';
782
819
  console.log(` ✓ Release notes OK (${sourceLabel})`);
783
820
  } else {
784
- const isMinorOrMajor = level === 'minor' || level === 'major';
785
- const prefix = isMinorOrMajor ? '✗' : '!';
786
- console.log(` ${prefix} Release notes need attention:`);
821
+ console.log(` ✗ Release notes blocked:`);
787
822
  for (const issue of notesCheck.issues) console.log(` - ${issue}`);
788
- if (isMinorOrMajor) {
789
- console.log('');
790
- console.log(' Minor/major releases require a RELEASE-NOTES file, not a --notes one-liner.');
791
- console.log(' Write RELEASE-NOTES-v{version}.md (dashes not dots), commit it, then release.');
792
- console.log('');
793
- return { currentVersion, newVersion, dryRun: false, failed: true };
794
- }
823
+ console.log('');
824
+ // Scaffold a template so the agent has something to fill in
825
+ const templatePath = scaffoldReleaseNotes(repoPath, newVersion);
826
+ console.log(` Scaffolded template: ${basename(templatePath)}`);
827
+ console.log(' Fill it in, commit, then run wip-release again.');
828
+ console.log('');
829
+ return { currentVersion, newVersion, dryRun: false, failed: true };
795
830
  }
796
831
  }
797
832
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.9.26",
3
+ "version": "1.9.28",
4
4
  "type": "module",
5
5
  "description": "One-command release pipeline. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.",
6
6
  "main": "core.mjs",