@wipcomputer/wip-release 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+
4
+ ## 1.1.0 (2026-02-21)
5
+
6
+ Rich release notes, agent-driven install prompt, REFERENCE.md
7
+
8
+ ## 1.0.0 (2026-02-21)
9
+
10
+ Initial release. Local release pipeline tool.
11
+
12
+ - `release()` ... full pipeline: bump, changelog, skill sync, commit, tag, publish
13
+ - `detectCurrentVersion()` ... read version from package.json
14
+ - `syncSkillVersion()` ... update SKILL.md frontmatter
15
+ - `updateChangelog()` ... prepend version entry
16
+ - `publishNpm()` ... npm publish via 1Password
17
+ - `publishGitHubPackages()` ... GitHub Packages publish
18
+ - `createGitHubRelease()` ... gh release create
19
+
20
+ CLI: `wip-release patch|minor|major [--notes --dry-run --no-publish]`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Parker Todd Brooks
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ ###### WIP Computer
2
+ # wip-release
3
+
4
+ You ship a fix. Now you have to bump package.json, update CHANGELOG.md, sync the version in SKILL.md, commit, tag, push, publish to npm, publish to GitHub Packages, and create a GitHub release. Every time. Miss a step and versions drift.
5
+
6
+ `wip-release` does all of it in one command.
7
+
8
+ ## Install
9
+
10
+ Open your AI coding tool and say:
11
+
12
+ ```
13
+ Read the REFERENCE.md and SKILL.md at github.com/wipcomputer/wip-release.
14
+ Then explain to me:
15
+ 1. What is this tool?
16
+ 2. What does it do?
17
+ 3. What would it change or fix in our current release process?
18
+
19
+ Then ask me:
20
+ - Do you have more questions?
21
+ - Do you want to integrate it into our system?
22
+ - Do you want to clone it (use as-is) or fork it (so you can contribute back if you find bugs)?
23
+ ```
24
+
25
+ Your agent will read the repo, explain the tool, and walk you through integration interactively.
26
+
27
+ See [REFERENCE.md](REFERENCE.md) for full usage, pipeline steps, flags, auth, and module API.
28
+
29
+ ---
30
+
31
+ ## License
32
+
33
+ MIT
34
+
35
+ Built by Parker Todd Brooks, with Claude Code and Lēsa (OpenClaw).
package/REFERENCE.md ADDED
@@ -0,0 +1,96 @@
1
+ ###### WIP Computer
2
+ # wip-release ... Reference
3
+
4
+ Detailed usage, pipeline steps, flags, auth, and module API.
5
+
6
+ ## Usage
7
+
8
+ Run from inside any repo:
9
+
10
+ ```bash
11
+ wip-release patch # 1.0.0 -> 1.0.1
12
+ wip-release minor # 1.0.0 -> 1.1.0
13
+ wip-release major # 1.0.0 -> 2.0.0
14
+
15
+ wip-release patch --notes="fix auth config" # with changelog note
16
+ wip-release minor --dry-run # preview, no changes
17
+ wip-release patch --no-publish # bump + tag only
18
+ ```
19
+
20
+ ## What It Does
21
+
22
+ ```
23
+ wip-grok: 1.0.0 -> 1.0.1 (patch)
24
+ ────────────────────────────────────────
25
+ ✓ package.json -> 1.0.1
26
+ ✓ SKILL.md -> 1.0.1
27
+ ✓ CHANGELOG.md updated
28
+ ✓ Committed and tagged v1.0.1
29
+ ✓ Pushed to remote
30
+ ✓ Published to npm
31
+ ✓ Published to GitHub Packages
32
+ ✓ GitHub release v1.0.1 created
33
+
34
+ Done. wip-grok v1.0.1 released.
35
+ ```
36
+
37
+ ## Pipeline Steps
38
+
39
+ 1. **Bump `package.json`** ... patch, minor, or major
40
+ 2. **Sync `SKILL.md`** ... updates version in YAML frontmatter (if file exists)
41
+ 3. **Update `CHANGELOG.md`** ... prepends new version entry with date and notes
42
+ 4. **Git commit + tag** ... commits changed files, creates `vX.Y.Z` tag
43
+ 5. **Push** ... pushes commit and tag to remote
44
+ 6. **npm publish** ... publishes to npmjs.com (auth via 1Password)
45
+ 7. **GitHub Packages** ... publishes to npm.pkg.github.com
46
+ 8. **GitHub release** ... creates release with changelog notes
47
+
48
+ ## Flags
49
+
50
+ | Flag | What |
51
+ |------|------|
52
+ | `--notes="text"` | Changelog entry text |
53
+ | `--dry-run` | Show what would happen, change nothing |
54
+ | `--no-publish` | Bump + tag only, skip npm and GitHub release |
55
+
56
+ ## Auth
57
+
58
+ npm token is fetched from 1Password at publish time. No `.npmrc` files stored. No credentials in repos.
59
+
60
+ Requires:
61
+ - `op` CLI installed and configured
62
+ - 1Password SA token at `~/.openclaw/secrets/op-sa-token`
63
+ - "npm Token" item in "Agent Secrets" vault
64
+ - `gh` CLI authenticated (for GitHub Packages and releases)
65
+
66
+ ## As a Module
67
+
68
+ ```javascript
69
+ import { release, detectCurrentVersion, bumpSemver } from '@wipcomputer/wip-release';
70
+
71
+ const current = detectCurrentVersion('/path/to/repo');
72
+ const next = bumpSemver(current, 'minor');
73
+ console.log(`${current} -> ${next}`);
74
+
75
+ await release({
76
+ repoPath: '/path/to/repo',
77
+ level: 'patch',
78
+ notes: 'fix auth',
79
+ dryRun: false,
80
+ noPublish: false,
81
+ });
82
+ ```
83
+
84
+ ## Exports
85
+
86
+ | Function | What |
87
+ |----------|------|
88
+ | `release({ repoPath, level, notes, dryRun, noPublish })` | Full pipeline |
89
+ | `detectCurrentVersion(repoPath)` | Read version from package.json |
90
+ | `bumpSemver(version, level)` | Bump a semver string |
91
+ | `syncSkillVersion(repoPath, newVersion)` | Update SKILL.md frontmatter |
92
+ | `updateChangelog(repoPath, newVersion, notes)` | Prepend to CHANGELOG.md |
93
+ | `publishNpm(repoPath)` | Publish to npmjs.com |
94
+ | `publishGitHubPackages(repoPath)` | Publish to npm.pkg.github.com |
95
+ | `createGitHubRelease(repoPath, newVersion, notes, currentVersion)` | Create GitHub release with rich notes |
96
+ | `buildReleaseNotes(repoPath, currentVersion, newVersion, notes)` | Generate detailed release notes |
package/SKILL.md ADDED
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: wip-release
3
+ version: 1.1.0
4
+ description: Local release tool. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.
5
+ homepage: https://github.com/wipcomputer/wip-release
6
+ metadata:
7
+ category: dev-tools
8
+ capabilities:
9
+ - version-bump
10
+ - changelog-update
11
+ - skill-sync
12
+ - npm-publish
13
+ - github-release
14
+ dependencies: []
15
+ interface: CLI
16
+ openclaw:
17
+ emoji: "🚀"
18
+ install:
19
+ env: []
20
+ author:
21
+ name: Parker Todd Brooks
22
+ ---
23
+
24
+ # wip-release
25
+
26
+ Local release pipeline. One command bumps version, updates all docs, publishes everywhere.
27
+
28
+ ## When to Use This Skill
29
+
30
+ **Use wip-release for:**
31
+ - Releasing a new version of any @wipcomputer package
32
+ - After merging a PR to main and you need to publish
33
+ - Bumping version + changelog + SKILL.md in one step
34
+
35
+ **Use --dry-run for:**
36
+ - Previewing what a release would do before committing
37
+
38
+ **Use --no-publish for:**
39
+ - Bumping version and tagging without publishing to registries
40
+
41
+ ### Do NOT Use For
42
+
43
+ - Pre-release / alpha versions (not yet supported)
44
+ - Repos without a package.json
45
+ - Publishing to ClawHub (separate step)
46
+
47
+ ## API Reference
48
+
49
+ ### CLI
50
+
51
+ ```bash
52
+ wip-release patch --notes="fix X" # full pipeline
53
+ wip-release minor --dry-run # preview only
54
+ wip-release major --no-publish # bump + tag only
55
+ ```
56
+
57
+ ### Module
58
+
59
+ ```javascript
60
+ import { release, detectCurrentVersion, bumpSemver, syncSkillVersion } from '@wipcomputer/wip-release';
61
+
62
+ await release({ repoPath: '.', level: 'patch', notes: 'fix', dryRun: false, noPublish: false });
63
+ ```
64
+
65
+ ## Troubleshooting
66
+
67
+ ### "Could not fetch npm token from 1Password"
68
+ Check that `~/.openclaw/secrets/op-sa-token` exists and `op` CLI is installed.
69
+
70
+ ### "Push failed"
71
+ Branch protection may prevent direct pushes. Make sure you're on main after merging a PR.
72
+
73
+ ### SKILL.md not updated
74
+ Only updates if the file has a YAML frontmatter `version:` field between `---` markers.
package/cli.mjs ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * wip-release/cli.mjs
5
+ * Release tool CLI. Bumps version, updates docs, publishes.
6
+ */
7
+
8
+ import { release, detectCurrentVersion } from './core.mjs';
9
+
10
+ const args = process.argv.slice(2);
11
+ const level = args.find(a => ['patch', 'minor', 'major'].includes(a));
12
+
13
+ function flag(name) {
14
+ const prefix = `--${name}=`;
15
+ const found = args.find(a => a.startsWith(prefix));
16
+ return found ? found.slice(prefix.length) : null;
17
+ }
18
+
19
+ const dryRun = args.includes('--dry-run');
20
+ const noPublish = args.includes('--no-publish');
21
+ const notes = flag('notes');
22
+
23
+ if (!level || args.includes('--help') || args.includes('-h')) {
24
+ const cwd = process.cwd();
25
+ let current = '';
26
+ try { current = ` (current: ${detectCurrentVersion(cwd)})`; } catch {}
27
+
28
+ console.log(`wip-release ... local release tool${current}
29
+
30
+ Usage:
31
+ wip-release patch 1.0.0 -> 1.0.1
32
+ wip-release minor 1.0.0 -> 1.1.0
33
+ wip-release major 1.0.0 -> 2.0.0
34
+
35
+ Flags:
36
+ --notes="description" Changelog entry text
37
+ --dry-run Show what would happen, change nothing
38
+ --no-publish Bump + tag only, skip npm/GitHub
39
+
40
+ Pipeline:
41
+ 1. Bump package.json version
42
+ 2. Sync SKILL.md version (if exists)
43
+ 3. Update CHANGELOG.md
44
+ 4. Git commit + tag
45
+ 5. Push to remote
46
+ 6. npm publish (via 1Password)
47
+ 7. GitHub Packages publish
48
+ 8. GitHub release create`);
49
+ process.exit(level ? 0 : 1);
50
+ }
51
+
52
+ release({
53
+ repoPath: process.cwd(),
54
+ level,
55
+ notes,
56
+ dryRun,
57
+ noPublish,
58
+ }).catch(err => {
59
+ console.error(` ✗ ${err.message}`);
60
+ process.exit(1);
61
+ });
package/core.mjs ADDED
@@ -0,0 +1,340 @@
1
+ /**
2
+ * wip-release/core.mjs
3
+ * Local release tool. Bumps version, updates changelog + SKILL.md,
4
+ * commits, tags, publishes to npm + GitHub Packages, creates GitHub release.
5
+ * Zero dependencies.
6
+ */
7
+
8
+ import { execSync } from 'node:child_process';
9
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
10
+ import { join, basename } from 'node:path';
11
+
12
+ // ── Version ─────────────────────────────────────────────────────────
13
+
14
+ /**
15
+ * Read current version from package.json.
16
+ */
17
+ export function detectCurrentVersion(repoPath) {
18
+ const pkgPath = join(repoPath, 'package.json');
19
+ if (!existsSync(pkgPath)) throw new Error(`No package.json found at ${repoPath}`);
20
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
21
+ return pkg.version;
22
+ }
23
+
24
+ /**
25
+ * Bump a semver string by level.
26
+ */
27
+ export function bumpSemver(version, level) {
28
+ const [major, minor, patch] = version.split('.').map(Number);
29
+ switch (level) {
30
+ case 'major': return `${major + 1}.0.0`;
31
+ case 'minor': return `${major}.${minor + 1}.0`;
32
+ case 'patch': return `${major}.${minor}.${patch + 1}`;
33
+ default: throw new Error(`Invalid level: ${level}. Use major, minor, or patch.`);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Write new version to package.json.
39
+ */
40
+ function writePackageVersion(repoPath, newVersion) {
41
+ const pkgPath = join(repoPath, 'package.json');
42
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
43
+ pkg.version = newVersion;
44
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
45
+ }
46
+
47
+ // ── SKILL.md ────────────────────────────────────────────────────────
48
+
49
+ /**
50
+ * Update version in SKILL.md YAML frontmatter.
51
+ */
52
+ export function syncSkillVersion(repoPath, newVersion) {
53
+ const skillPath = join(repoPath, 'SKILL.md');
54
+ if (!existsSync(skillPath)) return false;
55
+
56
+ let content = readFileSync(skillPath, 'utf8');
57
+ // Match version: X.Y.Z in YAML frontmatter (between --- markers)
58
+ const updated = content.replace(
59
+ /^(---[\s\S]*?)(version:\s*)\S+([\s\S]*?---)/,
60
+ `$1$2${newVersion}$3`
61
+ );
62
+
63
+ if (updated === content) return false;
64
+ writeFileSync(skillPath, updated);
65
+ return true;
66
+ }
67
+
68
+ // ── CHANGELOG.md ────────────────────────────────────────────────────
69
+
70
+ /**
71
+ * Prepend a new version entry to CHANGELOG.md.
72
+ */
73
+ export function updateChangelog(repoPath, newVersion, notes) {
74
+ const changelogPath = join(repoPath, 'CHANGELOG.md');
75
+ const date = new Date().toISOString().split('T')[0];
76
+ const entry = `## ${newVersion} (${date})\n\n${notes || 'Release.'}\n`;
77
+
78
+ if (!existsSync(changelogPath)) {
79
+ writeFileSync(changelogPath, `# Changelog\n\n${entry}\n`);
80
+ return;
81
+ }
82
+
83
+ let content = readFileSync(changelogPath, 'utf8');
84
+ // Insert after the # Changelog header
85
+ const headerMatch = content.match(/^# Changelog\s*\n/);
86
+ if (headerMatch) {
87
+ const insertPoint = headerMatch[0].length;
88
+ content = content.slice(0, insertPoint) + '\n' + entry + '\n' + content.slice(insertPoint);
89
+ } else {
90
+ content = `# Changelog\n\n${entry}\n${content}`;
91
+ }
92
+
93
+ writeFileSync(changelogPath, content);
94
+ }
95
+
96
+ // ── Git ─────────────────────────────────────────────────────────────
97
+
98
+ function gitCommitAndTag(repoPath, newVersion, notes) {
99
+ const msg = `v${newVersion}: ${notes || 'Release'}`;
100
+ execSync(`git add package.json CHANGELOG.md SKILL.md 2>/dev/null; git add package.json CHANGELOG.md`, { cwd: repoPath, stdio: 'pipe' });
101
+ execSync(`git commit -m "${msg.replace(/"/g, '\\"')}"`, { cwd: repoPath, stdio: 'pipe' });
102
+ execSync(`git tag v${newVersion}`, { cwd: repoPath, stdio: 'pipe' });
103
+ }
104
+
105
+ // ── Publish ─────────────────────────────────────────────────────────
106
+
107
+ /**
108
+ * Publish to npm via 1Password for auth.
109
+ */
110
+ export function publishNpm(repoPath) {
111
+ const token = getNpmToken();
112
+ execSync(
113
+ `npm publish --access public --//registry.npmjs.org/:_authToken="${token}"`,
114
+ { cwd: repoPath, stdio: 'inherit' }
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Publish to GitHub Packages.
120
+ */
121
+ export function publishGitHubPackages(repoPath) {
122
+ const ghToken = execSync('gh auth token', { encoding: 'utf8' }).trim();
123
+ execSync(
124
+ `npm publish --registry https://npm.pkg.github.com --//npm.pkg.github.com/:_authToken="${ghToken}"`,
125
+ { cwd: repoPath, stdio: 'inherit' }
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Build detailed release notes from git history and repo metadata.
131
+ */
132
+ export function buildReleaseNotes(repoPath, currentVersion, newVersion, notes) {
133
+ const slug = detectRepoSlug(repoPath);
134
+ const pkg = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
135
+ const lines = [];
136
+
137
+ // What changed section
138
+ lines.push('## What changed\n');
139
+ if (notes) {
140
+ lines.push(notes);
141
+ lines.push('');
142
+ }
143
+
144
+ // Commits since last tag
145
+ const prevTag = `v${currentVersion}`;
146
+ let commits = '';
147
+ try {
148
+ commits = execSync(
149
+ `git log ${prevTag}..HEAD --pretty=format:"- %s (%h)" 2>/dev/null`,
150
+ { cwd: repoPath, encoding: 'utf8' }
151
+ ).trim();
152
+ } catch {
153
+ // No previous tag ... show all commits on branch
154
+ try {
155
+ commits = execSync(
156
+ `git log --pretty=format:"- %s (%h)" -20`,
157
+ { cwd: repoPath, encoding: 'utf8' }
158
+ ).trim();
159
+ } catch {}
160
+ }
161
+
162
+ if (commits) {
163
+ lines.push('### Commits\n');
164
+ lines.push(commits);
165
+ lines.push('');
166
+ }
167
+
168
+ // Files changed
169
+ let filesChanged = '';
170
+ try {
171
+ filesChanged = execSync(
172
+ `git diff ${prevTag}..HEAD --stat 2>/dev/null`,
173
+ { cwd: repoPath, encoding: 'utf8' }
174
+ ).trim();
175
+ } catch {}
176
+
177
+ if (filesChanged) {
178
+ lines.push('### Files changed\n');
179
+ lines.push('```');
180
+ lines.push(filesChanged);
181
+ lines.push('```');
182
+ lines.push('');
183
+ }
184
+
185
+ // Install section
186
+ lines.push('### Install\n');
187
+ lines.push('```bash');
188
+ lines.push(`npm install -g ${pkg.name}@${newVersion}`);
189
+ lines.push('```');
190
+ lines.push('');
191
+ lines.push('Or update your local clone:');
192
+ lines.push('```bash');
193
+ lines.push('git pull origin main');
194
+ lines.push('```');
195
+ lines.push('');
196
+
197
+ // Compare URL
198
+ if (slug) {
199
+ lines.push('---');
200
+ lines.push('');
201
+ lines.push(`Full changelog: https://github.com/${slug}/compare/v${currentVersion}...v${newVersion}`);
202
+ }
203
+
204
+ return lines.join('\n');
205
+ }
206
+
207
+ /**
208
+ * Create a GitHub release with detailed notes.
209
+ */
210
+ export function createGitHubRelease(repoPath, newVersion, notes, currentVersion) {
211
+ const repoSlug = detectRepoSlug(repoPath);
212
+ const body = buildReleaseNotes(repoPath, currentVersion, newVersion, notes);
213
+
214
+ // Write notes to a temp file to avoid shell escaping issues
215
+ const tmpFile = join(repoPath, '.release-notes-tmp.md');
216
+ writeFileSync(tmpFile, body);
217
+
218
+ try {
219
+ execSync(
220
+ `gh release create v${newVersion} --title "v${newVersion}" --notes-file .release-notes-tmp.md --repo ${repoSlug}`,
221
+ { cwd: repoPath, stdio: 'inherit' }
222
+ );
223
+ } finally {
224
+ try { execSync(`rm -f "${tmpFile}"`); } catch {}
225
+ }
226
+ }
227
+
228
+ // ── Helpers ──────────────────────────────────────────────────────────
229
+
230
+ function getNpmToken() {
231
+ try {
232
+ return execSync(
233
+ `OP_SERVICE_ACCOUNT_TOKEN=$(cat ~/.openclaw/secrets/op-sa-token) op item get "npm Token" --vault "Agent Secrets" --fields label=password --reveal 2>/dev/null`,
234
+ { encoding: 'utf8' }
235
+ ).trim();
236
+ } catch {
237
+ throw new Error('Could not fetch npm token from 1Password. Check op CLI and SA token.');
238
+ }
239
+ }
240
+
241
+ function detectRepoSlug(repoPath) {
242
+ try {
243
+ const url = execSync('git remote get-url origin', { cwd: repoPath, encoding: 'utf8' }).trim();
244
+ // git@github.com:wipcomputer/wip-grok.git or https://github.com/wipcomputer/wip-grok.git
245
+ const match = url.match(/github\.com[:/](.+?)(?:\.git)?$/);
246
+ return match ? match[1] : null;
247
+ } catch {
248
+ return null;
249
+ }
250
+ }
251
+
252
+ // ── Main ────────────────────────────────────────────────────────────
253
+
254
+ /**
255
+ * Run the full release pipeline.
256
+ */
257
+ export async function release({ repoPath, level, notes, dryRun, noPublish }) {
258
+ repoPath = repoPath || process.cwd();
259
+ const currentVersion = detectCurrentVersion(repoPath);
260
+ const newVersion = bumpSemver(currentVersion, level);
261
+ const repoName = basename(repoPath);
262
+
263
+ console.log('');
264
+ console.log(` ${repoName}: ${currentVersion} -> ${newVersion} (${level})`);
265
+ console.log(` ${'─'.repeat(40)}`);
266
+
267
+ if (dryRun) {
268
+ console.log(` [dry run] Would bump package.json to ${newVersion}`);
269
+ const skillPath = join(repoPath, 'SKILL.md');
270
+ if (existsSync(skillPath)) console.log(` [dry run] Would update SKILL.md version`);
271
+ console.log(` [dry run] Would update CHANGELOG.md`);
272
+ console.log(` [dry run] Would commit and tag v${newVersion}`);
273
+ if (!noPublish) {
274
+ console.log(` [dry run] Would publish to npm (@wipcomputer scope)`);
275
+ console.log(` [dry run] Would publish to GitHub Packages`);
276
+ console.log(` [dry run] Would create GitHub release v${newVersion}`);
277
+ }
278
+ console.log('');
279
+ console.log(` Dry run complete. No changes made.`);
280
+ console.log('');
281
+ return { currentVersion, newVersion, dryRun: true };
282
+ }
283
+
284
+ // 1. Bump package.json
285
+ writePackageVersion(repoPath, newVersion);
286
+ console.log(` ✓ package.json -> ${newVersion}`);
287
+
288
+ // 2. Sync SKILL.md
289
+ if (syncSkillVersion(repoPath, newVersion)) {
290
+ console.log(` ✓ SKILL.md -> ${newVersion}`);
291
+ }
292
+
293
+ // 3. Update CHANGELOG.md
294
+ updateChangelog(repoPath, newVersion, notes);
295
+ console.log(` ✓ CHANGELOG.md updated`);
296
+
297
+ // 4. Git commit + tag
298
+ gitCommitAndTag(repoPath, newVersion, notes);
299
+ console.log(` ✓ Committed and tagged v${newVersion}`);
300
+
301
+ // 5. Push commit + tag
302
+ try {
303
+ execSync('git push && git push --tags', { cwd: repoPath, stdio: 'pipe' });
304
+ console.log(` ✓ Pushed to remote`);
305
+ } catch {
306
+ console.log(` ! Push failed (maybe branch protection). Push manually.`);
307
+ }
308
+
309
+ if (!noPublish) {
310
+ // 6. npm publish
311
+ try {
312
+ publishNpm(repoPath);
313
+ console.log(` ✓ Published to npm`);
314
+ } catch (e) {
315
+ console.log(` ✗ npm publish failed: ${e.message}`);
316
+ }
317
+
318
+ // 7. GitHub Packages
319
+ try {
320
+ publishGitHubPackages(repoPath);
321
+ console.log(` ✓ Published to GitHub Packages`);
322
+ } catch (e) {
323
+ console.log(` ✗ GitHub Packages publish failed: ${e.message}`);
324
+ }
325
+
326
+ // 8. GitHub release
327
+ try {
328
+ createGitHubRelease(repoPath, newVersion, notes, currentVersion);
329
+ console.log(` ✓ GitHub release v${newVersion} created`);
330
+ } catch (e) {
331
+ console.log(` ✗ GitHub release failed: ${e.message}`);
332
+ }
333
+ }
334
+
335
+ console.log('');
336
+ console.log(` Done. ${repoName} v${newVersion} released.`);
337
+ console.log('');
338
+
339
+ return { currentVersion, newVersion, dryRun: false };
340
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@wipcomputer/wip-release",
3
+ "version": "1.1.0",
4
+ "type": "module",
5
+ "description": "Local release tool. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.",
6
+ "main": "core.mjs",
7
+ "bin": {
8
+ "wip-release": "./cli.mjs"
9
+ },
10
+ "exports": {
11
+ ".": "./core.mjs",
12
+ "./cli": "./cli.mjs"
13
+ },
14
+ "scripts": {
15
+ "test": "node cli.mjs --help"
16
+ },
17
+ "keywords": [
18
+ "release",
19
+ "version",
20
+ "publish",
21
+ "npm",
22
+ "agent-native",
23
+ "sensor",
24
+ "actuator"
25
+ ],
26
+ "author": "Parker Todd Brooks",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/wipcomputer/wip-release.git"
31
+ },
32
+ "homepage": "https://github.com/wipcomputer/wip-release"
33
+ }