@wipcomputer/wip-ai-devops-toolbox 1.9.69-alpha.1 → 1.9.70

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,7 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.70 (2026-04-01)
3
4
 
5
+ ### wip-release
6
+ - Fix semver NaN bug: `semver.inc()` returns null on prerelease versions, causing NaN propagation through the entire release pipeline
7
+ - Add sub-tool independent version validation: detects when sub-tool package.json versions drift from root and warns before publish
4
8
 
9
+ ### wip-branch-guard
10
+ - Add cooldown skip: avoids redundant guard checks within rapid tool sequences
11
+ - Add trash pattern exclusion: allows `.Trash` and system cleanup paths through the guard
5
12
 
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ai-devops-toolbox",
3
- "version": "1.9.69-alpha.1",
3
+ "version": "1.9.70",
4
4
  "type": "module",
5
5
  "description": "The complete AI DevOps toolkit for AI-assisted development teams.",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-branch-guard",
3
- "version": "1.9.68",
3
+ "version": "1.9.70",
4
4
  "description": "PreToolUse hook that blocks all writes on main branch. Forces agents to work on branches or worktrees.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -25,7 +25,8 @@ export function detectCurrentVersion(repoPath) {
25
25
  * Bump a semver string by level.
26
26
  */
27
27
  export function bumpSemver(version, level) {
28
- const [major, minor, patch] = version.split('.').map(Number);
28
+ const base = version.replace(/-.*$/, '');
29
+ const [major, minor, patch] = base.split('.').map(Number);
29
30
  switch (level) {
30
31
  case 'major': return `${major + 1}.0.0`;
31
32
  case 'minor': return `${major}.${minor + 1}.0`;
@@ -1674,27 +1675,33 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
1674
1675
  writePackageVersion(repoPath, newVersion);
1675
1676
  console.log(` ✓ package.json -> ${newVersion}`);
1676
1677
 
1677
- // 1.5. Bump sub-tool versions in toolbox repos (tools/*/)
1678
+ // 1.5. Validate sub-tool version bumps in toolbox repos (tools/*/)
1679
+ // Sub-tools have independent versions. If files changed since last tag
1680
+ // but the version didn't bump, warn. Developer must bump manually.
1678
1681
  const toolsDir = join(repoPath, 'tools');
1679
1682
  if (existsSync(toolsDir)) {
1680
- let subBumped = 0;
1681
- try {
1682
- const entries = readdirSync(toolsDir, { withFileTypes: true });
1683
- for (const entry of entries) {
1684
- if (!entry.isDirectory()) continue;
1685
- const subPkgPath = join(toolsDir, entry.name, 'package.json');
1686
- if (existsSync(subPkgPath)) {
1683
+ const lastTag = (() => { try { return execFileSync('git', ['describe', '--tags', '--abbrev=0'], { cwd: repoPath, encoding: 'utf8' }).trim(); } catch { return null; } })();
1684
+ if (lastTag) {
1685
+ try {
1686
+ const entries = readdirSync(toolsDir, { withFileTypes: true });
1687
+ for (const entry of entries) {
1688
+ if (!entry.isDirectory()) continue;
1689
+ const subDir = join('tools', entry.name);
1690
+ const subPkgPath = join(toolsDir, entry.name, 'package.json');
1691
+ if (!existsSync(subPkgPath)) continue;
1687
1692
  try {
1688
- const subPkg = JSON.parse(readFileSync(subPkgPath, 'utf8'));
1689
- subPkg.version = newVersion;
1690
- writeFileSync(subPkgPath, JSON.stringify(subPkg, null, 2) + '\n');
1691
- subBumped++;
1693
+ const diff = execFileSync('git', ['diff', '--name-only', lastTag, 'HEAD', '--', subDir], { cwd: repoPath, encoding: 'utf8' }).trim();
1694
+ if (!diff) continue;
1695
+ const currentSubVersion = JSON.parse(readFileSync(subPkgPath, 'utf8')).version;
1696
+ const oldSubVersion = (() => { try { return JSON.parse(execFileSync('git', ['show', `${lastTag}:${subDir}/package.json`], { cwd: repoPath, encoding: 'utf8' })).version; } catch { return null; } })();
1697
+ if (currentSubVersion === oldSubVersion) {
1698
+ console.log(` ! WARNING: ${entry.name} has changed files since ${lastTag} but version is still ${currentSubVersion}`);
1699
+ console.log(` Changed: ${diff.split('\n').join(', ')}`);
1700
+ console.log(` Bump tools/${entry.name}/package.json before releasing.`);
1701
+ }
1692
1702
  } catch {}
1693
1703
  }
1694
- }
1695
- } catch {}
1696
- if (subBumped > 0) {
1697
- console.log(` ✓ ${subBumped} sub-tool(s) -> ${newVersion}`);
1704
+ } catch {}
1698
1705
  }
1699
1706
  }
1700
1707
 
@@ -2040,6 +2047,37 @@ export async function releasePrerelease({ repoPath, track, notes, dryRun, noPubl
2040
2047
  writePackageVersion(repoPath, newVersion);
2041
2048
  console.log(` \u2713 package.json -> ${newVersion}`);
2042
2049
 
2050
+ // 1.5. Validate sub-tool version bumps in toolbox repos (tools/*/)
2051
+ // If a sub-tool's files changed since the last tag but its version didn't bump, warn.
2052
+ const toolsDir = join(repoPath, 'tools');
2053
+ if (existsSync(toolsDir)) {
2054
+ const lastTag = (() => { try { return execFileSync('git', ['describe', '--tags', '--abbrev=0'], { cwd: repoPath, encoding: 'utf8' }).trim(); } catch { return null; } })();
2055
+ if (lastTag) {
2056
+ try {
2057
+ const entries = readdirSync(toolsDir, { withFileTypes: true });
2058
+ for (const entry of entries) {
2059
+ if (!entry.isDirectory()) continue;
2060
+ const subDir = join('tools', entry.name);
2061
+ const subPkgPath = join(toolsDir, entry.name, 'package.json');
2062
+ if (!existsSync(subPkgPath)) continue;
2063
+ // Check if any files in this sub-tool changed since last tag
2064
+ try {
2065
+ const diff = execFileSync('git', ['diff', '--name-only', lastTag, 'HEAD', '--', subDir], { cwd: repoPath, encoding: 'utf8' }).trim();
2066
+ if (!diff) continue; // No changes, skip
2067
+ // Files changed. Check if version was bumped.
2068
+ const currentSubVersion = JSON.parse(readFileSync(subPkgPath, 'utf8')).version;
2069
+ const oldSubVersion = (() => { try { return JSON.parse(execFileSync('git', ['show', `${lastTag}:${subDir}/package.json`], { cwd: repoPath, encoding: 'utf8' })).version; } catch { return null; } })();
2070
+ if (currentSubVersion === oldSubVersion) {
2071
+ console.log(` ! WARNING: ${entry.name} has changed files since ${lastTag} but version is still ${currentSubVersion}`);
2072
+ console.log(` Changed: ${diff.split('\n').join(', ')}`);
2073
+ console.log(` Bump tools/${entry.name}/package.json before releasing.`);
2074
+ }
2075
+ } catch {}
2076
+ }
2077
+ } catch {}
2078
+ }
2079
+ }
2080
+
2043
2081
  // 2. Update CHANGELOG.md (lightweight entry)
2044
2082
  updateChangelog(repoPath, newVersion, notes || `${track} prerelease`);
2045
2083
  console.log(` \u2713 CHANGELOG.md updated`);
@@ -2241,27 +2279,31 @@ export async function releaseHotfix({ repoPath, notes, notesSource, dryRun, noPu
2241
2279
  writePackageVersion(repoPath, newVersion);
2242
2280
  console.log(` \u2713 package.json -> ${newVersion}`);
2243
2281
 
2244
- // 1.5. Bump sub-tool versions
2282
+ // 1.5. Validate sub-tool version bumps in toolbox repos (tools/*/)
2245
2283
  const toolsDir = join(repoPath, 'tools');
2246
2284
  if (existsSync(toolsDir)) {
2247
- let subBumped = 0;
2248
- try {
2249
- const entries = readdirSync(toolsDir, { withFileTypes: true });
2250
- for (const entry of entries) {
2251
- if (!entry.isDirectory()) continue;
2252
- const subPkgPath = join(toolsDir, entry.name, 'package.json');
2253
- if (existsSync(subPkgPath)) {
2285
+ const lastTag = (() => { try { return execFileSync('git', ['describe', '--tags', '--abbrev=0'], { cwd: repoPath, encoding: 'utf8' }).trim(); } catch { return null; } })();
2286
+ if (lastTag) {
2287
+ try {
2288
+ const entries = readdirSync(toolsDir, { withFileTypes: true });
2289
+ for (const entry of entries) {
2290
+ if (!entry.isDirectory()) continue;
2291
+ const subDir = join('tools', entry.name);
2292
+ const subPkgPath = join(toolsDir, entry.name, 'package.json');
2293
+ if (!existsSync(subPkgPath)) continue;
2254
2294
  try {
2255
- const subPkg = JSON.parse(readFileSync(subPkgPath, 'utf8'));
2256
- subPkg.version = newVersion;
2257
- writeFileSync(subPkgPath, JSON.stringify(subPkg, null, 2) + '\n');
2258
- subBumped++;
2295
+ const diff = execFileSync('git', ['diff', '--name-only', lastTag, 'HEAD', '--', subDir], { cwd: repoPath, encoding: 'utf8' }).trim();
2296
+ if (!diff) continue;
2297
+ const currentSubVersion = JSON.parse(readFileSync(subPkgPath, 'utf8')).version;
2298
+ const oldSubVersion = (() => { try { return JSON.parse(execFileSync('git', ['show', `${lastTag}:${subDir}/package.json`], { cwd: repoPath, encoding: 'utf8' })).version; } catch { return null; } })();
2299
+ if (currentSubVersion === oldSubVersion) {
2300
+ console.log(` ! WARNING: ${entry.name} has changed files since ${lastTag} but version is still ${currentSubVersion}`);
2301
+ console.log(` Changed: ${diff.split('\n').join(', ')}`);
2302
+ console.log(` Bump tools/${entry.name}/package.json before releasing.`);
2303
+ }
2259
2304
  } catch {}
2260
2305
  }
2261
- }
2262
- } catch {}
2263
- if (subBumped > 0) {
2264
- console.log(` \u2713 ${subBumped} sub-tool(s) -> ${newVersion}`);
2306
+ } catch {}
2265
2307
  }
2266
2308
  }
2267
2309
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.9.68",
3
+ "version": "1.9.70",
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",
@@ -1,23 +0,0 @@
1
- # Release Notes: wip-branch-guard v1.9.64
2
-
3
- **One-line summary of what this release does**
4
-
5
- Tell the story. What was broken or missing? What did we build? Why does the user care?
6
- Write at least one real paragraph of prose. Not just bullets. The release notes gate
7
- will block if there is no narrative. Bullets are fine for details, but the story comes first.
8
-
9
- ## The story
10
-
11
- (Write a paragraph here. What was the problem? What does this release fix? Why does it matter?
12
- This is what users read. Make it worth reading.)
13
-
14
- ## Issues closed
15
-
16
- - #296
17
- - #295
18
-
19
- ## How to verify
20
-
21
- ```bash
22
- # Commands to test the changes
23
- ```