@wipcomputer/wip-release 1.1.1 → 1.2.2

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
@@ -2,6 +2,16 @@
2
2
 
3
3
 
4
4
 
5
+
6
+
7
+ ## 1.2.2 (2026-02-21)
8
+
9
+ Fix ClawHub display name and slug detection. Harden command injection fix.
10
+
11
+ ## 1.2.0 (2026-02-21)
12
+
13
+ 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.
14
+
5
15
  ## 1.1.1 (2026-02-21)
6
16
 
7
17
  Fix npm bin entry: rename cli.mjs to cli.js
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
- name: wip-release
3
- version: 1.1.1
2
+ name: WIP.release
3
+ version: 1.2.2
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,12 @@ function getNpmToken() {
238
263
  }
239
264
  }
240
265
 
266
+ function detectSkillSlug(repoPath) {
267
+ // Slug must be lowercase and url-safe. Use directory name, not SKILL.md name
268
+ // (SKILL.md name can be display-formatted like "WIP.release").
269
+ return basename(repoPath).toLowerCase();
270
+ }
271
+
241
272
  function detectRepoSlug(repoPath) {
242
273
  try {
243
274
  const url = execSync('git remote get-url origin', { cwd: repoPath, encoding: 'utf8' }).trim();
@@ -265,15 +296,16 @@ export async function release({ repoPath, level, notes, dryRun, noPublish }) {
265
296
  console.log(` ${'─'.repeat(40)}`);
266
297
 
267
298
  if (dryRun) {
299
+ const hasSkill = existsSync(join(repoPath, 'SKILL.md'));
268
300
  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`);
301
+ if (hasSkill) console.log(` [dry run] Would update SKILL.md version`);
271
302
  console.log(` [dry run] Would update CHANGELOG.md`);
272
303
  console.log(` [dry run] Would commit and tag v${newVersion}`);
273
304
  if (!noPublish) {
274
305
  console.log(` [dry run] Would publish to npm (@wipcomputer scope)`);
275
306
  console.log(` [dry run] Would publish to GitHub Packages`);
276
307
  console.log(` [dry run] Would create GitHub release v${newVersion}`);
308
+ if (hasSkill) console.log(` [dry run] Would publish to ClawHub`);
277
309
  }
278
310
  console.log('');
279
311
  console.log(` Dry run complete. No changes made.`);
@@ -330,6 +362,17 @@ export async function release({ repoPath, level, notes, dryRun, noPublish }) {
330
362
  } catch (e) {
331
363
  console.log(` ✗ GitHub release failed: ${e.message}`);
332
364
  }
365
+
366
+ // 9. ClawHub skill publish
367
+ const skillPath = join(repoPath, 'SKILL.md');
368
+ if (existsSync(skillPath)) {
369
+ try {
370
+ publishClawHub(repoPath, newVersion, notes);
371
+ console.log(` ✓ Published to ClawHub`);
372
+ } catch (e) {
373
+ console.log(` ✗ ClawHub publish failed: ${e.message}`);
374
+ }
375
+ }
333
376
  }
334
377
 
335
378
  console.log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.1.1",
3
+ "version": "1.2.2",
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",