@wipcomputer/wip-release 1.1.0 → 1.2.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 CHANGED
@@ -1,6 +1,16 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+
5
+
6
+ ## 1.2.0 (2026-02-21)
7
+
8
+ Add ClawHub publish as step 9 in release pipeline. Fix command injection by replacing execSync with execFileSync argument arrays. Declare required binaries and secrets in SKILL.md metadata.
9
+
10
+ ## 1.1.1 (2026-02-21)
11
+
12
+ Fix npm bin entry: rename cli.mjs to cli.js
13
+
4
14
  ## 1.1.0 (2026-02-21)
5
15
 
6
16
  Rich release notes, agent-driven install prompt, REFERENCE.md
package/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ###### WIP Computer
2
- # wip-release
2
+
3
+ [![npm](https://img.shields.io/npm/v/@wipcomputer/wip-release)](https://www.npmjs.com/package/@wipcomputer/wip-release) [![CLI / TUI](https://img.shields.io/badge/interface-CLI_/_TUI-black)](https://github.com/wipcomputer/wip-release/blob/main/cli.js) [![OpenClaw Skill](https://img.shields.io/badge/interface-OpenClaw_Skill-black)](https://clawhub.ai/parkertoddbrooks/wip-release) [![Claude Code Skill](https://img.shields.io/badge/interface-Claude_Code_Skill-black)](https://github.com/wipcomputer/wip-release/blob/main/SKILL.md) [![Universal Interface Spec](https://img.shields.io/badge/Universal_Interface_Spec-black?style=flat&color=black)](https://github.com/wipcomputer/wip-universal-installer/blob/main/SPEC.md)
4
+
5
+ # WIP.release
3
6
 
4
7
  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
8
 
@@ -24,6 +27,8 @@ Then ask me:
24
27
 
25
28
  Your agent will read the repo, explain the tool, and walk you through integration interactively.
26
29
 
30
+ Also see **[wip-file-guard](https://github.com/wipcomputer/wip-file-guard)** ... the lock for the repo. Blocks AI agents from overwriting your critical files.
31
+
27
32
  See [REFERENCE.md](REFERENCE.md) for full usage, pipeline steps, flags, auth, and module API.
28
33
 
29
34
  ---
package/REFERENCE.md CHANGED
@@ -30,6 +30,7 @@ wip-release patch --no-publish # bump + tag only
30
30
  ✓ Published to npm
31
31
  ✓ Published to GitHub Packages
32
32
  ✓ GitHub release v1.0.1 created
33
+ ✓ Published to ClawHub
33
34
 
34
35
  Done. wip-grok v1.0.1 released.
35
36
  ```
@@ -44,6 +45,7 @@ wip-release patch --no-publish # bump + tag only
44
45
  6. **npm publish** ... publishes to npmjs.com (auth via 1Password)
45
46
  7. **GitHub Packages** ... publishes to npm.pkg.github.com
46
47
  8. **GitHub release** ... creates release with changelog notes
48
+ 9. **ClawHub publish** ... publishes skill to ClawHub (if SKILL.md exists)
47
49
 
48
50
  ## Flags
49
51
 
@@ -62,6 +64,7 @@ Requires:
62
64
  - 1Password SA token at `~/.openclaw/secrets/op-sa-token`
63
65
  - "npm Token" item in "Agent Secrets" vault
64
66
  - `gh` CLI authenticated (for GitHub Packages and releases)
67
+ - `clawhub` CLI authenticated (for ClawHub skill publishing)
65
68
 
66
69
  ## As a Module
67
70
 
@@ -94,3 +97,4 @@ await release({
94
97
  | `publishGitHubPackages(repoPath)` | Publish to npm.pkg.github.com |
95
98
  | `createGitHubRelease(repoPath, newVersion, notes, currentVersion)` | Create GitHub release with rich notes |
96
99
  | `buildReleaseNotes(repoPath, currentVersion, newVersion, notes)` | Generate detailed release notes |
100
+ | `publishClawHub(repoPath, newVersion, notes)` | Publish skill to ClawHub |
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wip-release
3
- version: 1.1.0
3
+ version: 1.2.0
4
4
  description: Local release tool. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.
5
5
  homepage: https://github.com/wipcomputer/wip-release
6
6
  metadata:
@@ -13,6 +13,14 @@ metadata:
13
13
  - github-release
14
14
  dependencies: []
15
15
  interface: CLI
16
+ requires:
17
+ binaries: [git, npm, gh, op, clawhub]
18
+ secrets:
19
+ - path: ~/.openclaw/secrets/op-sa-token
20
+ description: 1Password service account token
21
+ - vault: Agent Secrets
22
+ item: npm Token
23
+ description: npm publish token
16
24
  openclaw:
17
25
  emoji: "🚀"
18
26
  install:
@@ -42,7 +50,6 @@ Local release pipeline. One command bumps version, updates all docs, publishes e
42
50
 
43
51
  - Pre-release / alpha versions (not yet supported)
44
52
  - Repos without a package.json
45
- - Publishing to ClawHub (separate step)
46
53
 
47
54
  ## API Reference
48
55
 
package/core.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  * Zero dependencies.
6
6
  */
7
7
 
8
- import { execSync } from 'node:child_process';
8
+ import { execSync, execFileSync } from 'node:child_process';
9
9
  import { readFileSync, writeFileSync, existsSync } from 'node:fs';
10
10
  import { join, basename } from 'node:path';
11
11
 
@@ -97,9 +97,15 @@ export function updateChangelog(repoPath, newVersion, notes) {
97
97
 
98
98
  function gitCommitAndTag(repoPath, newVersion, notes) {
99
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' });
100
+ // Stage known files (ignore missing ones)
101
+ for (const f of ['package.json', 'CHANGELOG.md', 'SKILL.md']) {
102
+ if (existsSync(join(repoPath, f))) {
103
+ execFileSync('git', ['add', f], { cwd: repoPath, stdio: 'pipe' });
104
+ }
105
+ }
106
+ // Use execFileSync to avoid shell injection via notes
107
+ execFileSync('git', ['commit', '-m', msg], { cwd: repoPath, stdio: 'pipe' });
108
+ execFileSync('git', ['tag', `v${newVersion}`], { cwd: repoPath, stdio: 'pipe' });
103
109
  }
104
110
 
105
111
  // ── Publish ─────────────────────────────────────────────────────────
@@ -109,10 +115,10 @@ function gitCommitAndTag(repoPath, newVersion, notes) {
109
115
  */
110
116
  export function publishNpm(repoPath) {
111
117
  const token = getNpmToken();
112
- execSync(
113
- `npm publish --access public --//registry.npmjs.org/:_authToken="${token}"`,
114
- { cwd: repoPath, stdio: 'inherit' }
115
- );
118
+ execFileSync('npm', [
119
+ 'publish', '--access', 'public',
120
+ `--//registry.npmjs.org/:_authToken=${token}`
121
+ ], { cwd: repoPath, stdio: 'inherit' });
116
122
  }
117
123
 
118
124
  /**
@@ -120,10 +126,11 @@ export function publishNpm(repoPath) {
120
126
  */
121
127
  export function publishGitHubPackages(repoPath) {
122
128
  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
- );
129
+ execFileSync('npm', [
130
+ 'publish',
131
+ '--registry', 'https://npm.pkg.github.com',
132
+ `--//npm.pkg.github.com/:_authToken=${ghToken}`
133
+ ], { cwd: repoPath, stdio: 'inherit' });
127
134
  }
128
135
 
129
136
  /**
@@ -145,17 +152,15 @@ export function buildReleaseNotes(repoPath, currentVersion, newVersion, notes) {
145
152
  const prevTag = `v${currentVersion}`;
146
153
  let commits = '';
147
154
  try {
148
- commits = execSync(
149
- `git log ${prevTag}..HEAD --pretty=format:"- %s (%h)" 2>/dev/null`,
150
- { cwd: repoPath, encoding: 'utf8' }
151
- ).trim();
155
+ commits = execFileSync('git', [
156
+ 'log', `${prevTag}..HEAD`, '--pretty=format:- %s (%h)'
157
+ ], { cwd: repoPath, encoding: 'utf8' }).trim();
152
158
  } catch {
153
159
  // No previous tag ... show all commits on branch
154
160
  try {
155
- commits = execSync(
156
- `git log --pretty=format:"- %s (%h)" -20`,
157
- { cwd: repoPath, encoding: 'utf8' }
158
- ).trim();
161
+ commits = execFileSync('git', [
162
+ 'log', '--pretty=format:- %s (%h)', '-20'
163
+ ], { cwd: repoPath, encoding: 'utf8' }).trim();
159
164
  } catch {}
160
165
  }
161
166
 
@@ -168,10 +173,9 @@ export function buildReleaseNotes(repoPath, currentVersion, newVersion, notes) {
168
173
  // Files changed
169
174
  let filesChanged = '';
170
175
  try {
171
- filesChanged = execSync(
172
- `git diff ${prevTag}..HEAD --stat 2>/dev/null`,
173
- { cwd: repoPath, encoding: 'utf8' }
174
- ).trim();
176
+ filesChanged = execFileSync('git', [
177
+ 'diff', `${prevTag}..HEAD`, '--stat'
178
+ ], { cwd: repoPath, encoding: 'utf8' }).trim();
175
179
  } catch {}
176
180
 
177
181
  if (filesChanged) {
@@ -216,15 +220,36 @@ export function createGitHubRelease(repoPath, newVersion, notes, currentVersion)
216
220
  writeFileSync(tmpFile, body);
217
221
 
218
222
  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
+ execFileSync('gh', [
224
+ 'release', 'create', `v${newVersion}`,
225
+ '--title', `v${newVersion}`,
226
+ '--notes-file', '.release-notes-tmp.md',
227
+ '--repo', repoSlug
228
+ ], { cwd: repoPath, stdio: 'inherit' });
223
229
  } finally {
224
- try { execSync(`rm -f "${tmpFile}"`); } catch {}
230
+ try { execFileSync('rm', ['-f', tmpFile]); } catch {}
225
231
  }
226
232
  }
227
233
 
234
+ /**
235
+ * Publish skill to ClawHub.
236
+ */
237
+ export function publishClawHub(repoPath, newVersion, notes) {
238
+ const skillPath = join(repoPath, 'SKILL.md');
239
+ if (!existsSync(skillPath)) return false;
240
+
241
+ const slug = detectSkillSlug(repoPath);
242
+ const changelog = notes || 'Release.';
243
+
244
+ execFileSync('clawhub', [
245
+ 'publish', repoPath,
246
+ '--slug', slug,
247
+ '--version', newVersion,
248
+ '--changelog', changelog
249
+ ], { cwd: repoPath, stdio: 'inherit' });
250
+ return true;
251
+ }
252
+
228
253
  // ── Helpers ──────────────────────────────────────────────────────────
229
254
 
230
255
  function getNpmToken() {
@@ -238,6 +263,14 @@ function getNpmToken() {
238
263
  }
239
264
  }
240
265
 
266
+ function detectSkillSlug(repoPath) {
267
+ const skillPath = join(repoPath, 'SKILL.md');
268
+ if (!existsSync(skillPath)) return basename(repoPath);
269
+ const content = readFileSync(skillPath, 'utf8');
270
+ const match = content.match(/^---[\s\S]*?name:\s*(.+?)[\s\n][\s\S]*?---/);
271
+ return match ? match[1].trim() : basename(repoPath);
272
+ }
273
+
241
274
  function detectRepoSlug(repoPath) {
242
275
  try {
243
276
  const url = execSync('git remote get-url origin', { cwd: repoPath, encoding: 'utf8' }).trim();
@@ -265,15 +298,16 @@ export async function release({ repoPath, level, notes, dryRun, noPublish }) {
265
298
  console.log(` ${'─'.repeat(40)}`);
266
299
 
267
300
  if (dryRun) {
301
+ const hasSkill = existsSync(join(repoPath, 'SKILL.md'));
268
302
  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`);
303
+ if (hasSkill) console.log(` [dry run] Would update SKILL.md version`);
271
304
  console.log(` [dry run] Would update CHANGELOG.md`);
272
305
  console.log(` [dry run] Would commit and tag v${newVersion}`);
273
306
  if (!noPublish) {
274
307
  console.log(` [dry run] Would publish to npm (@wipcomputer scope)`);
275
308
  console.log(` [dry run] Would publish to GitHub Packages`);
276
309
  console.log(` [dry run] Would create GitHub release v${newVersion}`);
310
+ if (hasSkill) console.log(` [dry run] Would publish to ClawHub`);
277
311
  }
278
312
  console.log('');
279
313
  console.log(` Dry run complete. No changes made.`);
@@ -330,6 +364,17 @@ export async function release({ repoPath, level, notes, dryRun, noPublish }) {
330
364
  } catch (e) {
331
365
  console.log(` ✗ GitHub release failed: ${e.message}`);
332
366
  }
367
+
368
+ // 9. ClawHub skill publish
369
+ const skillPath = join(repoPath, 'SKILL.md');
370
+ if (existsSync(skillPath)) {
371
+ try {
372
+ publishClawHub(repoPath, newVersion, notes);
373
+ console.log(` ✓ Published to ClawHub`);
374
+ } catch (e) {
375
+ console.log(` ✗ ClawHub publish failed: ${e.message}`);
376
+ }
377
+ }
333
378
  }
334
379
 
335
380
  console.log('');
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "Local release tool. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.",
6
6
  "main": "core.mjs",
7
7
  "bin": {
8
- "wip-release": "./cli.mjs"
8
+ "wip-release": "./cli.js"
9
9
  },
10
10
  "exports": {
11
11
  ".": "./core.mjs",
12
- "./cli": "./cli.mjs"
12
+ "./cli": "./cli.js"
13
13
  },
14
14
  "scripts": {
15
15
  "test": "node cli.mjs --help"
File without changes