@wipcomputer/wip-ai-devops-toolbox 1.9.68 → 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 +11 -0
- package/package.json +1 -1
- package/tools/wip-branch-guard/guard.mjs +5 -1
- package/tools/wip-branch-guard/package.json +1 -1
- package/tools/wip-release/core.mjs +75 -33
- package/tools/wip-release/package.json +1 -1
- package/tools/wip-branch-guard/RELEASE-NOTES-v1-9-64.md +0 -23
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
|
|
|
@@ -31,6 +38,10 @@
|
|
|
31
38
|
|
|
32
39
|
|
|
33
40
|
|
|
41
|
+
|
|
42
|
+
## 1.9.69-alpha.1 (2026-04-01)
|
|
43
|
+
|
|
44
|
+
alpha prerelease
|
|
34
45
|
|
|
35
46
|
## 1.9.68 (2026-04-01)
|
|
36
47
|
|
package/package.json
CHANGED
|
@@ -136,6 +136,8 @@ const ALLOWED_BASH_PATTERNS = [
|
|
|
136
136
|
/\brm\s+.*\.(openclaw|ldm)\/extensions\//, // cleaning deployed extensions (managed by ldm install, not source code)
|
|
137
137
|
/\bclaude\s+mcp\b/, // MCP registration, not repo files
|
|
138
138
|
/\bmkdir\s+.*\.worktrees\b/, // creating .worktrees/ directory is part of the process
|
|
139
|
+
/\brm\s+.*\.trash-approved-to-rm/, // Parker's approved-for-deletion folder (only Parker moves files here, agents only rm)
|
|
140
|
+
/\brm\s+.*\/_trash\//, // agent trash directories (agents can mv here and rm here)
|
|
139
141
|
];
|
|
140
142
|
|
|
141
143
|
// Workflow steps for error messages (#213)
|
|
@@ -320,7 +322,9 @@ Use the proper workflow: edit files in a worktree, commit, push, PR.`);
|
|
|
320
322
|
// Block npm install -g right after a release (#73)
|
|
321
323
|
// wip-release writes ~/.ldm/state/.last-release on completion.
|
|
322
324
|
// If a release happened < 5 minutes ago, block install unless user explicitly said "install".
|
|
323
|
-
|
|
325
|
+
// Exception: prerelease installs (@alpha, @beta) skip the cooldown. The cooldown exists
|
|
326
|
+
// to enforce dogfooding stable releases. Prerelease installs ARE the dogfooding.
|
|
327
|
+
if (/\bnpm\s+install\s+-g\b/.test(cmd) && !/@(alpha|beta)\b/.test(cmd)) {
|
|
324
328
|
try {
|
|
325
329
|
const releasePath = join(process.env.HOME || '', '.ldm', 'state', '.last-release');
|
|
326
330
|
if (existsSync(releasePath)) {
|
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
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
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
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.
|
|
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
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
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
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
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,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
|
-
```
|