@wipcomputer/wip-ai-devops-toolbox 1.9.27 → 1.9.28
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 +29 -0
- package/SKILL.md +1 -1
- package/package.json +1 -1
- package/scripts/deploy-public.sh +38 -3
- package/scripts/post-merge-rename.sh +5 -0
- package/tools/deploy-public/package.json +1 -1
- package/tools/post-merge-rename/package.json +1 -1
- package/tools/wip-branch-guard/package.json +1 -1
- package/tools/wip-file-guard/package.json +1 -1
- package/tools/wip-license-guard/package.json +1 -1
- package/tools/wip-license-hook/package.json +1 -1
- package/tools/wip-readme-format/format.mjs +20 -0
- package/tools/wip-readme-format/package.json +1 -1
- package/tools/wip-release/core.mjs +42 -5
- package/tools/wip-release/package.json +1 -1
- package/tools/wip-repo-init/package.json +1 -1
- package/tools/wip-repo-permissions-hook/package.json +1 -1
- package/tools/wip-repos/package.json +1 -1
- package/tools/wip-universal-installer/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -31,6 +31,35 @@
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
|
|
35
|
+
## 1.9.28 (2026-03-15)
|
|
36
|
+
|
|
37
|
+
# Release Notes Quality Gate
|
|
38
|
+
|
|
39
|
+
**Date:** 2026-03-15
|
|
40
|
+
|
|
41
|
+
## What changed
|
|
42
|
+
|
|
43
|
+
wip-release now blocks ALL releases (patch, minor, major) if the release notes are bad. Previously, patch releases only warned. Now they block.
|
|
44
|
+
|
|
45
|
+
The gate checks:
|
|
46
|
+
- Notes must be at least 50 characters
|
|
47
|
+
- Notes can't look like a changelog entry ("fix: ...", "add: ...", "update: ...")
|
|
48
|
+
- Minor/major still require a file (not --notes flag)
|
|
49
|
+
|
|
50
|
+
If the gate blocks, it tells you exactly how to fix it: write a RELEASE-NOTES file, write a dev update, or use --notes with at least 50 chars of real description.
|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
|
|
54
|
+
Release notes were consistently garbage. One-liner --notes flags like "Fix bug" or "Update docs" sailed through on patch releases. The warnings were ignored by both humans and agents. Every release page on GitHub had thin, useless notes that didn't explain what changed or why.
|
|
55
|
+
|
|
56
|
+
## Also in this release
|
|
57
|
+
|
|
58
|
+
- wip-repo-init templates renamed from ai/ to templates/ so they ship with npm install (deploy-public.sh was stripping them)
|
|
59
|
+
- SKILL.md restart notice after install (hooks need session restart)
|
|
60
|
+
- SPEC.md and TECHNICAL.md updated with all 17 tools and LDM OS links
|
|
61
|
+
- Branch guard matcher fix (catches Bash + NotebookEdit)
|
|
62
|
+
- Forced Git Worktrees and Branch Guard sections added to SKILL.md
|
|
34
63
|
|
|
35
64
|
## 1.9.27 (2026-03-15)
|
|
36
65
|
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
interface: [cli, module, mcp, skill, hook, plugin]
|
|
6
6
|
metadata:
|
|
7
7
|
display-name: "WIP AI DevOps Toolbox"
|
|
8
|
-
version: "1.9.
|
|
8
|
+
version: "1.9.28"
|
|
9
9
|
homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
|
|
10
10
|
author: "Parker Todd Brooks"
|
|
11
11
|
category: dev-tools
|
package/package.json
CHANGED
package/scripts/deploy-public.sh
CHANGED
|
@@ -18,12 +18,29 @@
|
|
|
18
18
|
|
|
19
19
|
set -euo pipefail
|
|
20
20
|
|
|
21
|
-
PRIVATE_REPO="$1"
|
|
22
|
-
PUBLIC_REPO="$2"
|
|
21
|
+
PRIVATE_REPO="${1:-}"
|
|
22
|
+
PUBLIC_REPO="${2:-}"
|
|
23
|
+
DRY_RUN=false
|
|
24
|
+
|
|
25
|
+
# Parse flags
|
|
26
|
+
for arg in "$@"; do
|
|
27
|
+
case "$arg" in
|
|
28
|
+
--dry-run) DRY_RUN=true ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
# Strip flags from positional args
|
|
33
|
+
ARGS=()
|
|
34
|
+
for arg in "$@"; do
|
|
35
|
+
[[ "$arg" == --* ]] || ARGS+=("$arg")
|
|
36
|
+
done
|
|
37
|
+
PRIVATE_REPO="${ARGS[0]:-}"
|
|
38
|
+
PUBLIC_REPO="${ARGS[1]:-}"
|
|
23
39
|
|
|
24
40
|
if [[ -z "$PRIVATE_REPO" || -z "$PUBLIC_REPO" ]]; then
|
|
25
|
-
echo "Usage: bash deploy-public.sh <private-repo-path> <public-github-repo>"
|
|
41
|
+
echo "Usage: bash deploy-public.sh <private-repo-path> <public-github-repo> [--dry-run]"
|
|
26
42
|
echo "Example: bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal"
|
|
43
|
+
echo " bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal --dry-run"
|
|
27
44
|
exit 1
|
|
28
45
|
fi
|
|
29
46
|
|
|
@@ -123,6 +140,24 @@ fi
|
|
|
123
140
|
BRANCH="$HARNESS_ID/deploy-$(date +%Y%m%d-%H%M%S)"
|
|
124
141
|
|
|
125
142
|
git add -A
|
|
143
|
+
|
|
144
|
+
# Dry-run: show what would be deployed, then stop
|
|
145
|
+
if $DRY_RUN; then
|
|
146
|
+
echo ""
|
|
147
|
+
echo " Dry run: deploy-public.sh"
|
|
148
|
+
echo " ────────────────────────────────────"
|
|
149
|
+
echo " Source: $PRIVATE_REPO"
|
|
150
|
+
echo " Target: $PUBLIC_REPO"
|
|
151
|
+
echo " Commit: $COMMIT_MSG ($COMMIT_HASH)"
|
|
152
|
+
echo ""
|
|
153
|
+
echo " Files that would change:"
|
|
154
|
+
git diff --cached --stat 2>/dev/null || git diff --stat HEAD 2>/dev/null || echo " (new files)"
|
|
155
|
+
git ls-files --others --exclude-standard | head -20 | while read f; do echo " + $f"; done
|
|
156
|
+
echo ""
|
|
157
|
+
echo " Dry run complete. No changes pushed."
|
|
158
|
+
exit 0
|
|
159
|
+
fi
|
|
160
|
+
|
|
126
161
|
git commit -m "$COMMIT_MSG (from $COMMIT_HASH)"
|
|
127
162
|
|
|
128
163
|
if [[ "$EMPTY_REPO" == "true" ]]; then
|
|
@@ -159,6 +159,11 @@ prune_branches() {
|
|
|
159
159
|
if [[ $count -le $KEEP_COUNT ]]; then
|
|
160
160
|
echo " ✓ KEEP $branch"
|
|
161
161
|
else
|
|
162
|
+
# Safety: verify branch is actually merged into main before deleting
|
|
163
|
+
if ! git merge-base --is-ancestor "origin/$branch" origin/main 2>/dev/null; then
|
|
164
|
+
echo " ! SKIP $branch (NOT merged into main despite --merged suffix)"
|
|
165
|
+
continue
|
|
166
|
+
fi
|
|
162
167
|
if $DRY_RUN; then
|
|
163
168
|
echo " [dry-run] DELETE $branch"
|
|
164
169
|
else
|
|
@@ -337,6 +337,26 @@ if (DEPLOY) {
|
|
|
337
337
|
process.exit(1);
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
// Safety: init files must be reviewed (committed or modified) before deploy.
|
|
341
|
+
// If all init files are untracked (just generated, never reviewed), block.
|
|
342
|
+
try {
|
|
343
|
+
const { execSync } = await import('node:child_process');
|
|
344
|
+
const initFiles = readdirSync(repoPath).filter(f => f.startsWith('README-init-'));
|
|
345
|
+
const allUntracked = initFiles.every(f => {
|
|
346
|
+
try {
|
|
347
|
+
const status = execSync(`git status --porcelain "${f}"`, { cwd: repoPath, encoding: 'utf8' }).trim();
|
|
348
|
+
return status.startsWith('??');
|
|
349
|
+
} catch { return false; }
|
|
350
|
+
});
|
|
351
|
+
if (allUntracked && initFiles.length > 0) {
|
|
352
|
+
fail('Init files have not been reviewed. They are all untracked (just generated).');
|
|
353
|
+
console.log(' Review the README-init-*.md files, edit as needed, then git add them before deploying.');
|
|
354
|
+
console.log(' Or commit them first so there is a review trail.');
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
} catch {}
|
|
358
|
+
|
|
359
|
+
|
|
340
360
|
const date = new Date().toISOString().slice(0, 10);
|
|
341
361
|
const aiTrash = join(repoPath, 'ai', '_trash');
|
|
342
362
|
|
|
@@ -252,6 +252,44 @@ function checkReleaseNotes(notes, notesSource, level) {
|
|
|
252
252
|
return { ok: issues.length === 0, issues, block: issues.length > 0 };
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Scaffold a RELEASE-NOTES-v{version}.md template if one doesn't exist.
|
|
257
|
+
* Called when the release notes gate blocks. Gives the agent a file to fill in.
|
|
258
|
+
*/
|
|
259
|
+
export function scaffoldReleaseNotes(repoPath, version) {
|
|
260
|
+
const dashed = version.replace(/\./g, '-');
|
|
261
|
+
const notesPath = join(repoPath, `RELEASE-NOTES-v${dashed}.md`);
|
|
262
|
+
if (existsSync(notesPath)) return notesPath;
|
|
263
|
+
|
|
264
|
+
const pkg = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf8'));
|
|
265
|
+
const name = pkg.name?.replace(/^@[^/]+\//, '') || basename(repoPath);
|
|
266
|
+
|
|
267
|
+
const template = `# Release Notes: ${name} v${version}
|
|
268
|
+
|
|
269
|
+
**One-line summary of what this release does**
|
|
270
|
+
|
|
271
|
+
## What changed
|
|
272
|
+
|
|
273
|
+
Describe the changes. Not a commit list. Explain:
|
|
274
|
+
- What was built or fixed
|
|
275
|
+
- Why it matters
|
|
276
|
+
- What the user should know
|
|
277
|
+
|
|
278
|
+
## Why
|
|
279
|
+
|
|
280
|
+
What problem does this solve? What was broken or missing?
|
|
281
|
+
|
|
282
|
+
## How to verify
|
|
283
|
+
|
|
284
|
+
\`\`\`bash
|
|
285
|
+
# Commands to test the changes
|
|
286
|
+
\`\`\`
|
|
287
|
+
`;
|
|
288
|
+
|
|
289
|
+
writeFileSync(notesPath, template);
|
|
290
|
+
return notesPath;
|
|
291
|
+
}
|
|
292
|
+
|
|
255
293
|
/**
|
|
256
294
|
* Check if a file was modified in commits since the last git tag.
|
|
257
295
|
*/
|
|
@@ -783,11 +821,10 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
783
821
|
console.log(` ✗ Release notes blocked:`);
|
|
784
822
|
for (const issue of notesCheck.issues) console.log(` - ${issue}`);
|
|
785
823
|
console.log('');
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
console.log(
|
|
789
|
-
console.log('
|
|
790
|
-
console.log(' 3. Use --notes="at least 50 chars explaining the change and its impact"');
|
|
824
|
+
// Scaffold a template so the agent has something to fill in
|
|
825
|
+
const templatePath = scaffoldReleaseNotes(repoPath, newVersion);
|
|
826
|
+
console.log(` Scaffolded template: ${basename(templatePath)}`);
|
|
827
|
+
console.log(' Fill it in, commit, then run wip-release again.');
|
|
791
828
|
console.log('');
|
|
792
829
|
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
793
830
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wipcomputer/universal-installer",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The Universal Interface specification for agent-native software. Teaches your AI how to build repos with every interface: CLI, Module, MCP Server, OpenClaw Plugin, Skill, Claude Code Hook.",
|
|
6
6
|
"main": "detect.mjs",
|