@wipcomputer/wip-ai-devops-toolbox 1.9.46 → 1.9.48

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 CHANGED
@@ -31,6 +31,76 @@
31
31
 
32
32
 
33
33
 
34
+
35
+ ## 1.9.48 (2026-03-20)
36
+
37
+ # Release Notes: wip-ai-devops-toolbox v1.9.48
38
+
39
+ **Document wip-repos claude command in SKILL.md and TECHNICAL.md.**
40
+
41
+ ## What changed
42
+
43
+ - SKILL.md: added `wip-repos claude` commands to the wip-repos section
44
+ - TECHNICAL.md: full documentation of how the ecosystem generator works, template locations, delimiter convention
45
+
46
+ ## Why
47
+
48
+ v1.9.47 shipped the `wip-repos claude` command without updating technical docs. Now documented.
49
+
50
+ ## Issues closed
51
+
52
+ - #212 (docs portion)
53
+
54
+ ## How to verify
55
+
56
+ ```bash
57
+ grep "wip-repos claude" SKILL.md TECHNICAL.md
58
+ ```
59
+
60
+ ## 1.9.47 (2026-03-20)
61
+
62
+ # Release Notes: wip-ai-devops-toolbox v1.9.47
63
+
64
+ **New: `wip-repos claude` command + CLAUDE.md templates.**
65
+
66
+ ## What changed
67
+
68
+ ### `wip-repos claude` (Phases 1-3 of the CLAUDE.md plan)
69
+
70
+ New subcommand that generates cross-repo ecosystem sections in CLAUDE.md files. When an agent opens repo-A, it can't read repo-B. This command pre-generates the context.
71
+
72
+ ```bash
73
+ wip-repos claude # regenerate all repos
74
+ wip-repos claude my-repo # regenerate one repo
75
+ wip-repos claude --init # create CLAUDE.md for repos missing one
76
+ wip-repos claude --dry-run # preview changes
77
+ ```
78
+
79
+ Features:
80
+ - Reads all repos from manifest, extracts metadata (package.json, SKILL.md, directory structure)
81
+ - Generates `## Ecosystem` sections with delimiter comments (`<!-- wip-repos:start/end -->`)
82
+ - Hand-written sections are never overwritten
83
+ - Relevance filtering: only related repos shown (same category + core repos)
84
+ - `--init` creates starter CLAUDE.md from template for repos missing one
85
+
86
+ ### Templates
87
+
88
+ - `templates/global-claude-md.md` ... universal CLAUDE.md for ~/.claude/CLAUDE.md
89
+ - `templates/repo-claude-md.template` ... per-repo starter with ecosystem placeholder
90
+
91
+ ## Why
92
+
93
+ Agents lose context across repos. They can't read sibling repos at runtime. Pre-generating cross-repo maps into CLAUDE.md solves this without requiring runtime access.
94
+
95
+ ## Issues closed
96
+
97
+ - #212 (partial: Phases 1-3 of 6)
98
+
99
+ ## How to verify
100
+
101
+ ```bash
102
+ wip-repos claude --dry-run
103
+ ```
34
104
 
35
105
  ## 1.9.46 (2026-03-18)
36
106
 
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.46"
8
+ version: "1.9.48"
9
9
  homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
10
10
  author: "Parker Todd Brooks"
11
11
  category: dev-tools
@@ -89,7 +89,7 @@ If the user already has the toolbox installed (check `ldm status` or look for `w
89
89
 
90
90
  **Repo Management**
91
91
  4. **Repo Visibility Guard** ... blocks repos from going public without a -private counterpart. Prevents accidental exposure.
92
- 5. **Repo Manifest Reconciler** ... one source of truth for folder structure. Drift detection and auto-sync.
92
+ 5. **Repo Manifest Reconciler** ... one source of truth for folder structure. Drift detection, auto-sync, and cross-repo CLAUDE.md generation.
93
93
  6. **Repo Init** ... scaffolds the standard ai/ directory. Plans, notes, dev updates, todos. One command.
94
94
  7. **README Formatter** ... generates READMEs following the standard format. Staging, review, deploy.
95
95
  8. **Forced Git Worktrees** ... agents never edit on main. Isolated copies, PRs to merge back.
@@ -583,6 +583,10 @@ wip-repos sync # move repos to match manifest
583
583
  wip-repos add <org/repo> <category> # add a repo to the manifest
584
584
  wip-repos move <org/repo> <new-category> # move a repo in the manifest
585
585
  wip-repos tree # show the manifest as a tree
586
+ wip-repos claude # regenerate CLAUDE.md ecosystem sections for all repos
587
+ wip-repos claude <repo> # regenerate one repo's CLAUDE.md
588
+ wip-repos claude --init # create CLAUDE.md for repos missing one
589
+ wip-repos claude --dry-run # preview changes
586
590
  ```
587
591
 
588
592
  **What it does:**
@@ -590,6 +594,7 @@ wip-repos tree # show the manifest as a tree
590
594
  - Compares against the actual filesystem
591
595
  - `check` reports drift (repos in wrong locations, missing repos, unknown repos)
592
596
  - `sync` moves repos to match the manifest
597
+ - `claude` generates cross-repo ecosystem context in CLAUDE.md files. Each repo gets a list of related repos with their interfaces, CLI commands, and descriptions. Agents can't read sibling repos at runtime, so this pre-generates the context they need.
593
598
 
594
599
  **MCP tools:** `repos_check`, `repos_sync_plan`, `repos_add`, `repos_move`, `repos_tree`
595
600
 
package/TECHNICAL.md CHANGED
@@ -189,7 +189,7 @@ bash scripts/deploy-public.sh /path/to/private-repo wipcomputer/public-repo
189
189
 
190
190
  ### wip-repos
191
191
 
192
- Repo manifest reconciler. Makes `repos-manifest.json` the single source of truth for repo organization. Like prettier for folder structure. Move folders around all day; on sync, everything snaps back to where the manifest says.
192
+ Repo manifest reconciler. Makes `repos-manifest.json` the single source of truth for repo organization. Like prettier for folder structure. Move folders around all day; on sync, everything snaps back to where the manifest says. Also generates cross-repo CLAUDE.md ecosystem sections.
193
193
 
194
194
  ```bash
195
195
  # Check for drift
@@ -206,9 +206,27 @@ wip-repos move ldm-os/utilities/my-tool --to ldm-os/devops/my-tool
206
206
 
207
207
  # Generate directory tree
208
208
  wip-repos tree
209
+
210
+ # Generate cross-repo CLAUDE.md ecosystem sections
211
+ wip-repos claude # all repos
212
+ wip-repos claude my-repo # one repo
213
+ wip-repos claude --init # create CLAUDE.md for repos missing one
214
+ wip-repos claude --dry-run # preview
209
215
  ```
210
216
 
211
- **Source:** Pure JavaScript, no build step. [`tools/wip-repos/core.mjs`](tools/wip-repos/core.mjs) (logic), [`tools/wip-repos/cli.mjs`](tools/wip-repos/cli.mjs) (CLI). Zero dependencies.
217
+ **How `wip-repos claude` works:**
218
+
219
+ Agents can't read sibling repos at runtime. This command solves that by pre-generating cross-repo context into each repo's CLAUDE.md.
220
+
221
+ 1. Reads all repos from `repos-manifest.json`
222
+ 2. Extracts metadata from each: `package.json` (name, version, bin, exports), `SKILL.md` (interfaces), directory structure
223
+ 3. For each repo, determines relevant siblings (same category + core repos)
224
+ 4. Generates an `## Ecosystem` section between `<!-- wip-repos:start -->` / `<!-- wip-repos:end -->` delimiter comments
225
+ 5. Hand-written CLAUDE.md content outside the delimiters is never touched
226
+
227
+ Templates at `templates/global-claude-md.md` (for `~/.claude/CLAUDE.md`) and `templates/repo-claude-md.template` (for per-repo starter).
228
+
229
+ **Source:** Pure JavaScript, no build step. [`tools/wip-repos/core.mjs`](tools/wip-repos/core.mjs) (manifest logic), [`tools/wip-repos/claude.mjs`](tools/wip-repos/claude.mjs) (ecosystem generator), [`tools/wip-repos/cli.mjs`](tools/wip-repos/cli.mjs) (CLI). Zero dependencies.
212
230
 
213
231
  [README](tools/wip-repos/README.md)
214
232
 
@@ -0,0 +1,42 @@
1
+ # Release Notes: wip-ai-devops-toolbox v1.9.47
2
+
3
+ **New: `wip-repos claude` command + CLAUDE.md templates.**
4
+
5
+ ## What changed
6
+
7
+ ### `wip-repos claude` (Phases 1-3 of the CLAUDE.md plan)
8
+
9
+ New subcommand that generates cross-repo ecosystem sections in CLAUDE.md files. When an agent opens repo-A, it can't read repo-B. This command pre-generates the context.
10
+
11
+ ```bash
12
+ wip-repos claude # regenerate all repos
13
+ wip-repos claude my-repo # regenerate one repo
14
+ wip-repos claude --init # create CLAUDE.md for repos missing one
15
+ wip-repos claude --dry-run # preview changes
16
+ ```
17
+
18
+ Features:
19
+ - Reads all repos from manifest, extracts metadata (package.json, SKILL.md, directory structure)
20
+ - Generates `## Ecosystem` sections with delimiter comments (`<!-- wip-repos:start/end -->`)
21
+ - Hand-written sections are never overwritten
22
+ - Relevance filtering: only related repos shown (same category + core repos)
23
+ - `--init` creates starter CLAUDE.md from template for repos missing one
24
+
25
+ ### Templates
26
+
27
+ - `templates/global-claude-md.md` ... universal CLAUDE.md for ~/.claude/CLAUDE.md
28
+ - `templates/repo-claude-md.template` ... per-repo starter with ecosystem placeholder
29
+
30
+ ## Why
31
+
32
+ Agents lose context across repos. They can't read sibling repos at runtime. Pre-generating cross-repo maps into CLAUDE.md solves this without requiring runtime access.
33
+
34
+ ## Issues closed
35
+
36
+ - #212 (partial: Phases 1-3 of 6)
37
+
38
+ ## How to verify
39
+
40
+ ```bash
41
+ wip-repos claude --dry-run
42
+ ```
@@ -0,0 +1,22 @@
1
+ # Release Notes: wip-ai-devops-toolbox v1.9.48
2
+
3
+ **Document wip-repos claude command in SKILL.md and TECHNICAL.md.**
4
+
5
+ ## What changed
6
+
7
+ - SKILL.md: added `wip-repos claude` commands to the wip-repos section
8
+ - TECHNICAL.md: full documentation of how the ecosystem generator works, template locations, delimiter convention
9
+
10
+ ## Why
11
+
12
+ v1.9.47 shipped the `wip-repos claude` command without updating technical docs. Now documented.
13
+
14
+ ## Issues closed
15
+
16
+ - #212 (docs portion)
17
+
18
+ ## How to verify
19
+
20
+ ```bash
21
+ grep "wip-repos claude" SKILL.md TECHNICAL.md
22
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ai-devops-toolbox",
3
- "version": "1.9.46",
3
+ "version": "1.9.48",
4
4
  "type": "module",
5
5
  "description": "The complete AI DevOps toolkit for AI-assisted development teams.",
6
6
  "license": "MIT",
@@ -0,0 +1,73 @@
1
+ # CLAUDE.md — Global (WIP Computer)
2
+
3
+ ## Team
4
+
5
+ WIP Computer. Three contributors on every commit:
6
+ - Parker Todd Brooks (human, parkertoddbrooks)
7
+ - Lesa (OpenClaw agent, lesaai)
8
+ - Claude Code (Claude Opus 4.6)
9
+
10
+ ## Git Rules
11
+
12
+ **Never push directly to main.** Branch, PR, merge. Every time.
13
+ **Never squash merge.** Always `--merge`. Squashing destroys co-author attribution.
14
+ **Always include `--delete-branch`** on `gh pr merge`.
15
+ **Co-authors on every commit:**
16
+ ```
17
+ Co-Authored-By: Parker Todd Brooks <parkertoddbrooks@users.noreply.github.com>
18
+ Co-Authored-By: Lesa <lesaai@icloud.com>
19
+ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20
+ ```
21
+
22
+ ## Branch Prefixes
23
+
24
+ `cc-mini/` for Claude Code on Mac mini. `cc-air/` for MacBook Air. `lesa-mini/` for Lesa.
25
+
26
+ ## Worktree Workflow
27
+
28
+ All edits happen in worktrees. Never edit directly on main.
29
+ ```bash
30
+ ldm worktree add cc-mini/feature-name # preferred
31
+ # or: git worktree add ../_worktrees/<repo>--<branch> -b <branch>
32
+ ```
33
+
34
+ ## Writing Style
35
+
36
+ **Never use em dashes.** Use periods, colons, semicolons, or "..." instead.
37
+
38
+ ## Release Process
39
+
40
+ 1. Branch + commit + push
41
+ 2. Release notes file on the branch: `RELEASE-NOTES-v{version}.md` (dashes, not dots)
42
+ 3. `gh pr create` then `gh pr merge --merge --delete-branch`
43
+ 4. `git checkout main && git pull`
44
+ 5. `wip-release patch` (auto-detects release notes file)
45
+ 6. `deploy-public.sh` to sync public repo
46
+
47
+ **Release notes go on the feature branch, committed with the code.** Not as a separate PR.
48
+
49
+ ## Tools
50
+
51
+ - `wip-release` for releases (version bump, changelog, npm, GitHub release)
52
+ - `wip-file-guard` protects CLAUDE.md, SOUL.md, MEMORY.md, SHARED-CONTEXT.md
53
+ - `wip-branch-guard` blocks writes on main, teaches the workflow
54
+ - `ldm install` for installing/updating extensions
55
+ - `ldm doctor` for health checks
56
+
57
+ ## Exclude from npm
58
+
59
+ Always add to `.npmignore`:
60
+ ```
61
+ CLAUDE.md
62
+ ai/
63
+ .claude/
64
+ _worktrees/
65
+ ```
66
+
67
+ ## Boot Sequence
68
+
69
+ Read the repo's own CLAUDE.md first. Then check `ai/read-me-first.md` if it exists.
70
+
71
+ ## Dev Guide
72
+
73
+ Full conventions: `DEV-GUIDE-GENERAL-PUBLIC.md` in wip-ai-devops-toolbox.
@@ -0,0 +1,24 @@
1
+ # CLAUDE.md — {{repo-name}}
2
+
3
+ ## Boot Sequence
4
+ 1. Read this file completely before acting
5
+ 2. Read `ai/read-me-first.md` if it exists
6
+ 3. Never commit directly to main. Use worktrees.
7
+
8
+ ## Project Overview
9
+ - **Description:** {{description}}
10
+ - **Language:** {{language}}
11
+ - **Test:** `{{scripts.test}}`
12
+ - **Build:** `{{scripts.build}}`
13
+ - **Lint:** `{{scripts.lint}}`
14
+
15
+ ## Architecture
16
+ {{detected-dirs}}
17
+
18
+ ## Repo-Specific Guardrails
19
+ {{guardrails}}
20
+
21
+ ## Ecosystem
22
+ <!-- wip-repos:start -->
23
+ _Run `wip-repos claude` to populate this section with cross-repo context._
24
+ <!-- wip-repos:end -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/deploy-public",
3
- "version": "1.9.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-branch-guard",
3
- "version": "1.9.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-release",
3
- "version": "1.9.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
4
4
  "type": "module",
5
5
  "description": "Repo visibility guard. Blocks repos from going public without a -private counterpart.",
6
6
  "main": "core.mjs",
@@ -0,0 +1,248 @@
1
+ /**
2
+ * wip-repos claude ... generate cross-repo CLAUDE.md ecosystem sections
3
+ *
4
+ * Reads all repos on disk via the manifest, extracts metadata from each,
5
+ * and writes an ## Ecosystem section into each repo's CLAUDE.md.
6
+ *
7
+ * Usage:
8
+ * wip-repos claude # regenerate all repos
9
+ * wip-repos claude <repo> # regenerate one repo
10
+ * wip-repos claude --init # create CLAUDE.md for repos missing one
11
+ * wip-repos claude --dry-run # preview changes
12
+ */
13
+
14
+ import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'node:fs';
15
+ import { join, basename, resolve, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+ const TEMPLATE_PATH = join(__dirname, '..', '..', 'templates', 'repo-claude-md.template');
20
+ const START_MARKER = '<!-- wip-repos:start -->';
21
+ const END_MARKER = '<!-- wip-repos:end -->';
22
+
23
+ function readJSON(path) {
24
+ try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
25
+ }
26
+
27
+ /**
28
+ * Extract metadata from a single repo on disk.
29
+ */
30
+ function extractRepoMeta(repoPath) {
31
+ const pkg = readJSON(join(repoPath, 'package.json'));
32
+ const name = pkg?.name || basename(repoPath);
33
+ const description = pkg?.description || '';
34
+ const version = pkg?.version || '';
35
+ const exports = pkg?.exports ? Object.keys(pkg.exports) : [];
36
+ const binCommands = pkg?.bin ? Object.keys(pkg.bin) : [];
37
+ const scripts = pkg?.scripts || {};
38
+
39
+ // Detect interfaces
40
+ const interfaces = [];
41
+ if (binCommands.length > 0) interfaces.push('CLI');
42
+ if (pkg?.main || pkg?.exports) interfaces.push('Module');
43
+ if (existsSync(join(repoPath, 'mcp-server.mjs')) || existsSync(join(repoPath, 'dist', 'mcp-server.js'))) interfaces.push('MCP');
44
+ if (existsSync(join(repoPath, 'openclaw.plugin.json'))) interfaces.push('OpenClaw Plugin');
45
+ if (existsSync(join(repoPath, 'SKILL.md'))) interfaces.push('Skill');
46
+
47
+ // Detect key dirs
48
+ const dirs = [];
49
+ for (const d of ['src', 'lib', 'tools', 'bin', 'dist', 'test', 'scripts', 'ai']) {
50
+ if (existsSync(join(repoPath, d))) dirs.push(d + '/');
51
+ }
52
+
53
+ return {
54
+ name,
55
+ description,
56
+ version,
57
+ interfaces,
58
+ binCommands,
59
+ dirs,
60
+ scripts,
61
+ path: repoPath,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Determine which repos are relevant to a given repo.
67
+ * Returns only repos in the same category or that share dependencies.
68
+ */
69
+ function filterRelevant(targetMeta, allMetas, manifest) {
70
+ // Find target's category from manifest
71
+ const targetBase = basename(targetMeta.path);
72
+ let targetCategory = null;
73
+
74
+ for (const [category, repos] of Object.entries(manifest.repos || {})) {
75
+ if (repos.some(r => r.local?.includes(targetBase) || r.name === targetBase)) {
76
+ targetCategory = category;
77
+ break;
78
+ }
79
+ }
80
+
81
+ // Core repos every repo should know about
82
+ const coreNames = ['wip-ldm-os', 'wip-ai-devops-toolbox', 'memory-crystal'];
83
+
84
+ return allMetas.filter(m => {
85
+ if (m.path === targetMeta.path) return false; // skip self
86
+ const mBase = basename(m.path);
87
+ // Same category
88
+ if (targetCategory) {
89
+ for (const repos of Object.values(manifest.repos || {})) {
90
+ if (repos.some(r => r.local?.includes(mBase))) return true;
91
+ }
92
+ }
93
+ // Core repo
94
+ if (coreNames.some(c => mBase.includes(c))) return true;
95
+ return false;
96
+ }).slice(0, 15); // cap at 15 to keep CLAUDE.md focused
97
+ }
98
+
99
+ /**
100
+ * Generate the ecosystem section for a repo.
101
+ */
102
+ function generateEcosystem(targetMeta, relevantMetas) {
103
+ const lines = [];
104
+ for (const m of relevantMetas) {
105
+ const relPath = basename(m.path);
106
+ lines.push(`### ${m.name}`);
107
+ lines.push(`**Path:** \`${relPath}\``);
108
+ if (m.description) lines.push(m.description);
109
+ if (m.interfaces.length > 0) lines.push(`**Interfaces:** ${m.interfaces.join(', ')}`);
110
+ if (m.binCommands.length > 0) lines.push(`**CLI:** ${m.binCommands.join(', ')}`);
111
+ if (m.version) lines.push(`**Version:** ${m.version}`);
112
+ lines.push('');
113
+ }
114
+ return lines.join('\n').trim();
115
+ }
116
+
117
+ /**
118
+ * Update the ecosystem section in a CLAUDE.md file.
119
+ * Only replaces content between the delimiter comments.
120
+ */
121
+ function updateEcosystemSection(claudeMdPath, ecosystemContent) {
122
+ const content = readFileSync(claudeMdPath, 'utf8');
123
+ const startIdx = content.indexOf(START_MARKER);
124
+ const endIdx = content.indexOf(END_MARKER);
125
+
126
+ if (startIdx === -1 || endIdx === -1) {
127
+ // No delimiters found. Append section.
128
+ const section = `\n## Ecosystem (auto-generated by wip-repos claude)\n${START_MARKER}\n\n${ecosystemContent}\n\n${END_MARKER}\n`;
129
+ return content + section;
130
+ }
131
+
132
+ // Replace between delimiters
133
+ const before = content.substring(0, startIdx + START_MARKER.length);
134
+ const after = content.substring(endIdx);
135
+ return `${before}\n\n${ecosystemContent}\n\n${after}`;
136
+ }
137
+
138
+ /**
139
+ * Create a starter CLAUDE.md from the template.
140
+ */
141
+ function createFromTemplate(repoPath, meta) {
142
+ const template = existsSync(TEMPLATE_PATH)
143
+ ? readFileSync(TEMPLATE_PATH, 'utf8')
144
+ : '# CLAUDE.md\n\n## Ecosystem\n<!-- wip-repos:start -->\n<!-- wip-repos:end -->\n';
145
+
146
+ let content = template
147
+ .replace(/\{\{repo-name\}\}/g, meta.name)
148
+ .replace(/\{\{description\}\}/g, meta.description || 'No description')
149
+ .replace(/\{\{language\}\}/g, meta.scripts?.build?.includes('tsc') ? 'TypeScript' : 'JavaScript')
150
+ .replace(/\{\{scripts\.test\}\}/g, meta.scripts?.test || 'npm test')
151
+ .replace(/\{\{scripts\.build\}\}/g, meta.scripts?.build || 'npm run build')
152
+ .replace(/\{\{scripts\.lint\}\}/g, meta.scripts?.lint || 'npm run lint')
153
+ .replace(/\{\{detected-dirs\}\}/g, meta.dirs.length > 0 ? meta.dirs.map(d => `- \`${d}\``).join('\n') : '(no standard dirs detected)')
154
+ .replace(/\{\{guardrails\}\}/g, existsSync(join(repoPath, '.license-guard.json')) ? '- Dual-license (MIT + AGPL). See LICENSE.' : '');
155
+
156
+ return content;
157
+ }
158
+
159
+ /**
160
+ * Main entry point.
161
+ */
162
+ export function runClaude(manifestPath, args = []) {
163
+ const dryRun = args.includes('--dry-run');
164
+ const init = args.includes('--init');
165
+ const targetRepo = args.find(a => !a.startsWith('--'));
166
+
167
+ const manifest = readJSON(manifestPath);
168
+ if (!manifest) {
169
+ console.error(' Could not read manifest at ' + manifestPath);
170
+ process.exit(1);
171
+ }
172
+
173
+ // Find all repo paths from manifest
174
+ const manifestDir = dirname(manifestPath);
175
+ const repoPaths = [];
176
+ for (const [category, repos] of Object.entries(manifest.repos || {})) {
177
+ for (const repo of repos) {
178
+ const localPath = repo.local ? resolve(manifestDir, repo.local) : null;
179
+ if (localPath && existsSync(localPath)) {
180
+ repoPaths.push(localPath);
181
+ }
182
+ }
183
+ }
184
+
185
+ if (repoPaths.length === 0) {
186
+ console.log(' No repos found in manifest.');
187
+ return;
188
+ }
189
+
190
+ // Extract metadata from all repos
191
+ const allMetas = repoPaths.map(p => extractRepoMeta(p));
192
+
193
+ // Filter to target if specified
194
+ const targets = targetRepo
195
+ ? allMetas.filter(m => basename(m.path).includes(targetRepo))
196
+ : allMetas;
197
+
198
+ if (targets.length === 0) {
199
+ console.error(` No repo matching "${targetRepo}" found.`);
200
+ process.exit(1);
201
+ }
202
+
203
+ console.log(` Processing ${targets.length} repo(s)...`);
204
+ let created = 0, updated = 0, skipped = 0;
205
+
206
+ for (const meta of targets) {
207
+ const claudeMdPath = join(meta.path, 'CLAUDE.md');
208
+ const repoName = basename(meta.path);
209
+
210
+ if (!existsSync(claudeMdPath)) {
211
+ if (init) {
212
+ const content = createFromTemplate(meta.path, meta);
213
+ if (dryRun) {
214
+ console.log(` [create] ${repoName}/CLAUDE.md`);
215
+ } else {
216
+ writeFileSync(claudeMdPath, content);
217
+ console.log(` + Created ${repoName}/CLAUDE.md`);
218
+ }
219
+ created++;
220
+ } else {
221
+ skipped++;
222
+ }
223
+ continue;
224
+ }
225
+
226
+ // Generate ecosystem section
227
+ const relevant = filterRelevant(meta, allMetas, manifest);
228
+ const ecosystem = generateEcosystem(meta, relevant);
229
+ const newContent = updateEcosystemSection(claudeMdPath, ecosystem);
230
+ const oldContent = readFileSync(claudeMdPath, 'utf8');
231
+
232
+ if (newContent === oldContent) {
233
+ skipped++;
234
+ continue;
235
+ }
236
+
237
+ if (dryRun) {
238
+ console.log(` [update] ${repoName}/CLAUDE.md (${relevant.length} related repos)`);
239
+ } else {
240
+ writeFileSync(claudeMdPath, newContent);
241
+ console.log(` + Updated ${repoName}/CLAUDE.md (${relevant.length} related repos)`);
242
+ }
243
+ updated++;
244
+ }
245
+
246
+ console.log('');
247
+ console.log(` ${dryRun ? 'Dry run' : 'Done'}. ${created} created, ${updated} updated, ${skipped} skipped.`);
248
+ }
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  import { check, planSync, executeSync, addRepo, moveRepo, generateReadmeTree, loadManifest } from './core.mjs';
15
+ import { runClaude } from './claude.mjs';
15
16
  import { resolve, dirname, join } from 'node:path';
16
17
  import { readFileSync } from 'node:fs';
17
18
  import { fileURLToPath } from 'node:url';
@@ -173,6 +174,11 @@ try {
173
174
  break;
174
175
  }
175
176
 
177
+ case 'claude': {
178
+ runClaude(manifestPath, args.slice(1));
179
+ break;
180
+ }
181
+
176
182
  default:
177
183
  usage();
178
184
  if (command && command !== '--help' && command !== '-h') {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-repos",
3
- "version": "1.9.46",
3
+ "version": "1.9.48",
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.46",
3
+ "version": "1.9.48",
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",