@wipcomputer/wip-ai-devops-toolbox 1.9.20
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/.license-guard.json +7 -0
- package/.publish-skill.json +4 -0
- package/CHANGELOG.md +1120 -0
- package/CLA.md +19 -0
- package/DEV-GUIDE-GENERAL-PUBLIC.md +882 -0
- package/LICENSE +52 -0
- package/README.md +238 -0
- package/SKILL.md +728 -0
- package/TECHNICAL.md +282 -0
- package/UNIVERSAL-INTERFACE.md +180 -0
- package/_trash/RELEASE-NOTES-v1-8-0.md +29 -0
- package/_trash/RELEASE-NOTES-v1-8-1.md +7 -0
- package/_trash/RELEASE-NOTES-v1-8-2.md +7 -0
- package/_trash/RELEASE-NOTES-v1-9-0.md +37 -0
- package/_trash/RELEASE-NOTES-v1-9-1.md +38 -0
- package/_trash/RELEASE-NOTES-v1-9-10.md +40 -0
- package/_trash/RELEASE-NOTES-v1-9-2.md +40 -0
- package/_trash/RELEASE-NOTES-v1-9-6.md +72 -0
- package/_trash/RELEASE-NOTES-v1-9-7.md +23 -0
- package/_trash/RELEASE-NOTES-v1-9-9.md +75 -0
- package/_trash/guide 2/DEV-GUIDE.md +487 -0
- package/_trash/guide 2/scripts/deploy-public.sh +152 -0
- package/package.json +27 -0
- package/scripts/SKILL-deploy-public.md +61 -0
- package/scripts/SKILL-post-merge-rename.md +47 -0
- package/scripts/deploy-public.sh +264 -0
- package/scripts/post-merge-rename.sh +205 -0
- package/scripts/publish-skill.sh +134 -0
- package/tools/deploy-public/LICENSE +52 -0
- package/tools/deploy-public/README.md +31 -0
- package/tools/deploy-public/SKILL.md +71 -0
- package/tools/deploy-public/deploy-public.sh +264 -0
- package/tools/deploy-public/package.json +9 -0
- package/tools/ldm-jobs/LICENSE +52 -0
- package/tools/ldm-jobs/README.md +46 -0
- package/tools/ldm-jobs/backup.sh +16 -0
- package/tools/ldm-jobs/branch-protect.sh +39 -0
- package/tools/ldm-jobs/crystal-capture.sh +19 -0
- package/tools/ldm-jobs/setup-shell.sh +27 -0
- package/tools/ldm-jobs/visibility-audit.sh +27 -0
- package/tools/post-merge-rename/LICENSE +52 -0
- package/tools/post-merge-rename/README.md +29 -0
- package/tools/post-merge-rename/SKILL.md +57 -0
- package/tools/post-merge-rename/package.json +9 -0
- package/tools/post-merge-rename/post-merge-rename.sh +122 -0
- package/tools/wip-branch-guard/INSTALL.md +41 -0
- package/tools/wip-branch-guard/guard.mjs +259 -0
- package/tools/wip-branch-guard/package.json +11 -0
- package/tools/wip-file-guard/CHANGELOG.md +6 -0
- package/tools/wip-file-guard/LICENSE +52 -0
- package/tools/wip-file-guard/README.md +113 -0
- package/tools/wip-file-guard/REFERENCE.md +86 -0
- package/tools/wip-file-guard/SKILL.md +105 -0
- package/tools/wip-file-guard/guard.mjs +128 -0
- package/tools/wip-file-guard/openclaw.plugin.json +8 -0
- package/tools/wip-file-guard/package.json +27 -0
- package/tools/wip-file-guard/test.sh +119 -0
- package/tools/wip-license-guard/LICENSE +52 -0
- package/tools/wip-license-guard/README.md +32 -0
- package/tools/wip-license-guard/SKILL.md +65 -0
- package/tools/wip-license-guard/cli.mjs +464 -0
- package/tools/wip-license-guard/core.mjs +310 -0
- package/tools/wip-license-guard/hook.mjs +146 -0
- package/tools/wip-license-guard/package.json +15 -0
- package/tools/wip-license-hook/CHANGELOG.md +17 -0
- package/tools/wip-license-hook/LICENSE +52 -0
- package/tools/wip-license-hook/README.md +200 -0
- package/tools/wip-license-hook/SKILL.md +111 -0
- package/tools/wip-license-hook/dist/cli/index.d.ts +15 -0
- package/tools/wip-license-hook/dist/cli/index.js +170 -0
- package/tools/wip-license-hook/dist/cli/index.js.map +1 -0
- package/tools/wip-license-hook/dist/core/detector.d.ts +12 -0
- package/tools/wip-license-hook/dist/core/detector.js +104 -0
- package/tools/wip-license-hook/dist/core/detector.js.map +1 -0
- package/tools/wip-license-hook/dist/core/index.d.ts +4 -0
- package/tools/wip-license-hook/dist/core/index.js +5 -0
- package/tools/wip-license-hook/dist/core/index.js.map +1 -0
- package/tools/wip-license-hook/dist/core/ledger.d.ts +49 -0
- package/tools/wip-license-hook/dist/core/ledger.js +72 -0
- package/tools/wip-license-hook/dist/core/ledger.js.map +1 -0
- package/tools/wip-license-hook/dist/core/reporter.d.ts +14 -0
- package/tools/wip-license-hook/dist/core/reporter.js +227 -0
- package/tools/wip-license-hook/dist/core/reporter.js.map +1 -0
- package/tools/wip-license-hook/dist/core/scanner.d.ts +39 -0
- package/tools/wip-license-hook/dist/core/scanner.js +325 -0
- package/tools/wip-license-hook/dist/core/scanner.js.map +1 -0
- package/tools/wip-license-hook/hooks/pre-pull.sh +55 -0
- package/tools/wip-license-hook/hooks/pre-push.sh +51 -0
- package/tools/wip-license-hook/mcp-server.mjs +119 -0
- package/tools/wip-license-hook/package-lock.json +54 -0
- package/tools/wip-license-hook/package.json +43 -0
- package/tools/wip-license-hook/src/cli/index.ts +189 -0
- package/tools/wip-license-hook/src/core/detector.ts +130 -0
- package/tools/wip-license-hook/src/core/index.ts +4 -0
- package/tools/wip-license-hook/src/core/ledger.ts +116 -0
- package/tools/wip-license-hook/src/core/reporter.ts +255 -0
- package/tools/wip-license-hook/src/core/scanner.ts +367 -0
- package/tools/wip-license-hook/tsconfig.json +16 -0
- package/tools/wip-readme-format/README.md +49 -0
- package/tools/wip-readme-format/SKILL.md +84 -0
- package/tools/wip-readme-format/format.mjs +570 -0
- package/tools/wip-readme-format/package.json +15 -0
- package/tools/wip-release/CHANGELOG.md +42 -0
- package/tools/wip-release/LICENSE +52 -0
- package/tools/wip-release/README.md +45 -0
- package/tools/wip-release/REFERENCE.md +100 -0
- package/tools/wip-release/SKILL.md +139 -0
- package/tools/wip-release/cli.js +161 -0
- package/tools/wip-release/core.mjs +1174 -0
- package/tools/wip-release/mcp-server.mjs +109 -0
- package/tools/wip-release/package.json +36 -0
- package/tools/wip-repo-init/README.md +38 -0
- package/tools/wip-repo-init/SKILL.md +77 -0
- package/tools/wip-repo-init/init.mjs +142 -0
- package/tools/wip-repo-init/package.json +11 -0
- package/tools/wip-repo-permissions-hook/LICENSE +52 -0
- package/tools/wip-repo-permissions-hook/README.md +86 -0
- package/tools/wip-repo-permissions-hook/SKILL.md +73 -0
- package/tools/wip-repo-permissions-hook/cli.js +83 -0
- package/tools/wip-repo-permissions-hook/core.mjs +122 -0
- package/tools/wip-repo-permissions-hook/guard.mjs +64 -0
- package/tools/wip-repo-permissions-hook/mcp-server.mjs +92 -0
- package/tools/wip-repo-permissions-hook/openclaw.plugin.json +8 -0
- package/tools/wip-repo-permissions-hook/package.json +31 -0
- package/tools/wip-repos/LICENSE +52 -0
- package/tools/wip-repos/README.md +77 -0
- package/tools/wip-repos/SKILL.md +80 -0
- package/tools/wip-repos/cli.mjs +176 -0
- package/tools/wip-repos/core.mjs +290 -0
- package/tools/wip-repos/mcp-server.mjs +157 -0
- package/tools/wip-repos/package.json +34 -0
- package/tools/wip-universal-installer/CHANGELOG.md +57 -0
- package/tools/wip-universal-installer/LICENSE +52 -0
- package/tools/wip-universal-installer/README.md +81 -0
- package/tools/wip-universal-installer/REFERENCE.md +122 -0
- package/tools/wip-universal-installer/SKILL.md +87 -0
- package/tools/wip-universal-installer/SPEC.md +180 -0
- package/tools/wip-universal-installer/detect.mjs +130 -0
- package/tools/wip-universal-installer/examples/minimal/README.md +20 -0
- package/tools/wip-universal-installer/examples/minimal/SKILL.md +28 -0
- package/tools/wip-universal-installer/examples/minimal/cli.mjs +4 -0
- package/tools/wip-universal-installer/examples/minimal/core.mjs +8 -0
- package/tools/wip-universal-installer/examples/minimal/mcp-server.mjs +27 -0
- package/tools/wip-universal-installer/examples/minimal/package.json +12 -0
- package/tools/wip-universal-installer/install.js +930 -0
- package/tools/wip-universal-installer/package.json +36 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# deploy-public.sh ... sync a private repo to its public counterpart
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash deploy-public.sh <private-repo-path> <public-github-repo>
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
# bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal
|
|
10
|
+
#
|
|
11
|
+
# Convention:
|
|
12
|
+
# - Private repo: {name}-private (where all work happens)
|
|
13
|
+
# - Public repo: {name} (deployment target, never work here directly)
|
|
14
|
+
# - ai/ folder is excluded from public deploys
|
|
15
|
+
# - Old ai/ in public git history is fine, just not going forward
|
|
16
|
+
#
|
|
17
|
+
# Location: wip-dev-guide-private/scripts/deploy-public.sh (one script for all repos)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
PRIVATE_REPO="$1"
|
|
22
|
+
PUBLIC_REPO="$2"
|
|
23
|
+
|
|
24
|
+
if [[ -z "$PRIVATE_REPO" || -z "$PUBLIC_REPO" ]]; then
|
|
25
|
+
echo "Usage: bash deploy-public.sh <private-repo-path> <public-github-repo>"
|
|
26
|
+
echo "Example: bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [[ ! -d "$PRIVATE_REPO/.git" ]]; then
|
|
31
|
+
echo "Error: $PRIVATE_REPO is not a git repository"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Get the latest commit message from private repo
|
|
36
|
+
COMMIT_MSG=$(cd "$PRIVATE_REPO" && git log -1 --pretty=format:"%s")
|
|
37
|
+
COMMIT_HASH=$(cd "$PRIVATE_REPO" && git log -1 --pretty=format:"%h")
|
|
38
|
+
|
|
39
|
+
TMPDIR=$(mktemp -d)
|
|
40
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
41
|
+
|
|
42
|
+
echo "Cloning public repo $PUBLIC_REPO..."
|
|
43
|
+
gh repo clone "$PUBLIC_REPO" "$TMPDIR/public" -- --depth 1 2>/dev/null || {
|
|
44
|
+
echo "Public repo is empty or doesn't exist. Initializing..."
|
|
45
|
+
mkdir -p "$TMPDIR/public"
|
|
46
|
+
cd "$TMPDIR/public"
|
|
47
|
+
git init
|
|
48
|
+
git remote add origin "git@github.com:${PUBLIC_REPO}.git"
|
|
49
|
+
cd - > /dev/null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
echo "Syncing files from private repo (excluding ai/, .git/)..."
|
|
53
|
+
|
|
54
|
+
# Remove all tracked files in public (except .git) so deleted files get removed
|
|
55
|
+
find "$TMPDIR/public" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
|
|
56
|
+
|
|
57
|
+
# rsync from private, excluding ai/ and .git/
|
|
58
|
+
rsync -a \
|
|
59
|
+
--exclude='ai/' \
|
|
60
|
+
--exclude='.git/' \
|
|
61
|
+
--exclude='.DS_Store' \
|
|
62
|
+
--exclude='.wrangler/' \
|
|
63
|
+
--exclude='.claude/' \
|
|
64
|
+
--exclude='CLAUDE.md' \
|
|
65
|
+
"$PRIVATE_REPO/" "$TMPDIR/public/"
|
|
66
|
+
|
|
67
|
+
cd "$TMPDIR/public"
|
|
68
|
+
|
|
69
|
+
# Check if there are changes
|
|
70
|
+
if git diff --quiet HEAD -- 2>/dev/null && git diff --cached --quiet HEAD -- 2>/dev/null && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then
|
|
71
|
+
echo "No changes to deploy."
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Harness ID for branch prefix. Set HARNESS_ID env var, or auto-detect from private repo path.
|
|
76
|
+
if [[ -z "${HARNESS_ID:-}" ]]; then
|
|
77
|
+
case "$PRIVATE_REPO" in
|
|
78
|
+
*"Claude Code - Mini"*) HARNESS_ID="cc-mini" ;;
|
|
79
|
+
*"Claude Code - MBA"*) HARNESS_ID="cc-air" ;;
|
|
80
|
+
*"Lēsa"*) HARNESS_ID="oc-lesa-mini" ;;
|
|
81
|
+
*) HARNESS_ID="deploy" ;;
|
|
82
|
+
esac
|
|
83
|
+
fi
|
|
84
|
+
BRANCH="$HARNESS_ID/deploy-$(date +%Y%m%d-%H%M%S)"
|
|
85
|
+
|
|
86
|
+
git checkout -b "$BRANCH"
|
|
87
|
+
git add -A
|
|
88
|
+
git commit -m "$COMMIT_MSG (from $COMMIT_HASH)"
|
|
89
|
+
|
|
90
|
+
echo "Pushing branch $BRANCH to $PUBLIC_REPO..."
|
|
91
|
+
git push -u origin "$BRANCH"
|
|
92
|
+
|
|
93
|
+
echo "Creating PR..."
|
|
94
|
+
PR_URL=$(gh pr create -R "$PUBLIC_REPO" \
|
|
95
|
+
--head "$BRANCH" \
|
|
96
|
+
--title "$COMMIT_MSG" \
|
|
97
|
+
--body "Synced from private repo (commit $COMMIT_HASH).")
|
|
98
|
+
|
|
99
|
+
echo "Merging PR..."
|
|
100
|
+
PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$')
|
|
101
|
+
gh pr merge "$PR_NUMBER" -R "$PUBLIC_REPO" --merge --delete-branch
|
|
102
|
+
|
|
103
|
+
echo "Code synced via PR: $PR_URL"
|
|
104
|
+
|
|
105
|
+
# ── Sync release to public repo ──
|
|
106
|
+
# If the private repo has a version tag, create a matching release on the public repo.
|
|
107
|
+
# Pulls full release notes from the private repo and rewrites any private repo references.
|
|
108
|
+
|
|
109
|
+
# Try package.json first, fall back to latest git tag
|
|
110
|
+
VERSION=$(cd "$PRIVATE_REPO" && node -p "require('./package.json').version" 2>/dev/null || echo "")
|
|
111
|
+
if [[ -z "$VERSION" ]]; then
|
|
112
|
+
# No package.json. Use the latest version tag (v*) in the repo
|
|
113
|
+
TAG=$(cd "$PRIVATE_REPO" && git tag -l 'v*' --sort=-version:refname 2>/dev/null | head -1 || echo "")
|
|
114
|
+
if [[ -n "$TAG" ]]; then
|
|
115
|
+
VERSION="${TAG#v}"
|
|
116
|
+
fi
|
|
117
|
+
fi
|
|
118
|
+
if [[ -n "$VERSION" ]]; then
|
|
119
|
+
TAG="v$VERSION"
|
|
120
|
+
EXISTING=$(gh release view "$TAG" -R "$PUBLIC_REPO" --json tagName 2>/dev/null || echo "")
|
|
121
|
+
if [[ -z "$EXISTING" ]]; then
|
|
122
|
+
# Get the private repo's GitHub path (e.g., wipcomputer/memory-crystal-private)
|
|
123
|
+
PRIVATE_GH=$(cd "$PRIVATE_REPO" && git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')
|
|
124
|
+
|
|
125
|
+
# Pull full release notes from private repo
|
|
126
|
+
NOTES=$(gh release view "$TAG" -R "$PRIVATE_GH" --json body -q '.body' 2>/dev/null || echo "")
|
|
127
|
+
|
|
128
|
+
if [[ -z "$NOTES" || "$NOTES" == "null" ]]; then
|
|
129
|
+
NOTES="Release $TAG"
|
|
130
|
+
else
|
|
131
|
+
# Rewrite private repo references to public repo
|
|
132
|
+
NOTES=$(echo "$NOTES" | sed "s|$PRIVATE_GH|$PUBLIC_REPO|g")
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
echo "Creating release $TAG on $PUBLIC_REPO..."
|
|
136
|
+
gh release create "$TAG" -R "$PUBLIC_REPO" --title "$TAG" --notes "$NOTES" 2>/dev/null && echo " ✓ Release $TAG created on $PUBLIC_REPO" || echo " ✗ Release creation failed (non-fatal)"
|
|
137
|
+
else
|
|
138
|
+
# Update existing release notes (in case they were incomplete)
|
|
139
|
+
PRIVATE_GH=$(cd "$PRIVATE_REPO" && git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')
|
|
140
|
+
NOTES=$(gh release view "$TAG" -R "$PRIVATE_GH" --json body -q '.body' 2>/dev/null || echo "")
|
|
141
|
+
if [[ -n "$NOTES" && "$NOTES" != "null" ]]; then
|
|
142
|
+
NOTES=$(echo "$NOTES" | sed "s|$PRIVATE_GH|$PUBLIC_REPO|g")
|
|
143
|
+
gh release edit "$TAG" -R "$PUBLIC_REPO" --notes "$NOTES" 2>/dev/null && echo " ✓ Release $TAG notes updated on $PUBLIC_REPO" || true
|
|
144
|
+
fi
|
|
145
|
+
echo " Release $TAG exists on $PUBLIC_REPO (notes synced)"
|
|
146
|
+
fi
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
echo "Done. Public repo updated."
|
|
150
|
+
echo " PR: $PR_URL"
|
|
151
|
+
echo " Commit: $COMMIT_MSG (from $COMMIT_HASH)"
|
|
152
|
+
[[ -n "${VERSION:-}" ]] && echo " Release: v$VERSION"
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wipcomputer/wip-ai-devops-toolbox",
|
|
3
|
+
"version": "1.9.20",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The complete AI DevOps toolkit for AI-assisted development teams.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Parker Todd Brooks",
|
|
8
|
+
"bin": {
|
|
9
|
+
"wip-release": "tools/wip-release/cli.js",
|
|
10
|
+
"wip-repos": "tools/wip-repos/cli.mjs",
|
|
11
|
+
"wip-install": "tools/wip-universal-installer/install.js",
|
|
12
|
+
"wip-file-guard": "tools/wip-file-guard/guard.mjs",
|
|
13
|
+
"wip-license-guard": "tools/wip-license-guard/cli.mjs",
|
|
14
|
+
"wip-license-hook": "tools/wip-license-hook/dist/cli/index.js",
|
|
15
|
+
"wip-repo-permissions": "tools/wip-repo-permissions-hook/cli.js",
|
|
16
|
+
"wip-repo-init": "tools/wip-repo-init/init.mjs",
|
|
17
|
+
"wip-readme-format": "tools/wip-readme-format/format.mjs",
|
|
18
|
+
"wip-branch-guard": "tools/wip-branch-guard/guard.mjs",
|
|
19
|
+
"deploy-public": "tools/deploy-public/deploy-public.sh",
|
|
20
|
+
"post-merge-rename": "tools/post-merge-rename/post-merge-rename.sh"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/wipcomputer/wip-ai-devops-toolbox.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/wipcomputer/wip-ai-devops-toolbox"
|
|
27
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deploy-public
|
|
3
|
+
version: 1.3.0
|
|
4
|
+
description: Private-to-public repo sync. Copies everything except ai/ to the public mirror. Creates PR, merges, syncs releases.
|
|
5
|
+
author: Parker Todd Brooks
|
|
6
|
+
interface: [cli]
|
|
7
|
+
metadata:
|
|
8
|
+
category: dev-tools
|
|
9
|
+
capabilities:
|
|
10
|
+
- repo-sync
|
|
11
|
+
- release-sync
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# deploy-public
|
|
15
|
+
|
|
16
|
+
Private-to-public repo sync. One script for all repos. Copies code, creates a PR on the public repo, merges it, and syncs GitHub releases.
|
|
17
|
+
|
|
18
|
+
## When to Use This Skill
|
|
19
|
+
|
|
20
|
+
**Use deploy-public for:**
|
|
21
|
+
- Publishing a private repo's code to its public counterpart
|
|
22
|
+
- After running `wip-release` on the private repo (release must exist first)
|
|
23
|
+
- Syncing release notes from private to public
|
|
24
|
+
|
|
25
|
+
**CRITICAL: Release order matters.**
|
|
26
|
+
1. Merge PR to private repo's main
|
|
27
|
+
2. Run `wip-release` (creates GitHub release with notes on private repo)
|
|
28
|
+
3. THEN run `deploy-public.sh` (pulls notes from private release)
|
|
29
|
+
|
|
30
|
+
If you skip step 2, the public release gets empty notes.
|
|
31
|
+
|
|
32
|
+
### Do NOT Use For
|
|
33
|
+
|
|
34
|
+
- Repos without a `-private` counterpart
|
|
35
|
+
- First-time repo setup (create the public repo on GitHub first)
|
|
36
|
+
|
|
37
|
+
## API Reference
|
|
38
|
+
|
|
39
|
+
### CLI
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bash scripts/deploy-public.sh /path/to/private-repo org/public-repo
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Examples
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Deploy memory-crystal
|
|
49
|
+
bash scripts/deploy-public.sh /path/to/memory-crystal-private wipcomputer/memory-crystal
|
|
50
|
+
|
|
51
|
+
# Deploy wip-dev-tools
|
|
52
|
+
bash scripts/deploy-public.sh /path/to/wip-ai-devops-toolbox-private wipcomputer/wip-ai-devops-toolbox
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What It Does
|
|
56
|
+
|
|
57
|
+
1. Clones the public repo to a temp directory
|
|
58
|
+
2. Copies all files from private repo (excluding `ai/`, `.git/`)
|
|
59
|
+
3. Creates a branch, commits, pushes, creates PR
|
|
60
|
+
4. Merges the PR (regular merge, never squash)
|
|
61
|
+
5. Syncs GitHub releases (pulls notes from private repo's releases)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: post-merge-rename
|
|
3
|
+
version: 1.3.0
|
|
4
|
+
description: Post-merge branch renaming. Appends --merged-YYYY-MM-DD to preserve history.
|
|
5
|
+
author: Parker Todd Brooks
|
|
6
|
+
interface: [cli]
|
|
7
|
+
metadata:
|
|
8
|
+
category: dev-tools
|
|
9
|
+
capabilities:
|
|
10
|
+
- branch-rename
|
|
11
|
+
- history-preservation
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# post-merge-rename
|
|
15
|
+
|
|
16
|
+
Scans for merged branches that haven't been renamed and appends `--merged-YYYY-MM-DD` to preserve history. We never delete branches. We rename them.
|
|
17
|
+
|
|
18
|
+
## When to Use This Skill
|
|
19
|
+
|
|
20
|
+
**Use post-merge-rename for:**
|
|
21
|
+
- After merging PRs, to rename the source branch
|
|
22
|
+
- Cleaning up branches that were merged but not renamed
|
|
23
|
+
- Runs automatically as step 10 of `wip-release`
|
|
24
|
+
|
|
25
|
+
### Do NOT Use For
|
|
26
|
+
|
|
27
|
+
- Unmerged branches
|
|
28
|
+
- Branches you're currently working on
|
|
29
|
+
|
|
30
|
+
## API Reference
|
|
31
|
+
|
|
32
|
+
### CLI
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bash scripts/post-merge-rename.sh # scan + rename all merged branches
|
|
36
|
+
bash scripts/post-merge-rename.sh --dry-run # preview only
|
|
37
|
+
bash scripts/post-merge-rename.sh <branch> # rename specific branch
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What It Does
|
|
41
|
+
|
|
42
|
+
1. Lists all local branches merged into main
|
|
43
|
+
2. Skips branches already renamed (containing `--merged-`)
|
|
44
|
+
3. Finds the merge date from git history
|
|
45
|
+
4. Renames: `feature-branch` -> `feature-branch--merged-2026-03-09`
|
|
46
|
+
5. Pushes the renamed branch to origin
|
|
47
|
+
6. Deletes the old branch name from origin
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# deploy-public.sh ... sync a private repo to its public counterpart
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# bash deploy-public.sh <private-repo-path> <public-github-repo>
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
# bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal
|
|
10
|
+
#
|
|
11
|
+
# Convention:
|
|
12
|
+
# - Private repo: {name}-private (where all work happens)
|
|
13
|
+
# - Public repo: {name} (deployment target, never work here directly)
|
|
14
|
+
# - ai/ folder is excluded from public deploys
|
|
15
|
+
# - Old ai/ in public git history is fine, just not going forward
|
|
16
|
+
#
|
|
17
|
+
# Location: wip-dev-guide-private/scripts/deploy-public.sh (one script for all repos)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
PRIVATE_REPO="$1"
|
|
22
|
+
PUBLIC_REPO="$2"
|
|
23
|
+
|
|
24
|
+
if [[ -z "$PRIVATE_REPO" || -z "$PUBLIC_REPO" ]]; then
|
|
25
|
+
echo "Usage: bash deploy-public.sh <private-repo-path> <public-github-repo>"
|
|
26
|
+
echo "Example: bash deploy-public.sh /path/to/memory-crystal wipcomputer/memory-crystal"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [[ ! -d "$PRIVATE_REPO/.git" ]]; then
|
|
31
|
+
echo "Error: $PRIVATE_REPO is not a git repository"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# ── Safety: never deploy back to the source repo ──
|
|
36
|
+
# Extract the private repo's GitHub org/name from its remote URL
|
|
37
|
+
PRIVATE_REMOTE=$(cd "$PRIVATE_REPO" && git remote get-url origin 2>/dev/null | sed 's/.*github.com[:/]\(.*\)\.git/\1/' || echo "")
|
|
38
|
+
|
|
39
|
+
if [[ -n "$PRIVATE_REMOTE" && "$PRIVATE_REMOTE" == "$PUBLIC_REPO" ]]; then
|
|
40
|
+
echo "ERROR: PUBLIC_REPO ($PUBLIC_REPO) is the same as the private repo's origin ($PRIVATE_REMOTE)."
|
|
41
|
+
echo "This would deploy sanitized code (no ai/) back to the source repo and destroy files."
|
|
42
|
+
echo "Did you mean to target the public counterpart instead?"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [[ "$PUBLIC_REPO" == *"-private"* ]]; then
|
|
47
|
+
echo "ERROR: PUBLIC_REPO ($PUBLIC_REPO) contains '-private'."
|
|
48
|
+
echo "deploy-public.sh should only target public repos. Private repos have ai/ folders that would be destroyed."
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Get the latest commit message from private repo
|
|
53
|
+
COMMIT_MSG=$(cd "$PRIVATE_REPO" && git log -1 --pretty=format:"%s")
|
|
54
|
+
COMMIT_HASH=$(cd "$PRIVATE_REPO" && git log -1 --pretty=format:"%h")
|
|
55
|
+
|
|
56
|
+
TMPDIR=$(mktemp -d)
|
|
57
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
58
|
+
|
|
59
|
+
# ── Auto-create public repo if it doesn't exist ──
|
|
60
|
+
REPO_EXISTS=$(gh repo view "$PUBLIC_REPO" --json name -q '.name' 2>/dev/null || echo "")
|
|
61
|
+
if [[ -z "$REPO_EXISTS" ]]; then
|
|
62
|
+
echo "Public repo $PUBLIC_REPO does not exist. Creating..."
|
|
63
|
+
DESCRIPTION=$(cd "$PRIVATE_REPO" && node -p "require('./package.json').description" 2>/dev/null || echo "")
|
|
64
|
+
gh repo create "$PUBLIC_REPO" --public --description "${DESCRIPTION:-Synced from private repo}" 2>/dev/null
|
|
65
|
+
echo " + Created $PUBLIC_REPO"
|
|
66
|
+
EMPTY_REPO=true
|
|
67
|
+
else
|
|
68
|
+
EMPTY_REPO=false
|
|
69
|
+
# Verify the resolved repo is actually the one we asked for (catch GitHub redirects)
|
|
70
|
+
RESOLVED_NAME=$(gh repo view "$PUBLIC_REPO" --json nameWithOwner -q '.nameWithOwner' 2>/dev/null || echo "")
|
|
71
|
+
if [[ -n "$RESOLVED_NAME" && "$RESOLVED_NAME" != "$PUBLIC_REPO" ]]; then
|
|
72
|
+
echo "ERROR: GitHub redirected $PUBLIC_REPO to $RESOLVED_NAME."
|
|
73
|
+
echo "The public repo doesn't actually exist. A repo with a similar name is redirecting."
|
|
74
|
+
echo "Create the public repo first: gh repo create $PUBLIC_REPO --public"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo "Cloning public repo $PUBLIC_REPO..."
|
|
80
|
+
gh repo clone "$PUBLIC_REPO" "$TMPDIR/public" -- --depth 1 2>/dev/null || {
|
|
81
|
+
echo "Public repo is empty. Initializing..."
|
|
82
|
+
mkdir -p "$TMPDIR/public"
|
|
83
|
+
cd "$TMPDIR/public"
|
|
84
|
+
git init
|
|
85
|
+
git remote add origin "git@github.com:${PUBLIC_REPO}.git"
|
|
86
|
+
cd - > /dev/null
|
|
87
|
+
EMPTY_REPO=true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
echo "Syncing files from private repo (excluding ai/, .git/)..."
|
|
91
|
+
|
|
92
|
+
# Remove all tracked files in public (except .git) so deleted files get removed
|
|
93
|
+
find "$TMPDIR/public" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
|
|
94
|
+
|
|
95
|
+
# rsync from private, excluding ai/ and .git/
|
|
96
|
+
rsync -a \
|
|
97
|
+
--exclude='ai/' \
|
|
98
|
+
--exclude='_trash/' \
|
|
99
|
+
--exclude='.git/' \
|
|
100
|
+
--exclude='.DS_Store' \
|
|
101
|
+
--exclude='.wrangler/' \
|
|
102
|
+
--exclude='.claude/' \
|
|
103
|
+
--exclude='CLAUDE.md' \
|
|
104
|
+
"$PRIVATE_REPO/" "$TMPDIR/public/"
|
|
105
|
+
|
|
106
|
+
cd "$TMPDIR/public"
|
|
107
|
+
|
|
108
|
+
# Check if there are changes
|
|
109
|
+
if git diff --quiet HEAD -- 2>/dev/null && git diff --cached --quiet HEAD -- 2>/dev/null && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then
|
|
110
|
+
echo "No changes to deploy."
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Harness ID for branch prefix. Set HARNESS_ID env var, or auto-detect from private repo path.
|
|
115
|
+
if [[ -z "${HARNESS_ID:-}" ]]; then
|
|
116
|
+
case "$PRIVATE_REPO" in
|
|
117
|
+
*"Claude Code - Mini"*) HARNESS_ID="cc-mini" ;;
|
|
118
|
+
*"Claude Code - MBA"*) HARNESS_ID="cc-air" ;;
|
|
119
|
+
*"Lēsa"*) HARNESS_ID="oc-lesa-mini" ;;
|
|
120
|
+
*) HARNESS_ID="deploy" ;;
|
|
121
|
+
esac
|
|
122
|
+
fi
|
|
123
|
+
BRANCH="$HARNESS_ID/deploy-$(date +%Y%m%d-%H%M%S)"
|
|
124
|
+
|
|
125
|
+
git add -A
|
|
126
|
+
git commit -m "$COMMIT_MSG (from $COMMIT_HASH)"
|
|
127
|
+
|
|
128
|
+
if [[ "$EMPTY_REPO" == "true" ]]; then
|
|
129
|
+
# Empty repo: push directly to main (no base branch to PR against)
|
|
130
|
+
echo "Pushing initial commit to main on $PUBLIC_REPO..."
|
|
131
|
+
git branch -M main
|
|
132
|
+
git push -u origin main
|
|
133
|
+
gh repo edit "$PUBLIC_REPO" --default-branch main 2>/dev/null || true
|
|
134
|
+
PR_URL="(initial push, no PR)"
|
|
135
|
+
echo " + Initial commit pushed to main"
|
|
136
|
+
else
|
|
137
|
+
git checkout -b "$BRANCH"
|
|
138
|
+
|
|
139
|
+
echo "Pushing branch $BRANCH to $PUBLIC_REPO..."
|
|
140
|
+
git push -u origin "$BRANCH"
|
|
141
|
+
|
|
142
|
+
echo "Creating PR..."
|
|
143
|
+
PR_URL=$(gh pr create -R "$PUBLIC_REPO" \
|
|
144
|
+
--head "$BRANCH" \
|
|
145
|
+
--title "$COMMIT_MSG" \
|
|
146
|
+
--body "Synced from private repo (commit $COMMIT_HASH).")
|
|
147
|
+
|
|
148
|
+
echo "Merging PR..."
|
|
149
|
+
PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$')
|
|
150
|
+
gh pr merge "$PR_NUMBER" -R "$PUBLIC_REPO" --merge --delete-branch
|
|
151
|
+
|
|
152
|
+
# Clean up any other non-main branches on public repo
|
|
153
|
+
echo "Checking for stale branches on public repo..."
|
|
154
|
+
STALE_BRANCHES=$(gh api "repos/$PUBLIC_REPO/branches" --paginate --jq '.[].name' 2>/dev/null | grep -v '^main$' || true)
|
|
155
|
+
if [[ -n "$STALE_BRANCHES" ]]; then
|
|
156
|
+
STALE_COUNT=$(echo "$STALE_BRANCHES" | wc -l | tr -d ' ')
|
|
157
|
+
echo " Found $STALE_COUNT stale branch(es). Deleting..."
|
|
158
|
+
echo "$STALE_BRANCHES" | while read -r stale; do
|
|
159
|
+
gh api -X DELETE "repos/$PUBLIC_REPO/git/refs/heads/$stale" 2>/dev/null && echo " ✓ Deleted $stale" || echo " ! Could not delete $stale"
|
|
160
|
+
done
|
|
161
|
+
else
|
|
162
|
+
echo " ✓ No stale branches"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
echo "Code synced via PR: $PR_URL"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# ── Sync release to public repo ──
|
|
169
|
+
# If the private repo has a version tag, create a matching release on the public repo.
|
|
170
|
+
# Pulls full release notes from the private repo and rewrites any private repo references.
|
|
171
|
+
|
|
172
|
+
# Try package.json first, fall back to latest git tag
|
|
173
|
+
VERSION=$(cd "$PRIVATE_REPO" && node -p "require('./package.json').version" 2>/dev/null || echo "")
|
|
174
|
+
if [[ -z "$VERSION" ]]; then
|
|
175
|
+
# No package.json. Use the latest version tag (v*) in the repo
|
|
176
|
+
TAG=$(cd "$PRIVATE_REPO" && git tag -l 'v*' --sort=-version:refname 2>/dev/null | head -1 || echo "")
|
|
177
|
+
if [[ -n "$TAG" ]]; then
|
|
178
|
+
VERSION="${TAG#v}"
|
|
179
|
+
fi
|
|
180
|
+
fi
|
|
181
|
+
if [[ -n "$VERSION" ]]; then
|
|
182
|
+
TAG="v$VERSION"
|
|
183
|
+
EXISTING=$(gh release view "$TAG" -R "$PUBLIC_REPO" --json tagName 2>/dev/null || echo "")
|
|
184
|
+
if [[ -z "$EXISTING" ]]; then
|
|
185
|
+
# Get the private repo's GitHub path (e.g., wipcomputer/memory-crystal-private)
|
|
186
|
+
PRIVATE_GH=$(cd "$PRIVATE_REPO" && git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')
|
|
187
|
+
|
|
188
|
+
# Pull full release notes from private repo
|
|
189
|
+
NOTES=$(gh release view "$TAG" -R "$PRIVATE_GH" --json body -q '.body' 2>/dev/null || echo "")
|
|
190
|
+
|
|
191
|
+
if [[ -z "$NOTES" || "$NOTES" == "null" ]]; then
|
|
192
|
+
NOTES="Release $TAG"
|
|
193
|
+
else
|
|
194
|
+
# Rewrite private repo references to public repo
|
|
195
|
+
NOTES=$(echo "$NOTES" | sed "s|$PRIVATE_GH|$PUBLIC_REPO|g")
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
echo "Creating release $TAG on $PUBLIC_REPO..."
|
|
199
|
+
gh release create "$TAG" -R "$PUBLIC_REPO" --title "$TAG" --notes "$NOTES" 2>/dev/null && echo " ✓ Release $TAG created on $PUBLIC_REPO" || echo " ✗ Release creation failed (non-fatal)"
|
|
200
|
+
else
|
|
201
|
+
# Update existing release notes (in case they were incomplete)
|
|
202
|
+
PRIVATE_GH=$(cd "$PRIVATE_REPO" && git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')
|
|
203
|
+
NOTES=$(gh release view "$TAG" -R "$PRIVATE_GH" --json body -q '.body' 2>/dev/null || echo "")
|
|
204
|
+
if [[ -n "$NOTES" && "$NOTES" != "null" ]]; then
|
|
205
|
+
NOTES=$(echo "$NOTES" | sed "s|$PRIVATE_GH|$PUBLIC_REPO|g")
|
|
206
|
+
gh release edit "$TAG" -R "$PUBLIC_REPO" --notes "$NOTES" 2>/dev/null && echo " ✓ Release $TAG notes updated on $PUBLIC_REPO" || true
|
|
207
|
+
fi
|
|
208
|
+
echo " Release $TAG exists on $PUBLIC_REPO (notes synced)"
|
|
209
|
+
fi
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
# ── npm publish from public repo (#100) ──
|
|
213
|
+
# After syncing code and release, publish to npm from the public clone.
|
|
214
|
+
# Only if package.json exists and private !== true.
|
|
215
|
+
|
|
216
|
+
if [[ -n "${VERSION:-}" ]]; then
|
|
217
|
+
# Re-clone public for npm publish (the previous tmpdir might be gone)
|
|
218
|
+
NPM_TMPDIR=$(mktemp -d)
|
|
219
|
+
gh repo clone "$PUBLIC_REPO" "$NPM_TMPDIR/public" -- --depth 1 2>/dev/null
|
|
220
|
+
|
|
221
|
+
if [[ -f "$NPM_TMPDIR/public/package.json" ]]; then
|
|
222
|
+
IS_PRIVATE=$(cd "$NPM_TMPDIR/public" && node -p "require('./package.json').private || false" 2>/dev/null)
|
|
223
|
+
echo "Publishing to npm from public repo..."
|
|
224
|
+
NPM_TOKEN=$(OP_SERVICE_ACCOUNT_TOKEN=$(cat ~/.openclaw/secrets/op-sa-token) \
|
|
225
|
+
op item get "npm Token" --vault "Agent Secrets" --fields label=password --reveal 2>/dev/null || echo "")
|
|
226
|
+
if [[ -n "$NPM_TOKEN" ]]; then
|
|
227
|
+
cd "$NPM_TMPDIR/public"
|
|
228
|
+
|
|
229
|
+
# Publish root package (if not private)
|
|
230
|
+
if [[ "$IS_PRIVATE" != "true" ]]; then
|
|
231
|
+
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
|
|
232
|
+
npm publish --access public 2>/dev/null && echo " ✓ Published root package to npm" || echo " ✗ Root npm publish failed (non-fatal)"
|
|
233
|
+
rm -f .npmrc
|
|
234
|
+
else
|
|
235
|
+
echo " - Root package is private. Skipping root npm publish."
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# For toolbox repos: publish each sub-tool regardless of root private status
|
|
239
|
+
if [[ -d "tools" ]]; then
|
|
240
|
+
for TOOL_DIR in tools/*/; do
|
|
241
|
+
if [[ -f "${TOOL_DIR}package.json" ]]; then
|
|
242
|
+
TOOL_PRIVATE=$(node -p "require('./${TOOL_DIR}package.json').private || false" 2>/dev/null)
|
|
243
|
+
if [[ "$TOOL_PRIVATE" != "true" ]]; then
|
|
244
|
+
TOOL_NAME=$(node -p "require('./${TOOL_DIR}package.json').name" 2>/dev/null)
|
|
245
|
+
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${TOOL_DIR}.npmrc"
|
|
246
|
+
(cd "$TOOL_DIR" && npm publish --access public 2>/dev/null) && echo " ✓ Published $TOOL_NAME to npm" || echo " ✗ npm publish failed for $TOOL_NAME (non-fatal)"
|
|
247
|
+
rm -f "${TOOL_DIR}.npmrc"
|
|
248
|
+
fi
|
|
249
|
+
fi
|
|
250
|
+
done
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
cd - > /dev/null
|
|
254
|
+
else
|
|
255
|
+
echo " ! npm Token not found in 1Password. Skipping npm publish."
|
|
256
|
+
fi
|
|
257
|
+
fi
|
|
258
|
+
rm -rf "$NPM_TMPDIR"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
echo "Done. Public repo updated."
|
|
262
|
+
echo " PR: $PR_URL"
|
|
263
|
+
echo " Commit: $COMMIT_MSG (from $COMMIT_HASH)"
|
|
264
|
+
[[ -n "${VERSION:-}" ]] && echo " Release: v$VERSION"
|