@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.
Files changed (146) hide show
  1. package/.license-guard.json +7 -0
  2. package/.publish-skill.json +4 -0
  3. package/CHANGELOG.md +1120 -0
  4. package/CLA.md +19 -0
  5. package/DEV-GUIDE-GENERAL-PUBLIC.md +882 -0
  6. package/LICENSE +52 -0
  7. package/README.md +238 -0
  8. package/SKILL.md +728 -0
  9. package/TECHNICAL.md +282 -0
  10. package/UNIVERSAL-INTERFACE.md +180 -0
  11. package/_trash/RELEASE-NOTES-v1-8-0.md +29 -0
  12. package/_trash/RELEASE-NOTES-v1-8-1.md +7 -0
  13. package/_trash/RELEASE-NOTES-v1-8-2.md +7 -0
  14. package/_trash/RELEASE-NOTES-v1-9-0.md +37 -0
  15. package/_trash/RELEASE-NOTES-v1-9-1.md +38 -0
  16. package/_trash/RELEASE-NOTES-v1-9-10.md +40 -0
  17. package/_trash/RELEASE-NOTES-v1-9-2.md +40 -0
  18. package/_trash/RELEASE-NOTES-v1-9-6.md +72 -0
  19. package/_trash/RELEASE-NOTES-v1-9-7.md +23 -0
  20. package/_trash/RELEASE-NOTES-v1-9-9.md +75 -0
  21. package/_trash/guide 2/DEV-GUIDE.md +487 -0
  22. package/_trash/guide 2/scripts/deploy-public.sh +152 -0
  23. package/package.json +27 -0
  24. package/scripts/SKILL-deploy-public.md +61 -0
  25. package/scripts/SKILL-post-merge-rename.md +47 -0
  26. package/scripts/deploy-public.sh +264 -0
  27. package/scripts/post-merge-rename.sh +205 -0
  28. package/scripts/publish-skill.sh +134 -0
  29. package/tools/deploy-public/LICENSE +52 -0
  30. package/tools/deploy-public/README.md +31 -0
  31. package/tools/deploy-public/SKILL.md +71 -0
  32. package/tools/deploy-public/deploy-public.sh +264 -0
  33. package/tools/deploy-public/package.json +9 -0
  34. package/tools/ldm-jobs/LICENSE +52 -0
  35. package/tools/ldm-jobs/README.md +46 -0
  36. package/tools/ldm-jobs/backup.sh +16 -0
  37. package/tools/ldm-jobs/branch-protect.sh +39 -0
  38. package/tools/ldm-jobs/crystal-capture.sh +19 -0
  39. package/tools/ldm-jobs/setup-shell.sh +27 -0
  40. package/tools/ldm-jobs/visibility-audit.sh +27 -0
  41. package/tools/post-merge-rename/LICENSE +52 -0
  42. package/tools/post-merge-rename/README.md +29 -0
  43. package/tools/post-merge-rename/SKILL.md +57 -0
  44. package/tools/post-merge-rename/package.json +9 -0
  45. package/tools/post-merge-rename/post-merge-rename.sh +122 -0
  46. package/tools/wip-branch-guard/INSTALL.md +41 -0
  47. package/tools/wip-branch-guard/guard.mjs +259 -0
  48. package/tools/wip-branch-guard/package.json +11 -0
  49. package/tools/wip-file-guard/CHANGELOG.md +6 -0
  50. package/tools/wip-file-guard/LICENSE +52 -0
  51. package/tools/wip-file-guard/README.md +113 -0
  52. package/tools/wip-file-guard/REFERENCE.md +86 -0
  53. package/tools/wip-file-guard/SKILL.md +105 -0
  54. package/tools/wip-file-guard/guard.mjs +128 -0
  55. package/tools/wip-file-guard/openclaw.plugin.json +8 -0
  56. package/tools/wip-file-guard/package.json +27 -0
  57. package/tools/wip-file-guard/test.sh +119 -0
  58. package/tools/wip-license-guard/LICENSE +52 -0
  59. package/tools/wip-license-guard/README.md +32 -0
  60. package/tools/wip-license-guard/SKILL.md +65 -0
  61. package/tools/wip-license-guard/cli.mjs +464 -0
  62. package/tools/wip-license-guard/core.mjs +310 -0
  63. package/tools/wip-license-guard/hook.mjs +146 -0
  64. package/tools/wip-license-guard/package.json +15 -0
  65. package/tools/wip-license-hook/CHANGELOG.md +17 -0
  66. package/tools/wip-license-hook/LICENSE +52 -0
  67. package/tools/wip-license-hook/README.md +200 -0
  68. package/tools/wip-license-hook/SKILL.md +111 -0
  69. package/tools/wip-license-hook/dist/cli/index.d.ts +15 -0
  70. package/tools/wip-license-hook/dist/cli/index.js +170 -0
  71. package/tools/wip-license-hook/dist/cli/index.js.map +1 -0
  72. package/tools/wip-license-hook/dist/core/detector.d.ts +12 -0
  73. package/tools/wip-license-hook/dist/core/detector.js +104 -0
  74. package/tools/wip-license-hook/dist/core/detector.js.map +1 -0
  75. package/tools/wip-license-hook/dist/core/index.d.ts +4 -0
  76. package/tools/wip-license-hook/dist/core/index.js +5 -0
  77. package/tools/wip-license-hook/dist/core/index.js.map +1 -0
  78. package/tools/wip-license-hook/dist/core/ledger.d.ts +49 -0
  79. package/tools/wip-license-hook/dist/core/ledger.js +72 -0
  80. package/tools/wip-license-hook/dist/core/ledger.js.map +1 -0
  81. package/tools/wip-license-hook/dist/core/reporter.d.ts +14 -0
  82. package/tools/wip-license-hook/dist/core/reporter.js +227 -0
  83. package/tools/wip-license-hook/dist/core/reporter.js.map +1 -0
  84. package/tools/wip-license-hook/dist/core/scanner.d.ts +39 -0
  85. package/tools/wip-license-hook/dist/core/scanner.js +325 -0
  86. package/tools/wip-license-hook/dist/core/scanner.js.map +1 -0
  87. package/tools/wip-license-hook/hooks/pre-pull.sh +55 -0
  88. package/tools/wip-license-hook/hooks/pre-push.sh +51 -0
  89. package/tools/wip-license-hook/mcp-server.mjs +119 -0
  90. package/tools/wip-license-hook/package-lock.json +54 -0
  91. package/tools/wip-license-hook/package.json +43 -0
  92. package/tools/wip-license-hook/src/cli/index.ts +189 -0
  93. package/tools/wip-license-hook/src/core/detector.ts +130 -0
  94. package/tools/wip-license-hook/src/core/index.ts +4 -0
  95. package/tools/wip-license-hook/src/core/ledger.ts +116 -0
  96. package/tools/wip-license-hook/src/core/reporter.ts +255 -0
  97. package/tools/wip-license-hook/src/core/scanner.ts +367 -0
  98. package/tools/wip-license-hook/tsconfig.json +16 -0
  99. package/tools/wip-readme-format/README.md +49 -0
  100. package/tools/wip-readme-format/SKILL.md +84 -0
  101. package/tools/wip-readme-format/format.mjs +570 -0
  102. package/tools/wip-readme-format/package.json +15 -0
  103. package/tools/wip-release/CHANGELOG.md +42 -0
  104. package/tools/wip-release/LICENSE +52 -0
  105. package/tools/wip-release/README.md +45 -0
  106. package/tools/wip-release/REFERENCE.md +100 -0
  107. package/tools/wip-release/SKILL.md +139 -0
  108. package/tools/wip-release/cli.js +161 -0
  109. package/tools/wip-release/core.mjs +1174 -0
  110. package/tools/wip-release/mcp-server.mjs +109 -0
  111. package/tools/wip-release/package.json +36 -0
  112. package/tools/wip-repo-init/README.md +38 -0
  113. package/tools/wip-repo-init/SKILL.md +77 -0
  114. package/tools/wip-repo-init/init.mjs +142 -0
  115. package/tools/wip-repo-init/package.json +11 -0
  116. package/tools/wip-repo-permissions-hook/LICENSE +52 -0
  117. package/tools/wip-repo-permissions-hook/README.md +86 -0
  118. package/tools/wip-repo-permissions-hook/SKILL.md +73 -0
  119. package/tools/wip-repo-permissions-hook/cli.js +83 -0
  120. package/tools/wip-repo-permissions-hook/core.mjs +122 -0
  121. package/tools/wip-repo-permissions-hook/guard.mjs +64 -0
  122. package/tools/wip-repo-permissions-hook/mcp-server.mjs +92 -0
  123. package/tools/wip-repo-permissions-hook/openclaw.plugin.json +8 -0
  124. package/tools/wip-repo-permissions-hook/package.json +31 -0
  125. package/tools/wip-repos/LICENSE +52 -0
  126. package/tools/wip-repos/README.md +77 -0
  127. package/tools/wip-repos/SKILL.md +80 -0
  128. package/tools/wip-repos/cli.mjs +176 -0
  129. package/tools/wip-repos/core.mjs +290 -0
  130. package/tools/wip-repos/mcp-server.mjs +157 -0
  131. package/tools/wip-repos/package.json +34 -0
  132. package/tools/wip-universal-installer/CHANGELOG.md +57 -0
  133. package/tools/wip-universal-installer/LICENSE +52 -0
  134. package/tools/wip-universal-installer/README.md +81 -0
  135. package/tools/wip-universal-installer/REFERENCE.md +122 -0
  136. package/tools/wip-universal-installer/SKILL.md +87 -0
  137. package/tools/wip-universal-installer/SPEC.md +180 -0
  138. package/tools/wip-universal-installer/detect.mjs +130 -0
  139. package/tools/wip-universal-installer/examples/minimal/README.md +20 -0
  140. package/tools/wip-universal-installer/examples/minimal/SKILL.md +28 -0
  141. package/tools/wip-universal-installer/examples/minimal/cli.mjs +4 -0
  142. package/tools/wip-universal-installer/examples/minimal/core.mjs +8 -0
  143. package/tools/wip-universal-installer/examples/minimal/mcp-server.mjs +27 -0
  144. package/tools/wip-universal-installer/examples/minimal/package.json +12 -0
  145. package/tools/wip-universal-installer/install.js +930 -0
  146. package/tools/wip-universal-installer/package.json +36 -0
@@ -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"
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@wipcomputer/deploy-public",
3
+ "version": "1.9.20",
4
+ "description": "Private-to-public repo sync. Excludes ai/ folder, creates PR, merges, cleans up branches.",
5
+ "bin": {
6
+ "deploy-public": "./deploy-public.sh"
7
+ },
8
+ "license": "MIT"
9
+ }
@@ -0,0 +1,52 @@
1
+ Dual License: MIT + AGPLv3
2
+
3
+ Copyright (c) 2026 WIP Computer, Inc.
4
+
5
+
6
+ 1. MIT License (local and personal use)
7
+ ---------------------------------------
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+
28
+ 2. GNU Affero General Public License v3.0 (commercial and cloud use)
29
+ --------------------------------------------------------------------
30
+
31
+ If you run this software as part of a hosted service, cloud platform,
32
+ marketplace listing, or any network-accessible offering for commercial
33
+ purposes, the AGPLv3 terms apply. You must either:
34
+
35
+ a) Release your complete source code under AGPLv3, or
36
+ b) Obtain a commercial license.
37
+
38
+ This program is free software: you can redistribute it and/or modify
39
+ it under the terms of the GNU Affero General Public License as published
40
+ by the Free Software Foundation, either version 3 of the License, or
41
+ (at your option) any later version.
42
+
43
+ This program is distributed in the hope that it will be useful,
44
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
45
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46
+ GNU Affero General Public License for more details.
47
+
48
+ You should have received a copy of the GNU Affero General Public License
49
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
50
+
51
+
52
+ AGPLv3 for personal use is free. Commercial licenses available.
@@ -0,0 +1,46 @@
1
+ # LDM Dev Tools Jobs
2
+
3
+ Shell scripts that power the LDM Dev Tools.app scheduled automation. These are the same scripts bundled inside the `.app` at `Contents/Resources/jobs/`. Committed here so the logic is fully readable and auditable without installing the app.
4
+
5
+ ## Jobs
6
+
7
+ ### backup.sh
8
+
9
+ Daily backup. Delegates to Lesa's backup script.
10
+
11
+ ### branch-protect.sh
12
+
13
+ Audits all repos in the `wipcomputer` GitHub org. Enforces branch protection on any repo missing it. Reports results.
14
+
15
+ ### visibility-audit.sh
16
+
17
+ Audits all public repos in the `wipcomputer` GitHub org for missing `-private` counterparts. Uses `wip-repo-permissions-hook` CLI.
18
+
19
+ ### setup-shell.sh
20
+
21
+ One-time shell environment setup for new machines. Configures:
22
+ - **tmux mouse scrolling** ... `set -g mouse on` in `~/.tmux.conf`. Trackpad scrolling works inside tmux sessions.
23
+
24
+ Idempotent. Safe to re-run.
25
+
26
+ ## Usage
27
+
28
+ These scripts can be run standalone (no `.app` required):
29
+
30
+ ```bash
31
+ bash tools/ldm-jobs/backup.sh
32
+ bash tools/ldm-jobs/branch-protect.sh
33
+ bash tools/ldm-jobs/visibility-audit.sh
34
+ ```
35
+
36
+ Or via the macOS app wrapper (which provides Full Disk Access for scripts that need it):
37
+
38
+ ```bash
39
+ open -W ~/Applications/LDMDevTools.app --args backup
40
+ open -W ~/Applications/LDMDevTools.app --args branch-protect
41
+ open -W ~/Applications/LDMDevTools.app --args visibility-audit
42
+ ```
43
+
44
+ ## App Source
45
+
46
+ The LDM Dev Tools.app is a minimal native macOS launcher that runs these shell scripts with Full Disk Access permissions. The app binary source is not yet committed. The automation logic lives entirely in these `.sh` files.
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ # Job: daily backup
3
+ # Calls Lesa's existing backup script
4
+
5
+ SCRIPT="$HOME/Documents/wipcomputer--mac-mini-01/staff/Lēsa/scripts/daily-backup.sh"
6
+
7
+ echo "=== Backup started: $(date) ==="
8
+
9
+ if [ -f "$SCRIPT" ]; then
10
+ /bin/bash "$SCRIPT"
11
+ else
12
+ echo "ERROR: backup script not found at $SCRIPT"
13
+ exit 1
14
+ fi
15
+
16
+ echo "=== Backup finished: $(date) ==="
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # Job: audit and enforce branch protection across all org repos
3
+ # Runs daily. Adds protection to any repo missing it.
4
+
5
+ echo "=== Branch protection audit: $(date) ==="
6
+
7
+ ORG="wipcomputer"
8
+ FIXED=0
9
+ SKIPPED=0
10
+ ALREADY=0
11
+
12
+ repos=$(gh repo list "$ORG" --limit 200 --json name,isArchived -q '.[] | select(.isArchived == false) | .name')
13
+
14
+ for repo in $repos; do
15
+ result=$(gh api "repos/$ORG/$repo/branches/main/protection" 2>&1)
16
+
17
+ if echo "$result" | grep -q "enforce_admins"; then
18
+ ALREADY=$((ALREADY + 1))
19
+ elif echo "$result" | grep -q "Branch not protected"; then
20
+ echo "FIXING: $repo"
21
+ gh api "repos/$ORG/$repo/branches/main/protection" -X PUT \
22
+ -F "required_pull_request_reviews[required_approving_review_count]=0" \
23
+ -F "enforce_admins=true" \
24
+ -F "restrictions=null" \
25
+ -F "required_status_checks=null" > /dev/null 2>&1
26
+ if [ $? -eq 0 ]; then
27
+ echo " Protected: $repo"
28
+ FIXED=$((FIXED + 1))
29
+ else
30
+ echo " FAILED: $repo"
31
+ fi
32
+ else
33
+ SKIPPED=$((SKIPPED + 1))
34
+ fi
35
+ done
36
+
37
+ echo ""
38
+ echo "Results: $ALREADY already protected, $FIXED fixed, $SKIPPED skipped (no main branch or error)"
39
+ echo "=== Branch protection audit complete: $(date) ==="
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # Job: crystal-capture
3
+ #
4
+ # SOURCE OF TRUTH: memory-crystal-private/scripts/crystal-capture.sh
5
+ # This copy is for LDM Dev Tools.app manual runs only.
6
+ # The cron job runs from ~/.ldm/bin/crystal-capture.sh (installed via crystal init).
7
+ # Memory Crystal does not depend on this app.
8
+ #
9
+ # If updating: update the source in memory-crystal-private first, then copy here.
10
+
11
+ POLLER="$HOME/.ldm/extensions/memory-crystal/dist/cc-poller.js"
12
+ NODE="/opt/homebrew/bin/node"
13
+
14
+ if [ ! -f "$POLLER" ]; then
15
+ echo "ERROR: cc-poller not found at $POLLER"
16
+ exit 1
17
+ fi
18
+
19
+ $NODE "$POLLER" 2>&1
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # setup-shell.sh ... Configure shell environment for LDM OS development.
3
+ # Run once on a new machine. Idempotent (safe to re-run).
4
+
5
+ set -euo pipefail
6
+
7
+ echo "LDM OS shell setup"
8
+ echo ""
9
+
10
+ # ── tmux: enable mouse scrolling ──
11
+
12
+ TMUX_CONF="$HOME/.tmux.conf"
13
+
14
+ if [ -f "$TMUX_CONF" ] && grep -q "set -g mouse on" "$TMUX_CONF"; then
15
+ echo "[OK] tmux mouse scrolling already configured"
16
+ else
17
+ echo "set -g mouse on" >> "$TMUX_CONF"
18
+ echo "[OK] tmux mouse scrolling enabled ($TMUX_CONF)"
19
+ fi
20
+
21
+ # Reload if tmux is running
22
+ if command -v tmux &>/dev/null && tmux list-sessions &>/dev/null 2>&1; then
23
+ tmux source-file "$TMUX_CONF" 2>/dev/null && echo "[OK] tmux config reloaded" || true
24
+ fi
25
+
26
+ echo ""
27
+ echo "Done. Shell environment configured."
@@ -0,0 +1,27 @@
1
+ #!/bin/bash
2
+ # visibility-audit.sh ... audit all public repos for missing -private counterparts
3
+ # Runs via LDMDevTools.app cron job
4
+ # Same pattern as branch-protect.sh
5
+
6
+ echo "=== Visibility audit: $(date) ==="
7
+
8
+ ORG="wipcomputer"
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ HOOK_DIR="$SCRIPT_DIR/../wip-repo-permissions-hook"
11
+
12
+ if [[ ! -f "$HOOK_DIR/cli.js" ]]; then
13
+ echo "Error: wip-repo-permissions-hook not found at $HOOK_DIR"
14
+ exit 1
15
+ fi
16
+
17
+ node "$HOOK_DIR/cli.js" audit "$ORG"
18
+ EXIT_CODE=$?
19
+
20
+ if [[ $EXIT_CODE -ne 0 ]]; then
21
+ echo ""
22
+ echo "VIOLATIONS FOUND. Some public repos lack -private counterparts."
23
+ echo "Run: node $HOOK_DIR/cli.js audit $ORG"
24
+ fi
25
+
26
+ echo "=== Done ==="
27
+ exit $EXIT_CODE
@@ -0,0 +1,52 @@
1
+ Dual License: MIT + AGPLv3
2
+
3
+ Copyright (c) 2026 WIP Computer, Inc.
4
+
5
+
6
+ 1. MIT License (local and personal use)
7
+ ---------------------------------------
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+
28
+ 2. GNU Affero General Public License v3.0 (commercial and cloud use)
29
+ --------------------------------------------------------------------
30
+
31
+ If you run this software as part of a hosted service, cloud platform,
32
+ marketplace listing, or any network-accessible offering for commercial
33
+ purposes, the AGPLv3 terms apply. You must either:
34
+
35
+ a) Release your complete source code under AGPLv3, or
36
+ b) Obtain a commercial license.
37
+
38
+ This program is free software: you can redistribute it and/or modify
39
+ it under the terms of the GNU Affero General Public License as published
40
+ by the Free Software Foundation, either version 3 of the License, or
41
+ (at your option) any later version.
42
+
43
+ This program is distributed in the hope that it will be useful,
44
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
45
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46
+ GNU Affero General Public License for more details.
47
+
48
+ You should have received a copy of the GNU Affero General Public License
49
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
50
+
51
+
52
+ AGPLv3 for personal use is free. Commercial licenses available.
@@ -0,0 +1,29 @@
1
+ ###### WIP Computer
2
+
3
+ # Post-Merge Branch Naming
4
+
5
+ Cleans up after itself. Merged branches get renamed with dates automatically.
6
+
7
+ ## What it does
8
+
9
+ - Scans for merged branches that haven't been renamed
10
+ - Appends `--merged-YYYY-MM-DD` to preserve history
11
+ - We never delete branches. We rename them.
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ bash post-merge-rename.sh
17
+ ```
18
+
19
+ ## Requirements
20
+
21
+ - git
22
+ - bash
23
+
24
+ ## Interfaces
25
+
26
+ - **CLI**: Shell script
27
+ - **Skill**: SKILL.md for agent instructions
28
+
29
+ ## Part of [AI DevOps Toolbox](https://github.com/wipcomputer/wip-ai-devops-toolbox)
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: post-merge-rename
3
+ description: Post-merge branch renaming. Appends --merged-YYYY-MM-DD to preserve history.
4
+ license: MIT
5
+ interface: [cli, skill]
6
+ metadata:
7
+ display-name: "Post-Merge Branch Naming"
8
+ version: "1.3.0"
9
+ homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
10
+ author: "Parker Todd Brooks"
11
+ category: dev-tools
12
+ capabilities:
13
+ - branch-rename
14
+ - history-preservation
15
+ requires:
16
+ bins: [git, bash]
17
+ openclaw:
18
+ requires:
19
+ bins: [git, bash]
20
+ emoji: "🏷️"
21
+ compatibility: Requires git, bash.
22
+ ---
23
+
24
+ # post-merge-rename
25
+
26
+ 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.
27
+
28
+ ## When to Use This Skill
29
+
30
+ **Use post-merge-rename for:**
31
+ - After merging PRs, to rename the source branch
32
+ - Cleaning up branches that were merged but not renamed
33
+ - Runs automatically as step 10 of `wip-release`
34
+
35
+ ### Do NOT Use For
36
+
37
+ - Unmerged branches
38
+ - Branches you're currently working on
39
+
40
+ ## API Reference
41
+
42
+ ### CLI
43
+
44
+ ```bash
45
+ bash scripts/post-merge-rename.sh # scan + rename all merged branches
46
+ bash scripts/post-merge-rename.sh --dry-run # preview only
47
+ bash scripts/post-merge-rename.sh <branch> # rename specific branch
48
+ ```
49
+
50
+ ## What It Does
51
+
52
+ 1. Lists all local branches merged into main
53
+ 2. Skips branches already renamed (containing `--merged-`)
54
+ 3. Finds the merge date from git history
55
+ 4. Renames: `feature-branch` -> `feature-branch--merged-2026-03-09`
56
+ 5. Pushes the renamed branch to origin
57
+ 6. Deletes the old branch name from origin
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@wipcomputer/post-merge-rename",
3
+ "version": "1.9.20",
4
+ "description": "Post-merge branch renaming. Appends --merged-YYYY-MM-DD to preserve history.",
5
+ "bin": {
6
+ "post-merge-rename": "./post-merge-rename.sh"
7
+ },
8
+ "license": "MIT"
9
+ }