glab-setup-git-identity 0.6.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 (66) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/release.yml +372 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.jscpd.json +20 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +10 -0
  8. package/CHANGELOG.md +143 -0
  9. package/LICENSE +24 -0
  10. package/README.md +455 -0
  11. package/bunfig.toml +3 -0
  12. package/deno.json +7 -0
  13. package/docs/case-studies/issue-13/README.md +195 -0
  14. package/docs/case-studies/issue-13/hive-mind-issue-960.json +23 -0
  15. package/docs/case-studies/issue-13/hive-mind-pr-961-diff.txt +773 -0
  16. package/docs/case-studies/issue-13/hive-mind-pr-961.json +126 -0
  17. package/docs/case-studies/issue-21/README.md +384 -0
  18. package/docs/case-studies/issue-21/ci-logs/run-20803315337.txt +1188 -0
  19. package/docs/case-studies/issue-21/ci-logs/run-20885464993.txt +1310 -0
  20. package/docs/case-studies/issue-21/issue-111-data.txt +15 -0
  21. package/docs/case-studies/issue-21/issue-113-data.txt +15 -0
  22. package/docs/case-studies/issue-21/pr-112-data.json +109 -0
  23. package/docs/case-studies/issue-21/pr-112-diff.patch +1336 -0
  24. package/docs/case-studies/issue-21/pr-114-data.json +126 -0
  25. package/docs/case-studies/issue-21/pr-114-diff.patch +879 -0
  26. package/docs/case-studies/issue-3/README.md +338 -0
  27. package/docs/case-studies/issue-3/created-issues.md +32 -0
  28. package/docs/case-studies/issue-3/issue-data.json +29 -0
  29. package/docs/case-studies/issue-3/original-format-release-notes.mjs +212 -0
  30. package/docs/case-studies/issue-3/reference-pr-59-diff.txt +614 -0
  31. package/docs/case-studies/issue-3/reference-pr-59.json +109 -0
  32. package/docs/case-studies/issue-3/release-v0.1.0.json +9 -0
  33. package/docs/case-studies/issue-3/repositories-with-same-script.json +22 -0
  34. package/docs/case-studies/issue-3/research-notes.md +33 -0
  35. package/docs/case-studies/issue-7/BEST-PRACTICES-COMPARISON.md +334 -0
  36. package/docs/case-studies/issue-7/FORMATTER-COMPARISON.md +649 -0
  37. package/docs/case-studies/issue-7/current-repository-analysis.json +70 -0
  38. package/docs/case-studies/issue-7/effect-template-analysis.json +178 -0
  39. package/eslint.config.js +91 -0
  40. package/examples/basic-usage.js +64 -0
  41. package/experiments/test-changeset-scripts.mjs +303 -0
  42. package/experiments/test-failure-detection.mjs +143 -0
  43. package/experiments/test-format-major-changes.mjs +49 -0
  44. package/experiments/test-format-minor-changes.mjs +52 -0
  45. package/experiments/test-format-no-hash.mjs +43 -0
  46. package/experiments/test-format-patch-changes.mjs +46 -0
  47. package/package.json +80 -0
  48. package/scripts/changeset-version.mjs +75 -0
  49. package/scripts/check-changesets.mjs +67 -0
  50. package/scripts/check-version.mjs +129 -0
  51. package/scripts/create-github-release.mjs +93 -0
  52. package/scripts/create-manual-changeset.mjs +89 -0
  53. package/scripts/detect-code-changes.mjs +194 -0
  54. package/scripts/format-github-release.mjs +83 -0
  55. package/scripts/format-release-notes.mjs +219 -0
  56. package/scripts/instant-version-bump.mjs +172 -0
  57. package/scripts/js-paths.mjs +177 -0
  58. package/scripts/merge-changesets.mjs +263 -0
  59. package/scripts/publish-to-npm.mjs +302 -0
  60. package/scripts/setup-npm.mjs +37 -0
  61. package/scripts/validate-changeset.mjs +265 -0
  62. package/scripts/version-and-commit.mjs +284 -0
  63. package/src/cli.js +386 -0
  64. package/src/index.d.ts +255 -0
  65. package/src/index.js +563 -0
  66. package/tests/index.test.js +137 -0
@@ -0,0 +1,614 @@
1
+ diff --git a/.changeset/fix-release-formatting.md b/.changeset/fix-release-formatting.md
2
+ new file mode 100644
3
+ index 0000000..cc77f79
4
+ --- /dev/null
5
+ +++ b/.changeset/fix-release-formatting.md
6
+ @@ -0,0 +1,5 @@
7
+ +---
8
+ +'@link-assistant/agent': patch
9
+ +---
10
+ +
11
+ +Fix GitHub release formatting to remove incorrect title for major/minor/patch versions and properly link related pull requests
12
+ diff --git a/CLAUDE.md b/CLAUDE.md
13
+ new file mode 100644
14
+ index 0000000..bc80f9c
15
+ --- /dev/null
16
+ +++ b/CLAUDE.md
17
+ @@ -0,0 +1,5 @@
18
+ +Issue to solve: https://github.com/link-assistant/agent/issues/58
19
+ +Your prepared branch: issue-58-bc11cbdc23dd
20
+ +Your prepared working directory: /tmp/gh-issue-solver-1765919093804
21
+ +
22
+ +Proceed.
23
+ diff --git a/docs/case-studies/issue-58/README.md b/docs/case-studies/issue-58/README.md
24
+ new file mode 100644
25
+ index 0000000..e1dfa5d
26
+ --- /dev/null
27
+ +++ b/docs/case-studies/issue-58/README.md
28
+ @@ -0,0 +1,227 @@
29
+ +# Case Study: Issue #58 - Release 0.1.0 Title and PR Link Bug
30
+ +
31
+ +## Issue Overview
32
+ +
33
+ +**Issue:** [#58](https://github.com/link-assistant/agent/issues/58)
34
+ +**Title:** Release 0.1.0 does show a title `Minor changes` and no linked pull request
35
+ +**Status:** In Progress
36
+ +**Created:** 2025-12-16
37
+ +
38
+ +### Problem Statement
39
+ +
40
+ +The release v0.1.0 shows two bugs:
41
+ +
42
+ +1. **Incorrect Title Display:** The release notes show "### Minor Changes" as a section header, but according to project requirements, there should be NO title for major/minor/patch version releases.
43
+ +2. **Missing PR Link:** The related pull request should be detected and shown in the release notes, but it is not present.
44
+ +
45
+ +## Timeline of Events
46
+ +
47
+ +### December 16, 2025
48
+ +
49
+ +1. **20:24:31 UTC** - Release v0.1.0 created by github-actions[bot]
50
+ +2. **20:26:20 UTC** - Release v0.1.0 published
51
+ +3. **Issue reported** - User identified that release has incorrect formatting
52
+ +
53
+ +## Data Collection
54
+ +
55
+ +### Release Data
56
+ +
57
+ +**Release URL:** https://github.com/link-assistant/agent/releases/tag/v0.1.0
58
+ +
59
+ +**Release Body (Raw):**
60
+ +
61
+ +```
62
+ +### Minor Changes
63
+ +
64
+ +- 2bcef5f: Add support for google/gemini-3-pro model alias
65
+ + - Added `google/gemini-3-pro` as an alias to `gemini-3-pro-preview`
66
+ + - Updated README.md with Google Gemini usage examples
67
+ + - Created comprehensive case study in docs/case-studies/issue-53/
68
+ + - Fixes ProviderModelNotFoundError when using google/gemini-3-pro
69
+ +
70
+ + This change allows users to use the commonly expected model name `gemini-3-pro` while maintaining compatibility with Google's official `gemini-3-pro-preview` identifier.
71
+ +```
72
+ +
73
+ +### CHANGELOG.md Content
74
+ +
75
+ +The CHANGELOG.md file (lines 1-14) shows:
76
+ +
77
+ +```markdown
78
+ +# Changelog
79
+ +
80
+ +## 0.1.0
81
+ +
82
+ +### Minor Changes
83
+ +
84
+ +- 2bcef5f: Add support for google/gemini-3-pro model alias
85
+ + - Added `google/gemini-3-pro` as an alias to `gemini-3-pro-preview`
86
+ + - Updated README.md with Google Gemini usage examples
87
+ + - Created comprehensive case study in docs/case-studies/issue-53/
88
+ + - Fixes ProviderModelNotFoundError when using google/gemini-3-pro
89
+ +
90
+ + This change allows users to use the commonly expected model name `gemini-3-pro` while maintaining compatibility with Google's official `gemini-3-pro-preview` identifier.
91
+ +```
92
+ +
93
+ +## Root Cause Analysis
94
+ +
95
+ +### Bug #1: Incorrect "### Minor Changes" Title
96
+ +
97
+ +**File:** `scripts/format-release-notes.mjs`
98
+ +**Lines:** 92-115
99
+ +
100
+ +The script only handles `### Patch Changes` section:
101
+ +
102
+ +```javascript
103
+ +// Extract the patch changes section
104
+ +// This regex handles two formats:
105
+ +// 1. With commit hash: "- abc1234: Description"
106
+ +// 2. Without commit hash: "- Description"
107
+ +const patchChangesMatchWithHash = currentBody.match(
108
+ + /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
109
+ +);
110
+ +const patchChangesMatchNoHash = currentBody.match(
111
+ + /### Patch Changes\s*\n\s*-\s+(.+?)$/s
112
+ +);
113
+ +```
114
+ +
115
+ +**Root Cause:**
116
+ +
117
+ +- The script ONLY matches `### Patch Changes` sections
118
+ +- When a release contains `### Minor Changes` or `### Major Changes`, the regex doesn't match
119
+ +- The script exits early with "⚠️ Could not parse patch changes from release notes" (line 113)
120
+ +- As a result, the formatting never happens and the "### Minor Changes" title remains in the release
121
+ +
122
+ +### Bug #2: Missing PR Link
123
+ +
124
+ +**Related to Bug #1**
125
+ +
126
+ +Because the script exits early when it can't find `### Patch Changes`, it never reaches the PR detection logic (lines 136-182). Therefore:
127
+ +
128
+ +- No commit SHA is extracted
129
+ +- No PR lookup is performed
130
+ +- No PR link is added to the release notes
131
+ +
132
+ +## Comparison with Template Repository
133
+ +
134
+ +### Template Repository Analysis
135
+ +
136
+ +**Repository:** https://github.com/link-foundation/js-ai-driven-development-pipeline-template
137
+ +**Latest Release:** v0.1.0 (2025-12-13)
138
+ +
139
+ +The template repository has the **EXACT SAME BUG**:
140
+ +
141
+ +```
142
+ +### Minor Changes
143
+ +
144
+ +- 65d76dc: Initial template setup with complete AI-driven development pipeline
145
+ + ...
146
+ +```
147
+ +
148
+ +The template's `scripts/format-release-notes.mjs` has identical code (lines 92-115) that only handles `### Patch Changes`.
149
+ +
150
+ +**Conclusion:** This bug exists in the upstream template and was inherited by our repository.
151
+ +
152
+ +## Proposed Solution
153
+ +
154
+ +### Fix for Our Repository
155
+ +
156
+ +Modify `scripts/format-release-notes.mjs` to:
157
+ +
158
+ +1. **Match all changeset types** (Major, Minor, Patch)
159
+ +2. **Remove the section header** (### Major Changes, ### Minor Changes, ### Patch Changes)
160
+ +3. **Extract commit hash and description** from any changeset type
161
+ +4. **Continue with PR detection and formatting**
162
+ +
163
+ +### Implementation Strategy
164
+ +
165
+ +1. Replace single regex patterns with a more flexible approach:
166
+ + - Match `### (Major|Minor|Patch) Changes` pattern
167
+ + - Extract the description regardless of the section type
168
+ + - Remove the section header entirely from the formatted output
169
+ +
170
+ +2. Update regex patterns:
171
+ +
172
+ +```javascript
173
+ +// Match any changeset type (Major, Minor, or Patch)
174
+ +const changesPattern =
175
+ + /### (Major|Minor|Patch) Changes\s*\n\s*-\s+(?:([a-f0-9]+):\s+)?(.+?)$/s;
176
+ +```
177
+ +
178
+ +3. Format output without section headers:
179
+ +
180
+ +```javascript
181
+ +// Remove "### X Changes" header entirely
182
+ +const formattedBody = `${cleanDescription}`;
183
+ +```
184
+ +
185
+ +### Fix for Template Repository
186
+ +
187
+ +Create an issue in the template repository:
188
+ +
189
+ +- **Repository:** link-foundation/js-ai-driven-development-pipeline-template
190
+ +- **Title:** Release formatting script only handles Patch changes, not Minor/Major
191
+ +- **Description:** Document the bug and provide the fix
192
+ +
193
+ +## Expected Outcomes
194
+ +
195
+ +### After Fix
196
+ +
197
+ +Release notes should look like:
198
+ +
199
+ +```
200
+ +Add support for google/gemini-3-pro model alias
201
+ +- Added `google/gemini-3-pro` as an alias to `gemini-3-pro-preview`
202
+ +- Updated README.md with Google Gemini usage examples
203
+ +- Created comprehensive case study in docs/case-studies/issue-53/
204
+ +- Fixes ProviderModelNotFoundError when using google/gemini-3-pro
205
+ +
206
+ +This change allows users to use the commonly expected model name `gemini-3-pro` while maintaining compatibility with Google's official `gemini-3-pro-preview` identifier.
207
+ +
208
+ +**Related Pull Request:** #56
209
+ +
210
+ +---
211
+ +
212
+ +[![npm version](https://img.shields.io/badge/npm-0.1.0-blue.svg)](https://www.npmjs.com/package/@link-assistant/agent/v/0.1.0)
213
+ +```
214
+ +
215
+ +**Key Changes:**
216
+ +
217
+ +1. ✅ NO "### Minor Changes" header
218
+ +2. ✅ Clean description starting directly with the content
219
+ +3. ✅ PR link detected and shown (#56)
220
+ +4. ✅ NPM badge included
221
+ +
222
+ +## Additional Research
223
+ +
224
+ +### Changesets Documentation
225
+ +
226
+ +According to [@changesets/cli](https://github.com/changesets/changesets) documentation:
227
+ +
228
+ +- Changesets generate CHANGELOG entries with section headers like "### Major Changes", "### Minor Changes", "### Patch Changes"
229
+ +- These are useful for the CHANGELOG.md file to organize changes by type
230
+ +- However, for GitHub Releases, these headers are redundant because:
231
+ + - The release version already indicates the type (0.1.0 is a minor version)
232
+ + - Users expect clean, concise release notes without internal categorization
233
+ +
234
+ +### Best Practices for Release Notes
235
+ +
236
+ +Research indicates that clean release notes should:
237
+ +
238
+ +1. Start directly with the content (no categorization headers)
239
+ +2. Include links to related PRs for context
240
+ +3. Include package version badge for quick reference
241
+ +4. Be formatted as clean markdown without internal structure headers
242
+ +
243
+ +## Files Modified
244
+ +
245
+ +1. `scripts/format-release-notes.mjs` - Fix regex patterns and remove section headers
246
+ +2. `docs/case-studies/issue-58/README.md` - This case study document
247
+ +3. `.changeset/fix-release-formatting.md` - Changeset for the fix
248
+ +
249
+ +## Verification Steps
250
+ +
251
+ +1. Run the fixed script against v0.1.0 release
252
+ +2. Verify "### Minor Changes" header is removed
253
+ +3. Verify PR #56 link is detected and added
254
+ +4. Verify NPM badge is added
255
+ +5. Check that formatting is preserved correctly
256
+ diff --git a/docs/case-studies/issue-58/release-v0.1.0-api.json b/docs/case-studies/issue-58/release-v0.1.0-api.json
257
+ new file mode 100644
258
+ index 0000000..5090394
259
+ --- /dev/null
260
+ +++ b/docs/case-studies/issue-58/release-v0.1.0-api.json
261
+ @@ -0,0 +1,42 @@
262
+ +{
263
+ + "url": "https://api.github.com/repos/link-assistant/agent/releases/270875722",
264
+ + "assets_url": "https://api.github.com/repos/link-assistant/agent/releases/270875722/assets",
265
+ + "upload_url": "https://uploads.github.com/repos/link-assistant/agent/releases/270875722/assets{?name,label}",
266
+ + "html_url": "https://github.com/link-assistant/agent/releases/tag/v0.1.0",
267
+ + "id": 270875722,
268
+ + "author": {
269
+ + "login": "github-actions[bot]",
270
+ + "id": 41898282,
271
+ + "node_id": "MDM6Qm90NDE4OTgyODI=",
272
+ + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
273
+ + "gravatar_id": "",
274
+ + "url": "https://api.github.com/users/github-actions%5Bbot%5D",
275
+ + "html_url": "https://github.com/apps/github-actions",
276
+ + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
277
+ + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
278
+ + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
279
+ + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
280
+ + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
281
+ + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
282
+ + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
283
+ + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
284
+ + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
285
+ + "type": "Bot",
286
+ + "user_view_type": "public",
287
+ + "site_admin": false
288
+ + },
289
+ + "node_id": "RE_kwDOQYTy3M4QJTxK",
290
+ + "tag_name": "v0.1.0",
291
+ + "target_commitish": "main",
292
+ + "name": "0.1.0",
293
+ + "draft": false,
294
+ + "immutable": false,
295
+ + "prerelease": false,
296
+ + "created_at": "2025-12-16T20:24:31Z",
297
+ + "updated_at": "2025-12-16T20:26:20Z",
298
+ + "published_at": "2025-12-16T20:26:20Z",
299
+ + "assets": [],
300
+ + "tarball_url": "https://api.github.com/repos/link-assistant/agent/tarball/v0.1.0",
301
+ + "zipball_url": "https://api.github.com/repos/link-assistant/agent/zipball/v0.1.0",
302
+ + "body": "### Minor Changes\n\n- 2bcef5f: Add support for google/gemini-3-pro model alias\n - Added `google/gemini-3-pro` as an alias to `gemini-3-pro-preview`\n - Updated README.md with Google Gemini usage examples\n - Created comprehensive case study in docs/case-studies/issue-53/\n - Fixes ProviderModelNotFoundError when using google/gemini-3-pro\n\n This change allows users to use the commonly expected model name `gemini-3-pro` while maintaining compatibility with Google's official `gemini-3-pro-preview` identifier."
303
+ +}
304
+ diff --git a/docs/case-studies/issue-58/release-v0.1.0.json b/docs/case-studies/issue-58/release-v0.1.0.json
305
+ new file mode 100644
306
+ index 0000000..37be6d8
307
+ --- /dev/null
308
+ +++ b/docs/case-studies/issue-58/release-v0.1.0.json
309
+ @@ -0,0 +1,10 @@
310
+ +{
311
+ + "author": { "id": "MDM6Qm90NDE4OTgyODI=", "login": "github-actions[bot]" },
312
+ + "body": "### Minor Changes\n\n- 2bcef5f: Add support for google/gemini-3-pro model alias\n - Added `google/gemini-3-pro` as an alias to `gemini-3-pro-preview`\n - Updated README.md with Google Gemini usage examples\n - Created comprehensive case study in docs/case-studies/issue-53/\n - Fixes ProviderModelNotFoundError when using google/gemini-3-pro\n\n This change allows users to use the commonly expected model name `gemini-3-pro` while maintaining compatibility with Google's official `gemini-3-pro-preview` identifier.",
313
+ + "createdAt": "2025-12-16T20:24:31Z",
314
+ + "name": "0.1.0",
315
+ + "publishedAt": "2025-12-16T20:26:20Z",
316
+ + "tagName": "v0.1.0",
317
+ + "targetCommitish": "main",
318
+ + "url": "https://github.com/link-assistant/agent/releases/tag/v0.1.0"
319
+ +}
320
+ diff --git a/docs/case-studies/issue-58/template-format-release-notes.mjs b/docs/case-studies/issue-58/template-format-release-notes.mjs
321
+ new file mode 100644
322
+ index 0000000..913791a
323
+ --- /dev/null
324
+ +++ b/docs/case-studies/issue-58/template-format-release-notes.mjs
325
+ @@ -0,0 +1,212 @@
326
+ +#!/usr/bin/env node
327
+ +
328
+ +/**
329
+ + * Script to format GitHub release notes with proper formatting:
330
+ + * - Fix special characters like \n
331
+ + * - Add link to PR that contains the release commit (if found)
332
+ + * - Add shields.io NPM version badge
333
+ + * - Format nicely with proper markdown
334
+ + *
335
+ + * IMPORTANT: Update the PACKAGE_NAME constant below to match your package.json
336
+ + *
337
+ + * PR Detection Logic:
338
+ + * 1. Extract commit hash from changelog entry (if present)
339
+ + * 2. Fall back to --commit-sha argument (passed from workflow)
340
+ + * 3. Look up PRs that contain the commit via GitHub API
341
+ + * 4. If no PR found, simply don't display any PR link (no guessing)
342
+ + *
343
+ + * Uses link-foundation libraries:
344
+ + * - use-m: Dynamic package loading without package.json dependencies
345
+ + * - command-stream: Modern shell command execution with streaming support
346
+ + * - lino-arguments: Unified configuration from CLI args, env vars, and .lenv files
347
+ + *
348
+ + * Note: Uses --release-version instead of --version to avoid conflict with yargs' built-in --version flag.
349
+ + */
350
+ +
351
+ +// TODO: Update this to match your package name in package.json
352
+ +const PACKAGE_NAME = 'my-package';
353
+ +
354
+ +// Load use-m dynamically
355
+ +const { use } = eval(
356
+ + await (await fetch('https://unpkg.com/use-m/use.js')).text()
357
+ +);
358
+ +
359
+ +// Import link-foundation libraries
360
+ +const { $ } = await use('command-stream');
361
+ +const { makeConfig } = await use('lino-arguments');
362
+ +
363
+ +// Parse CLI arguments using lino-arguments
364
+ +// Note: Using --release-version instead of --version to avoid conflict with yargs' built-in --version flag
365
+ +const config = makeConfig({
366
+ + yargs: ({ yargs, getenv }) =>
367
+ + yargs
368
+ + .option('release-version', {
369
+ + type: 'string',
370
+ + default: getenv('VERSION', ''),
371
+ + describe: 'Version number (e.g., v0.8.36)',
372
+ + })
373
+ + .option('release-id', {
374
+ + type: 'string',
375
+ + default: getenv('RELEASE_ID', ''),
376
+ + describe: 'GitHub release ID',
377
+ + })
378
+ + .option('repository', {
379
+ + type: 'string',
380
+ + default: getenv('REPOSITORY', ''),
381
+ + describe: 'GitHub repository (e.g., owner/repo)',
382
+ + })
383
+ + .option('commit-sha', {
384
+ + type: 'string',
385
+ + default: getenv('COMMIT_SHA', ''),
386
+ + describe: 'Commit SHA for PR detection',
387
+ + }),
388
+ +});
389
+ +
390
+ +const releaseId = config.releaseId;
391
+ +const version = config.releaseVersion;
392
+ +const repository = config.repository;
393
+ +const passedCommitSha = config.commitSha;
394
+ +
395
+ +if (!releaseId || !version || !repository) {
396
+ + console.error(
397
+ + 'Usage: format-release-notes.mjs --release-id <releaseId> --release-version <version> --repository <repository> [--commit-sha <sha>]'
398
+ + );
399
+ + process.exit(1);
400
+ +}
401
+ +
402
+ +try {
403
+ + // Get current release body
404
+ + const result = await $`gh api repos/${repository}/releases/${releaseId}`.run({
405
+ + capture: true,
406
+ + });
407
+ + const releaseData = JSON.parse(result.stdout);
408
+ +
409
+ + const currentBody = releaseData.body || '';
410
+ +
411
+ + // Skip if already formatted (has shields.io badge image)
412
+ + if (currentBody.includes('img.shields.io')) {
413
+ + console.log('ℹ️ Release notes already formatted');
414
+ + process.exit(0);
415
+ + }
416
+ +
417
+ + // Extract the patch changes section
418
+ + // This regex handles two formats:
419
+ + // 1. With commit hash: "- abc1234: Description"
420
+ + // 2. Without commit hash: "- Description"
421
+ + const patchChangesMatchWithHash = currentBody.match(
422
+ + /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
423
+ + );
424
+ + const patchChangesMatchNoHash = currentBody.match(
425
+ + /### Patch Changes\s*\n\s*-\s+(.+?)$/s
426
+ + );
427
+ +
428
+ + let commitHash = null;
429
+ + let rawDescription = null;
430
+ +
431
+ + if (patchChangesMatchWithHash) {
432
+ + // Format: - abc1234: Description
433
+ + [, commitHash, rawDescription] = patchChangesMatchWithHash;
434
+ + } else if (patchChangesMatchNoHash) {
435
+ + // Format: - Description (no commit hash)
436
+ + [, rawDescription] = patchChangesMatchNoHash;
437
+ + } else {
438
+ + console.log('⚠️ Could not parse patch changes from release notes');
439
+ + process.exit(0);
440
+ + }
441
+ +
442
+ + // Clean up the description:
443
+ + // 1. Convert literal \n sequences (escaped newlines from GitHub API) to actual newlines
444
+ + // 2. Remove leading/trailing quotes (including escaped quotes from command-stream shell escaping)
445
+ + // 3. Remove any trailing npm package links or markdown that might be there
446
+ + // 4. Normalize whitespace while preserving line breaks
447
+ + const cleanDescription = rawDescription
448
+ + .replace(/\\n/g, '\n') // Convert escaped \n to actual newlines
449
+ + .replace(/^(\\['"])+/g, '') // Remove leading escaped quotes (e.g., \', \", \'', \'')
450
+ + .replace(/(['"])+$/g, '') // Remove trailing unescaped quotes (e.g., ', ", '', '')
451
+ + .replace(/^(['"])+/g, '') // Remove leading unescaped quotes
452
+ + .replace(/📦.*$/s, '') // Remove any existing npm package info
453
+ + .replace(/---.*$/s, '') // Remove any existing separators and everything after
454
+ + .trim()
455
+ + .split('\n') // Split by lines
456
+ + .map((line) => line.trim()) // Trim whitespace from each line
457
+ + .join('\n') // Rejoin with newlines
458
+ + .replace(/\n{3,}/g, '\n\n'); // Normalize excessive blank lines (3+ becomes 2)
459
+ +
460
+ + // Find the PR that contains the release commit
461
+ + // Uses commit hash from changelog or passed commit SHA from workflow
462
+ + let prNumber = null;
463
+ +
464
+ + // Determine which commit SHA to use for PR lookup
465
+ + const commitShaToLookup = commitHash || passedCommitSha;
466
+ +
467
+ + if (commitShaToLookup) {
468
+ + const source = commitHash ? 'changelog' : 'workflow';
469
+ + console.log(
470
+ + `ℹ️ Looking up PR for commit ${commitShaToLookup} (from ${source})`
471
+ + );
472
+ +
473
+ + try {
474
+ + const prResult =
475
+ + await $`gh api "repos/${repository}/commits/${commitShaToLookup}/pulls"`.run(
476
+ + { capture: true }
477
+ + );
478
+ + const prsData = JSON.parse(prResult.stdout);
479
+ +
480
+ + // Find the PR that's not the version bump PR (not "chore: version packages")
481
+ + const relevantPr = prsData.find(
482
+ + (pr) => !pr.title.includes('version packages')
483
+ + );
484
+ +
485
+ + if (relevantPr) {
486
+ + prNumber = relevantPr.number;
487
+ + console.log(`✅ Found PR #${prNumber} containing commit`);
488
+ + } else if (prsData.length > 0) {
489
+ + console.log(
490
+ + '⚠️ Found PRs but all are version bump PRs, not linking any'
491
+ + );
492
+ + } else {
493
+ + console.log(
494
+ + 'ℹ️ No PR found containing this commit - not adding PR link'
495
+ + );
496
+ + }
497
+ + } catch (error) {
498
+ + console.log('⚠️ Could not find PR for commit', commitShaToLookup);
499
+ + console.log(' Error:', error.message);
500
+ + if (process.env.DEBUG) {
501
+ + console.error(error);
502
+ + }
503
+ + }
504
+ + } else {
505
+ + // No commit hash available from any source
506
+ + console.log('ℹ️ No commit SHA available - not adding PR link');
507
+ + }
508
+ +
509
+ + // Build formatted release notes
510
+ + const versionWithoutV = version.replace(/^v/, '');
511
+ + const npmBadge = `[![npm version](https://img.shields.io/badge/npm-${versionWithoutV}-blue.svg)](https://www.npmjs.com/package/${PACKAGE_NAME}/v/${versionWithoutV})`;
512
+ +
513
+ + let formattedBody = `${cleanDescription}`;
514
+ +
515
+ + // Add PR link if available
516
+ + if (prNumber) {
517
+ + formattedBody += `\n\n**Related Pull Request:** #${prNumber}`;
518
+ + }
519
+ +
520
+ + formattedBody += `\n\n---\n\n${npmBadge}`;
521
+ +
522
+ + // Update the release using JSON input to properly handle special characters
523
+ + const updatePayload = JSON.stringify({ body: formattedBody });
524
+ + await $`gh api repos/${repository}/releases/${releaseId} -X PATCH --input -`.run(
525
+ + { stdin: updatePayload }
526
+ + );
527
+ +
528
+ + console.log(`✅ Formatted release notes for v${versionWithoutV}`);
529
+ + if (prNumber) {
530
+ + console.log(` - Added link to PR #${prNumber}`);
531
+ + }
532
+ + console.log(' - Added shields.io npm badge');
533
+ + console.log(' - Cleaned up formatting');
534
+ +} catch (error) {
535
+ + console.error('❌ Error formatting release notes:', error.message);
536
+ + process.exit(1);
537
+ +}
538
+ diff --git a/package-lock.json b/package-lock.json
539
+ index 8ec099f..3c851c6 100644
540
+ --- a/package-lock.json
541
+ +++ b/package-lock.json
542
+ @@ -1,12 +1,12 @@
543
+ {
544
+ "name": "@link-assistant/agent",
545
+ - "version": "0.0.17",
546
+ + "version": "0.1.0",
547
+ "lockfileVersion": 3,
548
+ "requires": true,
549
+ "packages": {
550
+ "": {
551
+ "name": "@link-assistant/agent",
552
+ - "version": "0.0.17",
553
+ + "version": "0.1.0",
554
+ "license": "Unlicense",
555
+ "dependencies": {
556
+ "@actions/core": "^1.11.1",
557
+ diff --git a/scripts/format-release-notes.mjs b/scripts/format-release-notes.mjs
558
+ index 76fef1c..da0eec4 100644
559
+ --- a/scripts/format-release-notes.mjs
560
+ +++ b/scripts/format-release-notes.mjs
561
+ @@ -89,28 +89,35 @@ try {
562
+ process.exit(0);
563
+ }
564
+
565
+ - // Extract the patch changes section
566
+ - // This regex handles two formats:
567
+ - // 1. With commit hash: "- abc1234: Description"
568
+ - // 2. Without commit hash: "- Description"
569
+ - const patchChangesMatchWithHash = currentBody.match(
570
+ - /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
571
+ - );
572
+ - const patchChangesMatchNoHash = currentBody.match(
573
+ - /### Patch Changes\s*\n\s*-\s+(.+?)$/s
574
+ - );
575
+ + // Extract changes section (Major, Minor, or Patch)
576
+ + // This regex handles multiple formats:
577
+ + // 1. With commit hash: "### [Major|Minor|Patch] Changes\n- abc1234: Description"
578
+ + // 2. Without commit hash: "### [Major|Minor|Patch] Changes\n- Description"
579
+ + const changesPattern =
580
+ + /### (Major|Minor|Patch) Changes\s*\n\s*-\s+(?:([a-f0-9]+):\s+)?(.+?)$/s;
581
+ + const changesMatch = currentBody.match(changesPattern);
582
+
583
+ let commitHash = null;
584
+ let rawDescription = null;
585
+ -
586
+ - if (patchChangesMatchWithHash) {
587
+ - // Format: - abc1234: Description
588
+ - [, commitHash, rawDescription] = patchChangesMatchWithHash;
589
+ - } else if (patchChangesMatchNoHash) {
590
+ - // Format: - Description (no commit hash)
591
+ - [, rawDescription] = patchChangesMatchNoHash;
592
+ + let changeType = null;
593
+ +
594
+ + if (changesMatch) {
595
+ + // Extract: [full match, changeType, commitHash (optional), description]
596
+ + [, changeType, commitHash, rawDescription] = changesMatch;
597
+ + console.log(`ℹ️ Found ${changeType} Changes section`);
598
+ +
599
+ + // If commitHash is undefined and description contains it, try to extract
600
+ + if (!commitHash && rawDescription) {
601
+ + // This handles the case where description itself might be null/undefined
602
+ + // and we need to safely check for commit hash at the start
603
+ + const descWithHashMatch = rawDescription.match(/^([a-f0-9]+):\s+(.+)$/s);
604
+ + if (descWithHashMatch) {
605
+ + [, commitHash, rawDescription] = descWithHashMatch;
606
+ + }
607
+ + }
608
+ } else {
609
+ - console.log('⚠️ Could not parse patch changes from release notes');
610
+ + console.log('⚠️ Could not parse changes from release notes');
611
+ + console.log(' Looking for pattern: ### [Major|Minor|Patch] Changes');
612
+ process.exit(0);
613
+ }
614
+