@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,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// wip-release/mcp-server.mjs
|
|
3
|
+
// MCP server exposing release pipeline as tools.
|
|
4
|
+
// Wraps core.mjs. Registered via .mcp.json.
|
|
5
|
+
|
|
6
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import {
|
|
10
|
+
release, detectCurrentVersion, bumpSemver, buildReleaseNotes,
|
|
11
|
+
} from './core.mjs';
|
|
12
|
+
|
|
13
|
+
const server = new Server(
|
|
14
|
+
{ name: 'wip-release', version: '1.3.0' },
|
|
15
|
+
{ capabilities: { tools: {} } }
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// ── Tool Definitions ──
|
|
19
|
+
|
|
20
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
21
|
+
tools: [
|
|
22
|
+
{
|
|
23
|
+
name: 'release',
|
|
24
|
+
description: 'Run the full release pipeline. Bumps version, updates changelog + SKILL.md, commits, tags, publishes to npm + GitHub. Must be run from repo root or provide repoPath.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
repoPath: { type: 'string', description: 'Absolute path to the repo. Defaults to cwd.' },
|
|
29
|
+
level: { type: 'string', enum: ['patch', 'minor', 'major'], description: 'Semver bump level' },
|
|
30
|
+
notes: { type: 'string', description: 'Changelog entry and release notes summary' },
|
|
31
|
+
dryRun: { type: 'boolean', description: 'Preview only, no changes', default: false },
|
|
32
|
+
noPublish: { type: 'boolean', description: 'Bump + tag only, skip npm/GitHub publish', default: false },
|
|
33
|
+
skipProductCheck: { type: 'boolean', description: 'Skip product doc freshness check', default: false },
|
|
34
|
+
},
|
|
35
|
+
required: ['level', 'notes'],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'release_status',
|
|
40
|
+
description: 'Check current version and what the next version would be for a given bump level.',
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
repoPath: { type: 'string', description: 'Absolute path to the repo. Defaults to cwd.' },
|
|
45
|
+
level: { type: 'string', enum: ['patch', 'minor', 'major'], description: 'Semver bump level to preview' },
|
|
46
|
+
},
|
|
47
|
+
required: ['level'],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
// ── Tool Handlers ──
|
|
54
|
+
|
|
55
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
56
|
+
const { name, arguments: args } = req.params;
|
|
57
|
+
|
|
58
|
+
if (name === 'release') {
|
|
59
|
+
try {
|
|
60
|
+
const result = await release({
|
|
61
|
+
repoPath: args.repoPath || process.cwd(),
|
|
62
|
+
level: args.level,
|
|
63
|
+
notes: args.notes,
|
|
64
|
+
dryRun: args.dryRun || false,
|
|
65
|
+
notesSource: 'flag', // MCP always passes notes directly
|
|
66
|
+
noPublish: args.noPublish || false,
|
|
67
|
+
skipProductCheck: args.skipProductCheck || false,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
content: [{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Release complete: ${result.currentVersion} -> ${result.newVersion}${result.dryRun ? ' (dry run)' : ''}`,
|
|
73
|
+
}],
|
|
74
|
+
};
|
|
75
|
+
} catch (err) {
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: 'text', text: `Release failed: ${err.message}` }],
|
|
78
|
+
isError: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (name === 'release_status') {
|
|
84
|
+
try {
|
|
85
|
+
const repoPath = args.repoPath || process.cwd();
|
|
86
|
+
const current = detectCurrentVersion(repoPath);
|
|
87
|
+
const next = bumpSemver(current, args.level);
|
|
88
|
+
return {
|
|
89
|
+
content: [{
|
|
90
|
+
type: 'text',
|
|
91
|
+
text: `Current: ${current}\nNext (${args.level}): ${next}`,
|
|
92
|
+
}],
|
|
93
|
+
};
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return {
|
|
96
|
+
content: [{ type: 'text', text: `Status check failed: ${err.message}` }],
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const transport = new StdioServerTransport();
|
|
109
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wipcomputer/wip-release",
|
|
3
|
+
"version": "1.9.20",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "One-command release pipeline. Bumps version, updates changelog + SKILL.md, publishes to npm + GitHub.",
|
|
6
|
+
"main": "core.mjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"wip-release": "cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./core.mjs",
|
|
12
|
+
"./cli": "./cli.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node cli.js --help"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"release",
|
|
19
|
+
"version",
|
|
20
|
+
"publish",
|
|
21
|
+
"npm",
|
|
22
|
+
"agent-native",
|
|
23
|
+
"sensor",
|
|
24
|
+
"actuator"
|
|
25
|
+
],
|
|
26
|
+
"author": "Parker Todd Brooks",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/wipcomputer/wip-release.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/wipcomputer/wip-ai-devops-toolbox",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
###### WIP Computer
|
|
2
|
+
|
|
3
|
+
# Repo Init
|
|
4
|
+
|
|
5
|
+
Scaffold the standard `ai/` directory in any repo. Plans, notes, ideas, dev updates, todos. One command.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
- **New repo:** Creates the full `ai/` directory structure
|
|
10
|
+
- **Existing repo:** Moves old `ai/` contents to `ai/_sort/ai_old/` so you can sort at your own pace
|
|
11
|
+
- Nothing is deleted
|
|
12
|
+
|
|
13
|
+
## The `ai/` directory
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
ai/
|
|
17
|
+
plan/ architecture plans, roadmaps
|
|
18
|
+
dev-updates/ what was built, session logs
|
|
19
|
+
todos/
|
|
20
|
+
PUNCHLIST.md blockers to ship
|
|
21
|
+
inboxes/ per-agent action items
|
|
22
|
+
notes/ research, references, raw conversation logs
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The `ai/` folder is the development process. It is not part of the published product. Public repos exclude it via deploy-public.sh.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node tools/wip-repo-init/init.mjs /path/to/repo
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Interfaces
|
|
34
|
+
|
|
35
|
+
- **CLI**: Run from terminal
|
|
36
|
+
- **Skill**: SKILL.md for agent instructions
|
|
37
|
+
|
|
38
|
+
## Part of [AI DevOps Toolbox](https://github.com/wipcomputer/wip-ai-devops-toolbox)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wip-repo-init
|
|
3
|
+
description: Scaffold the standard ai/ directory structure in any repo.
|
|
4
|
+
license: MIT
|
|
5
|
+
interface: [cli, skill]
|
|
6
|
+
metadata:
|
|
7
|
+
display-name: "Repo Init"
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
|
|
10
|
+
author: "Parker Todd Brooks"
|
|
11
|
+
category: repo-management
|
|
12
|
+
capabilities:
|
|
13
|
+
- scaffold-ai-dir
|
|
14
|
+
- template-copy
|
|
15
|
+
requires:
|
|
16
|
+
bins: [node]
|
|
17
|
+
openclaw:
|
|
18
|
+
requires:
|
|
19
|
+
bins: [node]
|
|
20
|
+
install:
|
|
21
|
+
- id: node
|
|
22
|
+
kind: node
|
|
23
|
+
package: "@wipcomputer/wip-repo-init"
|
|
24
|
+
bins: [wip-repo-init]
|
|
25
|
+
label: "Install via npm"
|
|
26
|
+
emoji: "📁"
|
|
27
|
+
compatibility: Requires node. Node.js 18+.
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Repo Init
|
|
31
|
+
|
|
32
|
+
Scaffolds the standard `ai/` directory structure in any repo.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
wip-repo-init /path/to/repo # scaffold ai/ in a repo
|
|
38
|
+
wip-repo-init /path/to/repo --dry-run # preview without changes
|
|
39
|
+
wip-repo-init /path/to/repo --yes # skip confirmation prompt
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## What happens
|
|
43
|
+
|
|
44
|
+
**New repo (no ai/ folder):** Creates the full standard structure with all READMEs explaining what goes where.
|
|
45
|
+
|
|
46
|
+
**Existing repo (ai/ folder exists):** Shows you what will happen and asks for confirmation. If you say yes:
|
|
47
|
+
1. Moves your current `ai/` contents to `ai/_sort/ai_old/`
|
|
48
|
+
2. Scaffolds the new standard structure
|
|
49
|
+
3. You sort files from `ai_old/` into the new structure at your own pace
|
|
50
|
+
|
|
51
|
+
Nothing is deleted. Your old files are all in `ai/_sort/ai_old/`.
|
|
52
|
+
|
|
53
|
+
## The standard ai/ structure
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
ai/
|
|
57
|
+
read-me-first.md <- explains everything, links to all sections
|
|
58
|
+
_sort/ <- holding pen for files that need sorting
|
|
59
|
+
_trash/ <- archive (never delete, move here)
|
|
60
|
+
dev-updates/ <- engineering changelog, auto-detected by wip-release
|
|
61
|
+
product/
|
|
62
|
+
readme-first-product.md <- the product bible
|
|
63
|
+
notes/ <- freeform notes, research
|
|
64
|
+
plans-prds/ <- plans with lifecycle stages
|
|
65
|
+
roadmap.md <- prioritized roadmap
|
|
66
|
+
current/ <- plans being built now
|
|
67
|
+
upcoming/ <- plans that are next
|
|
68
|
+
archive-complete/ <- plans that shipped
|
|
69
|
+
todos/ <- per-agent todo files
|
|
70
|
+
product-ideas/ <- ideas that aren't plans yet
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Every folder has a `_trash/` subfolder. Every section has a README explaining what it is, what goes in it, and how to maintain it.
|
|
74
|
+
|
|
75
|
+
## Interfaces
|
|
76
|
+
|
|
77
|
+
CLI, Skill
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// wip-repo-init: scaffold the standard ai/ directory structure in any repo.
|
|
3
|
+
//
|
|
4
|
+
// New repo (no ai/ folder):
|
|
5
|
+
// Copies the template as-is.
|
|
6
|
+
//
|
|
7
|
+
// Existing repo (ai/ folder exists):
|
|
8
|
+
// 1. Creates the new ai/ structure
|
|
9
|
+
// 2. Moves old ai/ contents into ai/_sort/ai_old/
|
|
10
|
+
// 3. You sort from there at your own pace.
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, cpSync, renameSync, readdirSync, statSync } from 'node:fs';
|
|
13
|
+
import { join, resolve, dirname } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { createInterface } from 'node:readline';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const TEMPLATE = join(__dirname, 'ai');
|
|
19
|
+
|
|
20
|
+
const targetRepo = resolve(process.argv[2] || process.cwd());
|
|
21
|
+
const aiDir = join(targetRepo, 'ai');
|
|
22
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
23
|
+
|
|
24
|
+
const forceYes = process.argv.includes('--yes') || process.argv.includes('-y');
|
|
25
|
+
|
|
26
|
+
function log(msg) { console.log(` ${msg}`); }
|
|
27
|
+
function ok(msg) { console.log(` ✓ ${msg}`); }
|
|
28
|
+
function skip(msg) { console.log(` - ${msg}`); }
|
|
29
|
+
|
|
30
|
+
async function confirm(question) {
|
|
31
|
+
if (forceYes) return true;
|
|
32
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
33
|
+
return new Promise(resolve => {
|
|
34
|
+
rl.question(` ${question} (y/N) `, answer => {
|
|
35
|
+
rl.close();
|
|
36
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Recursively copy template, skipping files that already exist
|
|
42
|
+
function scaffoldDir(src, dest) {
|
|
43
|
+
if (!existsSync(dest)) {
|
|
44
|
+
if (!dryRun) mkdirSync(dest, { recursive: true });
|
|
45
|
+
ok(`Created ${dest.replace(targetRepo + '/', '')}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const entry of readdirSync(src)) {
|
|
49
|
+
if (entry === '.DS_Store') continue;
|
|
50
|
+
const srcPath = join(src, entry);
|
|
51
|
+
const destPath = join(dest, entry);
|
|
52
|
+
const stat = statSync(srcPath);
|
|
53
|
+
|
|
54
|
+
if (stat.isDirectory()) {
|
|
55
|
+
scaffoldDir(srcPath, destPath);
|
|
56
|
+
} else {
|
|
57
|
+
if (existsSync(destPath)) {
|
|
58
|
+
skip(`${destPath.replace(targetRepo + '/', '')} already exists`);
|
|
59
|
+
} else {
|
|
60
|
+
if (!dryRun) cpSync(srcPath, destPath);
|
|
61
|
+
ok(`${destPath.replace(targetRepo + '/', '')}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(` wip-repo-init${dryRun ? ' (dry run)' : ''}`);
|
|
69
|
+
console.log(` Target: ${targetRepo}`);
|
|
70
|
+
console.log(` ${'─'.repeat(40)}`);
|
|
71
|
+
|
|
72
|
+
if (!existsSync(targetRepo)) {
|
|
73
|
+
console.log(` ✗ Target directory does not exist: ${targetRepo}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (existsSync(aiDir)) {
|
|
78
|
+
// Existing ai/ folder: explain what will happen, then confirm
|
|
79
|
+
log('Found existing ai/ folder.');
|
|
80
|
+
console.log('');
|
|
81
|
+
log('Here\'s what will happen:');
|
|
82
|
+
log(' 1. Your current ai/ contents will be moved to ai/_sort/ai_old/');
|
|
83
|
+
log(' 2. The standard ai/ structure will be scaffolded');
|
|
84
|
+
log(' 3. You can sort files from ai_old/ into the new structure at your own pace');
|
|
85
|
+
console.log('');
|
|
86
|
+
log('Nothing is deleted. Your old files will all be in ai/_sort/ai_old/.');
|
|
87
|
+
console.log('');
|
|
88
|
+
|
|
89
|
+
const sortDir = join(aiDir, '_sort');
|
|
90
|
+
const aiOldDir = join(sortDir, 'ai_old');
|
|
91
|
+
|
|
92
|
+
if (existsSync(aiOldDir)) {
|
|
93
|
+
console.log(` ✗ ai/_sort/ai_old/ already exists. A previous init was run but not sorted yet.`);
|
|
94
|
+
console.log(` Sort the files in ai/_sort/ai_old/ first, then run again.`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (dryRun) {
|
|
99
|
+
log('[dry run] Would move old ai/ contents to ai/_sort/ai_old/');
|
|
100
|
+
log('[dry run] Would scaffold new ai/ structure:');
|
|
101
|
+
console.log('');
|
|
102
|
+
scaffoldDir(TEMPLATE, aiDir);
|
|
103
|
+
console.log(`\n ${'─'.repeat(40)}`);
|
|
104
|
+
console.log(' Dry run complete. No changes made.\n');
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const proceed = await confirm('Proceed?');
|
|
109
|
+
if (!proceed) {
|
|
110
|
+
console.log(' Cancelled.\n');
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log('');
|
|
115
|
+
const tmpOld = join(targetRepo, '_ai_old_tmp');
|
|
116
|
+
renameSync(aiDir, tmpOld);
|
|
117
|
+
ok('Moved old ai/ to temporary location');
|
|
118
|
+
|
|
119
|
+
scaffoldDir(TEMPLATE, aiDir);
|
|
120
|
+
|
|
121
|
+
mkdirSync(join(aiDir, '_sort'), { recursive: true });
|
|
122
|
+
renameSync(tmpOld, aiOldDir);
|
|
123
|
+
ok('Moved old ai/ contents to ai/_sort/ai_old/');
|
|
124
|
+
} else {
|
|
125
|
+
// No ai/ folder: scaffold from scratch
|
|
126
|
+
log('No existing ai/ folder. Scaffolding from template.');
|
|
127
|
+
console.log('');
|
|
128
|
+
scaffoldDir(TEMPLATE, aiDir);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(` ${'─'.repeat(40)}`);
|
|
132
|
+
if (dryRun) {
|
|
133
|
+
console.log(' Dry run complete. No changes made.');
|
|
134
|
+
} else {
|
|
135
|
+
ok('Done.');
|
|
136
|
+
if (existsSync(join(aiDir, '_sort', 'ai_old'))) {
|
|
137
|
+
console.log('');
|
|
138
|
+
log('Your old ai/ contents are in ai/_sort/ai_old/');
|
|
139
|
+
log('Sort them into the new structure at your own pace.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log('');
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wipcomputer/wip-repo-init",
|
|
3
|
+
"version": "1.9.20",
|
|
4
|
+
"description": "Scaffold the standard ai/ directory structure in any repo",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wip-repo-init": "init.mjs"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "WIP Computer"
|
|
11
|
+
}
|
|
@@ -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,86 @@
|
|
|
1
|
+
###### WIP Computer
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@wipcomputer/wip-repo-permissions-hook) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-repo-permissions-hook/cli.js) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-repo-permissions-hook/mcp-server.mjs) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-repo-permissions-hook/openclaw.plugin.json) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-repo-permissions-hook/guard.mjs) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-repo-permissions-hook/SKILL.md) [](https://github.com/wipcomputer/wip-ai-devops-toolbox/blob/main/tools/wip-universal-installer/SPEC.md)
|
|
4
|
+
|
|
5
|
+
# wip-repo-permissions-hook
|
|
6
|
+
|
|
7
|
+
## Repo visibility guard. Blocks repos from going public without a -private counterpart.
|
|
8
|
+
|
|
9
|
+
Every repo follows the public/private pattern. The private repo is the working repo with `ai/` folders (plans, todos, dev updates). The public repo is the same code without `ai/`. Making a repo public without the -private counterpart exposes internal development context.
|
|
10
|
+
|
|
11
|
+
This tool blocks that.
|
|
12
|
+
|
|
13
|
+
## How It Works
|
|
14
|
+
|
|
15
|
+
Before any repo visibility change to public, the guard checks:
|
|
16
|
+
|
|
17
|
+
1. Is this a fork of an external project? If yes, allow (exempt).
|
|
18
|
+
2. Does `{repo-name}-private` exist on GitHub? If yes, allow.
|
|
19
|
+
3. Otherwise, block with an error.
|
|
20
|
+
|
|
21
|
+
## Surfaces
|
|
22
|
+
|
|
23
|
+
- **CLI** ... `wip-repo-permissions check|audit|can-publish`
|
|
24
|
+
- **Claude Code hook** ... PreToolUse:Bash, blocks `gh repo edit --visibility public`
|
|
25
|
+
- **OpenClaw plugin** ... before_tool_use lifecycle hook
|
|
26
|
+
- **Cron audit** ... periodic scan of all public repos via ldm-jobs
|
|
27
|
+
|
|
28
|
+
## CLI Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Check a single repo
|
|
32
|
+
node cli.js check wipcomputer/memory-crystal
|
|
33
|
+
# -> OK: memory-crystal-private exists
|
|
34
|
+
|
|
35
|
+
# Check a repo without -private (blocked)
|
|
36
|
+
node cli.js check wipcomputer/wip-bridge
|
|
37
|
+
# -> BLOCKED: no -private counterpart
|
|
38
|
+
|
|
39
|
+
# Audit all public repos in org
|
|
40
|
+
node cli.js audit wipcomputer
|
|
41
|
+
|
|
42
|
+
# Alias for check
|
|
43
|
+
node cli.js can-publish wipcomputer/wip-ai-devops-toolbox
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Claude Code Setup
|
|
47
|
+
|
|
48
|
+
Add to `~/.claude/settings.json`:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"hooks": {
|
|
53
|
+
"PreToolUse": [
|
|
54
|
+
{
|
|
55
|
+
"matcher": "Bash",
|
|
56
|
+
"hooks": [{
|
|
57
|
+
"type": "command",
|
|
58
|
+
"command": "node /path/to/wip-repo-permissions-hook/guard.mjs",
|
|
59
|
+
"timeout": 10
|
|
60
|
+
}]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## OpenClaw Setup
|
|
68
|
+
|
|
69
|
+
Symlink or copy to extensions:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cp -r tools/wip-repo-permissions-hook ~/.ldm/extensions/wip-repo-permissions-hook
|
|
73
|
+
ln -sf ~/.ldm/extensions/wip-repo-permissions-hook ~/.openclaw/extensions/wip-repo-permissions-hook
|
|
74
|
+
openclaw gateway restart
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
CLI, MCP server, OpenClaw plugin, hooks MIT (use anywhere, no restrictions)
|
|
81
|
+
Hosted or cloud service use AGPL (network service distribution)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
AGPL for personal use is free.
|
|
85
|
+
|
|
86
|
+
Built by Parker Todd Brooks, Lēsa (OpenClaw, Claude Opus 4.6), Claude Code (Claude Opus 4.6).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wip-repo-permissions-hook
|
|
3
|
+
description: Repo visibility guard. Blocks repos from going public without a -private counterpart.
|
|
4
|
+
license: MIT
|
|
5
|
+
interface: [cli, module, mcp, hook, plugin]
|
|
6
|
+
metadata:
|
|
7
|
+
display-name: "Repo Visibility Guard"
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
homepage: "https://github.com/wipcomputer/wip-ai-devops-toolbox"
|
|
10
|
+
author: "Parker Todd Brooks"
|
|
11
|
+
category: dev-tools
|
|
12
|
+
capabilities:
|
|
13
|
+
- visibility-check
|
|
14
|
+
- org-audit
|
|
15
|
+
- public-gate
|
|
16
|
+
requires:
|
|
17
|
+
bins: [node, gh]
|
|
18
|
+
openclaw:
|
|
19
|
+
requires:
|
|
20
|
+
bins: [node, gh]
|
|
21
|
+
install:
|
|
22
|
+
- id: node
|
|
23
|
+
kind: node
|
|
24
|
+
package: "@wipcomputer/wip-repo-permissions-hook"
|
|
25
|
+
bins: [wip-repo-permissions]
|
|
26
|
+
label: "Install via npm"
|
|
27
|
+
emoji: "🔒"
|
|
28
|
+
compatibility: Requires node, gh (GitHub CLI). Node.js 18+.
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
# Repo Permissions Hook
|
|
32
|
+
|
|
33
|
+
## What This Does
|
|
34
|
+
|
|
35
|
+
Prevents repos from being made public unless a `-private` counterpart exists on GitHub. Protects internal plans, todos, and development context from accidental exposure.
|
|
36
|
+
|
|
37
|
+
## The Rule
|
|
38
|
+
|
|
39
|
+
Every repo that goes public must have a `{name}-private` repo. The private repo holds `ai/` folders with plans, todos, dev updates, and notes. The public repo has the same code without `ai/`.
|
|
40
|
+
|
|
41
|
+
Forks of external projects are exempt.
|
|
42
|
+
|
|
43
|
+
## How to Use
|
|
44
|
+
|
|
45
|
+
### Check before changing visibility
|
|
46
|
+
```bash
|
|
47
|
+
node cli.js check <org>/<repo>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Audit all public repos
|
|
51
|
+
```bash
|
|
52
|
+
node cli.js audit <org>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Install as Claude Code hook
|
|
56
|
+
Add to `~/.claude/settings.json` under `hooks.PreToolUse` with matcher `"Bash"`.
|
|
57
|
+
|
|
58
|
+
### Install as OpenClaw plugin
|
|
59
|
+
Copy to `~/.ldm/extensions/wip-repo-permissions-hook/` and restart gateway.
|
|
60
|
+
|
|
61
|
+
### MCP
|
|
62
|
+
|
|
63
|
+
Tools: `repo_permissions_check`, `repo_permissions_audit`
|
|
64
|
+
|
|
65
|
+
Add to `.mcp.json`:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"wip-repo-permissions": {
|
|
69
|
+
"command": "node",
|
|
70
|
+
"args": ["/path/to/tools/wip-repo-permissions-hook/mcp-server.mjs"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|