@wipcomputer/wip-ai-devops-toolbox 1.9.51 → 1.9.52

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.
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "wip-ai-devops-toolbox",
3
- "websiteRepo": "/Users/lesa/Documents/wipcomputer--mac-mini-01/staff/Parker/Claude Code - Mini/repos/wip-web/wip-websites-private"
3
+ "websiteRepo": "/Users/lesa/wipcomputerinc/repos/wip-web/wip-websites-private"
4
4
  }
package/CHANGELOG.md CHANGED
@@ -31,6 +31,34 @@
31
31
 
32
32
 
33
33
 
34
+
35
+ ## 1.9.52 (2026-03-27)
36
+
37
+ # Release Notes: wip-ai-devops-toolbox v1.9.52
38
+
39
+ **Fix branch guard false-blocking bash commands targeting worktree paths**
40
+
41
+ ## What changed
42
+
43
+ - Branch guard now extracts absolute paths from any bash command (mkdir, cp, mv, touch, etc.) and resolves the git branch from the target path's repo, not the CWD
44
+ - `findRepoRoot()` improved to walk up to existing directories for paths that don't exist yet (handles mkdir for new directories)
45
+ - Added `.ldm/worktrees` to allowed worktree locations alongside `_worktrees/` and `.claude/worktrees`
46
+
47
+ ## Why
48
+
49
+ When Claude Code launches from `~/wipcomputerinc/` (on main) and runs bash commands targeting files inside a worktree (e.g., `mkdir -p /path/to/_worktrees/repo--branch/new-dir/`), the guard only knew how to extract paths from `cd` and `git -C` patterns. Any other command fell back to CWD resolution, saw "main", and blocked incorrectly. This caused minutes of wasted time every session.
50
+
51
+ ## Issues closed
52
+
53
+ - wipcomputer/wip-ldm-os#187
54
+
55
+ ## How to verify
56
+
57
+ ```bash
58
+ # From CWD on main, this should no longer be blocked:
59
+ # mkdir -p /path/to/_worktrees/repo--branch/new-directory/
60
+ # cp file.txt /path/to/_worktrees/repo--branch/
61
+ ```
34
62
 
35
63
  ## 1.9.51 (2026-03-24)
36
64
 
@@ -1,6 +1,6 @@
1
1
  # Dev Guide ... Best Practices for AI-Assisted Development
2
2
 
3
- **WIP team members: also read [the private Dev Guide](ai/DEV-GUIDE-FOR-WIP-ONLY-PRIVATE.md).** It covers WIP-specific conventions (branch prefixes, agent IDs, deploy paths, incidents) that supplement everything below. Neither guide is complete without the other.
3
+ **WIP team members: also read the private Dev Guide at `~/wipcomputerinc/settings/templates/dev-guide-private.md`.** It covers WIP-specific conventions (branch prefixes, agent IDs, deploy paths, incidents) that supplement everything below. Neither guide is complete without the other.
4
4
 
5
5
  ## Repo Structure Convention
6
6
 
@@ -648,6 +648,47 @@ Product docs drift fast. If roadmap updates only happen "when someone remembers,
648
648
 
649
649
  Release notes are the public face of the project. They must be comprehensive. One-liners like "Release v0.6.0" are unacceptable. Every feature, every change, documented section by section. This applies to both private and public GitHub releases.
650
650
 
651
+ ## Feature Planning: 5 Mandatory Questions
652
+
653
+ Before implementing any feature that changes how the system works (installer, config, deploy, architecture), answer these 5 questions in your plan or PR description. Not optional. Not "consider this." Answer all five or the plan is incomplete.
654
+
655
+ 1. **What source files change?** (in the repo)
656
+ 2. **What does `ldm install` deploy?** (templates, rules, docs, boot config, CLAUDE.md, skills)
657
+ 3. **What needs to update for fresh install vs existing install?**
658
+ 4. **What docs need updating?** (repo docs, settings/docs, website)
659
+ 5. **What are ALL the files the installer touches on deploy?**
660
+
661
+ If you can't answer #5, you don't understand the change well enough to ship it.
662
+
663
+ ## Doc Update Dependencies
664
+
665
+ When you change a file on the left, you MUST also update the files on the right. This is not optional.
666
+
667
+ | If you change | You must also update |
668
+ |--------------|---------------------|
669
+ | `SKILL.md` | `references/`, `settings/docs/how-install-works.md`, wip.computer/install/ (auto via wip-release) |
670
+ | `lib/deploy.mjs` | `settings/docs/how-install-works.md`, `docs/universal-installer/SPEC.md`, `docs/universal-installer/TECHNICAL.md` |
671
+ | `catalog.json` | `settings/docs/what-is-ldm-os.md`, `README.md`, `docs/skills/README.md` |
672
+ | `bin/ldm.js` (init) | `settings/docs/how-install-works.md`, `settings/docs/system-directories.md` |
673
+ | `shared/rules/*` | `settings/docs/how-rules-and-commands-work.md` |
674
+ | `shared/boot/*` | `settings/docs/how-agents-work.md` |
675
+ | `lib/detect.mjs` | `docs/universal-installer/SPEC.md`, `settings/docs/how-install-works.md` |
676
+ | Backup scripts | `settings/docs/how-backup-works.md` |
677
+ | Agent identity files | `settings/docs/how-agents-work.md` |
678
+ | Any repo `README.md` | Public repo README (via deploy-public.sh) |
679
+
680
+ Machine-readable version: `~/wipcomputerinc/settings/docs/change-dependencies.json`
681
+
682
+ ### Three doc layers
683
+
684
+ Every change flows through three layers:
685
+
686
+ 1. **Repo docs** (generic, source of truth): README.md, TECHNICAL.md, SPEC.md per repo. Written for anyone.
687
+ 2. **Settings docs** (personalized, deployed by ldm install): `~/wipcomputerinc/settings/docs/`. Written for YOUR system. Personalized from config.json.
688
+ 3. **Website docs** (public): wip.computer. SKILL.md auto-deployed by wip-release. Other docs via Mintlify or deploy script.
689
+
690
+ If repo docs change but settings docs don't update, the user's local docs drift. If settings docs change but the website doesn't, the public docs drift. All three layers must stay in sync.
691
+
651
692
  ## Repo Directory Structure
652
693
 
653
694
  ### The Standard Layout
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.51"
8
+ version: "1.9.52"
9
9
  homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
10
10
  author: "Parker Todd Brooks"
11
11
  category: dev-tools
@@ -0,0 +1,25 @@
1
+ # Release Notes: wip-ai-devops-toolbox v1.9.52
2
+
3
+ **Fix branch guard false-blocking bash commands targeting worktree paths**
4
+
5
+ ## What changed
6
+
7
+ - Branch guard now extracts absolute paths from any bash command (mkdir, cp, mv, touch, etc.) and resolves the git branch from the target path's repo, not the CWD
8
+ - `findRepoRoot()` improved to walk up to existing directories for paths that don't exist yet (handles mkdir for new directories)
9
+ - Added `.ldm/worktrees` to allowed worktree locations alongside `_worktrees/` and `.claude/worktrees`
10
+
11
+ ## Why
12
+
13
+ When Claude Code launches from `~/wipcomputerinc/` (on main) and runs bash commands targeting files inside a worktree (e.g., `mkdir -p /path/to/_worktrees/repo--branch/new-dir/`), the guard only knew how to extract paths from `cd` and `git -C` patterns. Any other command fell back to CWD resolution, saw "main", and blocked incorrectly. This caused minutes of wasted time every session.
14
+
15
+ ## Issues closed
16
+
17
+ - wipcomputer/wip-ldm-os#187
18
+
19
+ ## How to verify
20
+
21
+ ```bash
22
+ # From CWD on main, this should no longer be blocked:
23
+ # mkdir -p /path/to/_worktrees/repo--branch/new-directory/
24
+ # cp file.txt /path/to/_worktrees/repo--branch/
25
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ai-devops-toolbox",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
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/deploy-public",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "Private-to-public repo sync. Excludes ai/ folder, creates PR, merges, cleans up branches.",
5
5
  "bin": {
6
6
  "deploy-public": "./deploy-public.sh"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/post-merge-rename",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "Post-merge branch renaming. Appends --merged-YYYY-MM-DD to preserve history.",
5
5
  "bin": {
6
6
  "post-merge-rename": "./post-merge-rename.sh"
@@ -135,6 +135,17 @@ function findRepoRoot(filePath) {
135
135
  dir = dirname(dir); // File might not exist yet
136
136
  }
137
137
 
138
+ // Walk up until we find an existing directory (handles mkdir for new paths)
139
+ while (dir && dir !== '/') {
140
+ try {
141
+ const s = statSync(dir);
142
+ if (s.isDirectory()) break;
143
+ dir = dirname(dir);
144
+ } catch {
145
+ dir = dirname(dir);
146
+ }
147
+ }
148
+
138
149
  // Use git rev-parse from the directory
139
150
  const result = execSync('git rev-parse --show-toplevel 2>/dev/null', {
140
151
  cwd: dir,
@@ -146,6 +157,18 @@ function findRepoRoot(filePath) {
146
157
  return null;
147
158
  }
148
159
 
160
+ function extractPathsFromCommand(command) {
161
+ // Extract absolute paths from a bash command
162
+ // Matches paths like /Users/foo/bar, /tmp/something, etc.
163
+ const paths = [];
164
+ const regex = /(\/(?:Users|home|tmp|var|opt|etc|private)[^\s"'|;&>)]+)/g;
165
+ let match;
166
+ while ((match = regex.exec(command)) !== null) {
167
+ paths.push(match[1]);
168
+ }
169
+ return paths;
170
+ }
171
+
149
172
  function getCurrentBranch(cwd) {
150
173
  try {
151
174
  return execSync('git branch --show-current 2>/dev/null', {
@@ -230,7 +253,7 @@ async function main() {
230
253
  const wtMatch = cmd.match(/\bgit\s+worktree\s+add\s+["']?([^\s"']+)/);
231
254
  if (wtMatch) {
232
255
  const wtPath = wtMatch[1];
233
- if (!wtPath.includes('_worktrees') && !wtPath.includes('.claude/worktrees')) {
256
+ if (!wtPath.includes('_worktrees') && !wtPath.includes('.claude/worktrees') && !wtPath.includes('.ldm/worktrees')) {
234
257
  deny(`WARNING: Creating worktree outside _worktrees/. Use: ldm worktree add <branch>
235
258
 
236
259
  The convention is _worktrees/<repo>--<branch>/ so worktrees don't mix with real repos.
@@ -274,6 +297,17 @@ This is a warning, not a block. If you need to create it here, retry.`);
274
297
  if (!repoDir && gitCMatch) {
275
298
  repoDir = findRepoRoot(gitCMatch[1].trim());
276
299
  }
300
+ // Extract absolute paths from the command itself (handles mkdir, cp, mv, etc.)
301
+ if (!repoDir) {
302
+ const paths = extractPathsFromCommand(command);
303
+ for (const p of paths) {
304
+ const resolved = findRepoRoot(p);
305
+ if (resolved) {
306
+ repoDir = resolved;
307
+ break;
308
+ }
309
+ }
310
+ }
277
311
  }
278
312
 
279
313
  // Fall back to CWD
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-branch-guard",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
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
  "main": "guard.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-file-guard",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "type": "module",
5
5
  "description": "Hook that blocks destructive edits to protected identity files. For Claude Code CLI and OpenClaw.",
6
6
  "main": "guard.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-license-guard",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "License compliance for your own repos. Ensures correct copyright, dual-license blocks, and LICENSE files.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-license-hook",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "License rug-pull detection and dependency license compliance for open source projects",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-readme-format",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "Reformat any repo's README to follow the WIP Computer standard. Agent-first, human-readable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -155,8 +155,10 @@ function gitCommitAndTag(repoPath, newVersion, notes) {
155
155
  execFileSync('git', ['add', f], { cwd: repoPath, stdio: 'pipe' });
156
156
  }
157
157
  }
158
- // Use execFileSync to avoid shell injection via notes
159
- execFileSync('git', ['commit', '-m', msg], { cwd: repoPath, stdio: 'pipe' });
158
+ // Use execFileSync to avoid shell injection via notes.
159
+ // --no-verify: wip-release legitimately commits on main (version bump + changelog).
160
+ // The pre-commit hook blocks all commits on main, but wip-release is the one exception.
161
+ execFileSync('git', ['commit', '--no-verify', '-m', msg], { cwd: repoPath, stdio: 'pipe' });
160
162
  execFileSync('git', ['tag', `v${newVersion}`], { cwd: repoPath, stdio: 'pipe' });
161
163
  }
162
164
 
@@ -261,6 +263,24 @@ function checkReleaseNotes(notes, notesSource, level) {
261
263
  issues.push('Notes look like a changelog entry, not a narrative. Explain the impact.');
262
264
  }
263
265
 
266
+ // Narrative quality: must have at least one paragraph (not just bullets/headers)
267
+ // A paragraph is 2+ consecutive lines of prose (not starting with -, *, #, |, or ```)
268
+ const lines = notes.split('\n').filter(l => l.trim().length > 0);
269
+ const proseLines = lines.filter(l => {
270
+ const t = l.trim();
271
+ return !t.startsWith('#') && !t.startsWith('-') && !t.startsWith('*') &&
272
+ !t.startsWith('|') && !t.startsWith('```') && !t.startsWith('>') &&
273
+ t.length > 30;
274
+ });
275
+ if (proseLines.length < 2) {
276
+ issues.push('Release notes need narrative, not just bullets. Write at least one paragraph explaining what changed and why it matters. Tell the story.');
277
+ }
278
+
279
+ // Must be substantial (not just a header + bullets)
280
+ if (notes.length < 200) {
281
+ issues.push('Release notes are too short (under 200 chars). Every release deserves a story: what was broken or missing, what we built, why the user should care.');
282
+ }
283
+
264
284
  // Release notes should reference at least one issue
265
285
  const hasIssueRef = /#\d+/.test(notes);
266
286
  if (!hasIssueRef) {
@@ -299,16 +319,14 @@ export function scaffoldReleaseNotes(repoPath, version) {
299
319
 
300
320
  **One-line summary of what this release does**
301
321
 
302
- ## What changed
303
-
304
- Describe the changes. Not a commit list. Explain:
305
- - What was built or fixed
306
- - Why it matters
307
- - What the user should know
322
+ Tell the story. What was broken or missing? What did we build? Why does the user care?
323
+ Write at least one real paragraph of prose. Not just bullets. The release notes gate
324
+ will block if there is no narrative. Bullets are fine for details, but the story comes first.
308
325
 
309
- ## Why
326
+ ## The story
310
327
 
311
- What problem does this solve? What was broken or missing?
328
+ (Write a paragraph here. What was the problem? What does this release fix? Why does it matter?
329
+ This is what users read. Make it worth reading.)
312
330
 
313
331
  ## Issues closed
314
332
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-repo-init",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "description": "Scaffold the standard ai/ directory structure in any repo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-repo-permissions-hook",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "type": "module",
5
5
  "description": "Repo visibility guard. Blocks repos from going public without a -private counterpart.",
6
6
  "main": "core.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-repos",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
4
4
  "type": "module",
5
5
  "description": "Repo manifest reconciler. Single source of truth for repo organization. Like prettier for folder structure.",
6
6
  "main": "core.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/universal-installer",
3
- "version": "1.9.51",
3
+ "version": "1.9.52",
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",
@@ -1,63 +0,0 @@
1
- # todos/
2
-
3
- One todo file per person or agent. Three sections per file: To Do, Done, Deprecated.
4
-
5
- ## What This Folder Is
6
-
7
- Todos that are specific to this repo. Each person or agent who works on this repo gets one file. The file tracks what they need to do, what they've done, and what got dropped.
8
-
9
- For cross-repo work or bigger items, use GitHub Issues. Todos here are for repo-scoped tasks that don't need the overhead of an issue.
10
-
11
- ## Files
12
-
13
- One file per person/agent. Name it `[Name]-todo.md`.
14
-
15
- Example:
16
-
17
- | File | Who |
18
- |------|-----|
19
- | `Parker-todo.md` | Parker (human tasks: reviews, credentials, approvals) |
20
- | `CC-Mini-todo.md` | Claude Code on Mac Mini (code, builds, deploys) |
21
-
22
- Create the file when that person/agent first has work to do. No empty placeholder files.
23
-
24
- ## File Format
25
-
26
- ```markdown
27
- # [Name] Todos
28
-
29
- **Last updated:** YYYY-MM-DD
30
-
31
- ## To Do
32
-
33
- - [ ] Task description
34
- - [ ] Task description (blocked by: reason)
35
-
36
- ## Done
37
-
38
- - [x] Task description (YYYY-MM-DD)
39
- - [x] Task description (YYYY-MM-DD)
40
-
41
- ## Deprecated
42
-
43
- - ~~Task description~~ ... reason. (YYYY-MM-DD)
44
- ```
45
-
46
- ## Rules
47
-
48
- - **Never delete anything.** Items move between sections, never off the page.
49
- - **To Do** ... work that needs to happen.
50
- - **Done** ... completed work. Check the box, add the date.
51
- - **Deprecated** ... planned but no longer needed. Strikethrough, add the reason and date. Not the same as Done. Done means it shipped. Deprecated means the requirement changed.
52
- - **Update the date** at the top of the file every time you edit it.
53
- - **One file per person/agent.** No dated files, no subfolders, no inboxes.
54
- - **Blocked items** stay in To Do with a `(blocked by: reason)` note. Don't move them to a separate section.
55
-
56
- ## When to Use Todos vs GitHub Issues
57
-
58
- | Use | When |
59
- |-----|------|
60
- | **Todo file** | Quick tasks, repo-scoped work, things you'll do this session or this week |
61
- | **GitHub Issue** | Bugs, feature requests, cross-repo work, things that need tracking or discussion |
62
-
63
- Both is fine. File an issue AND add a todo that references it. The todo is your personal checklist. The issue is the team's record.