@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,367 @@
1
+ /**
2
+ * Scanner — detects dependencies and their licenses across package managers.
3
+ * Supports: npm, pip, cargo, go modules. Works offline.
4
+ */
5
+
6
+ import { readFileSync, existsSync, readdirSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { execSync } from "node:child_process";
9
+ import { detectLicenseFromText, normalizeSpdx, type LicenseId } from "./detector.js";
10
+ import {
11
+ readLedger, writeLedger, upsertEntry, findEntry, addAlert,
12
+ archiveSnapshot, hasLicenseChanged,
13
+ type Ledger, type LedgerEntry, type DependencyType,
14
+ } from "./ledger.js";
15
+
16
+ export interface ScanResult {
17
+ name: string;
18
+ source: string;
19
+ type: DependencyType;
20
+ detectedLicense: LicenseId;
21
+ licenseText?: string;
22
+ wasChanged: boolean;
23
+ isNew: boolean;
24
+ }
25
+
26
+ interface ScanOptions {
27
+ repoRoot: string;
28
+ offline?: boolean;
29
+ verbose?: boolean;
30
+ }
31
+
32
+ // ─── License file discovery ───
33
+
34
+ const LICENSE_NAMES = ["LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "COPYING", "COPYING.md"];
35
+
36
+ export function findLicenseFile(dir: string): string | null {
37
+ for (const name of LICENSE_NAMES) {
38
+ const p = join(dir, name);
39
+ if (existsSync(p)) return p;
40
+ }
41
+ return null;
42
+ }
43
+
44
+ export function readLicenseFromDir(dir: string): { license: LicenseId; text: string } | null {
45
+ const p = findLicenseFile(dir);
46
+ if (!p) return null;
47
+ const text = readFileSync(p, "utf-8");
48
+ return { license: detectLicenseFromText(text), text };
49
+ }
50
+
51
+ // ─── Package manager detection ───
52
+
53
+ function detectPackageManagers(repoRoot: string): DependencyType[] {
54
+ const types: DependencyType[] = [];
55
+ if (existsSync(join(repoRoot, "package.json"))) types.push("npm");
56
+ if (existsSync(join(repoRoot, "requirements.txt")) || existsSync(join(repoRoot, "Pipfile")) || existsSync(join(repoRoot, "pyproject.toml"))) types.push("pip");
57
+ if (existsSync(join(repoRoot, "Cargo.toml"))) types.push("cargo");
58
+ if (existsSync(join(repoRoot, "go.mod"))) types.push("go");
59
+ return types;
60
+ }
61
+
62
+ // ─── npm scanning ───
63
+
64
+ function scanNpmDeps(repoRoot: string, offline: boolean): ScanResult[] {
65
+ const results: ScanResult[] = [];
66
+ const pkgPath = join(repoRoot, "package.json");
67
+ if (!existsSync(pkgPath)) return results;
68
+
69
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
70
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
71
+
72
+ for (const [name, _version] of Object.entries(allDeps)) {
73
+ let detectedLicense: LicenseId = "UNKNOWN";
74
+ let licenseText: string | undefined;
75
+
76
+ // Check node_modules first (works offline)
77
+ const modDir = join(repoRoot, "node_modules", name);
78
+ if (existsSync(modDir)) {
79
+ const fromFile = readLicenseFromDir(modDir);
80
+ if (fromFile) {
81
+ detectedLicense = fromFile.license;
82
+ licenseText = fromFile.text;
83
+ }
84
+ // Also check package.json license field
85
+ const modPkg = join(modDir, "package.json");
86
+ if (existsSync(modPkg) && detectedLicense === "UNKNOWN") {
87
+ try {
88
+ const modMeta = JSON.parse(readFileSync(modPkg, "utf-8"));
89
+ if (modMeta.license) detectedLicense = normalizeSpdx(modMeta.license);
90
+ } catch { /* skip */ }
91
+ }
92
+ }
93
+
94
+ // Try npm view if online and still unknown
95
+ if (detectedLicense === "UNKNOWN" && !offline) {
96
+ try {
97
+ const out = execSync(`npm view ${name} license 2>/dev/null`, { encoding: "utf-8", timeout: 10000 }).trim();
98
+ if (out) detectedLicense = normalizeSpdx(out);
99
+ } catch { /* offline or not found */ }
100
+ }
101
+
102
+ results.push({
103
+ name,
104
+ source: `npm:${name}`,
105
+ type: "npm",
106
+ detectedLicense,
107
+ licenseText,
108
+ wasChanged: false,
109
+ isNew: true,
110
+ });
111
+ }
112
+
113
+ return results;
114
+ }
115
+
116
+ // ─── pip scanning ───
117
+
118
+ function parsePipDeps(repoRoot: string): string[] {
119
+ const names: string[] = [];
120
+ const reqPath = join(repoRoot, "requirements.txt");
121
+ if (existsSync(reqPath)) {
122
+ const lines = readFileSync(reqPath, "utf-8").split("\n");
123
+ for (const line of lines) {
124
+ const trimmed = line.trim();
125
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) continue;
126
+ const name = trimmed.split(/[=<>!~\[]/)[0].trim();
127
+ if (name) names.push(name);
128
+ }
129
+ }
130
+ return names;
131
+ }
132
+
133
+ function scanPipDeps(repoRoot: string, offline: boolean): ScanResult[] {
134
+ const deps = parsePipDeps(repoRoot);
135
+ const results: ScanResult[] = [];
136
+
137
+ for (const name of deps) {
138
+ let detectedLicense: LicenseId = "UNKNOWN";
139
+
140
+ if (!offline) {
141
+ try {
142
+ const out = execSync(`pip show ${name} 2>/dev/null`, { encoding: "utf-8", timeout: 10000 });
143
+ const match = out.match(/^License:\s*(.+)$/m);
144
+ if (match) detectedLicense = normalizeSpdx(match[1]);
145
+ } catch { /* skip */ }
146
+ }
147
+
148
+ results.push({
149
+ name,
150
+ source: `pip:${name}`,
151
+ type: "pip",
152
+ detectedLicense,
153
+ wasChanged: false,
154
+ isNew: true,
155
+ });
156
+ }
157
+
158
+ return results;
159
+ }
160
+
161
+ // ─── cargo scanning ───
162
+
163
+ function scanCargoDeps(repoRoot: string, offline: boolean): ScanResult[] {
164
+ const results: ScanResult[] = [];
165
+ const cargoPath = join(repoRoot, "Cargo.toml");
166
+ if (!existsSync(cargoPath)) return results;
167
+
168
+ const content = readFileSync(cargoPath, "utf-8");
169
+ const depSection = content.match(/\[dependencies\]([\s\S]*?)(\[|$)/);
170
+ if (!depSection) return results;
171
+
172
+ const lines = depSection[1].split("\n");
173
+ for (const line of lines) {
174
+ const match = line.match(/^(\S+)\s*=/);
175
+ if (!match) continue;
176
+ const name = match[1];
177
+
178
+ let detectedLicense: LicenseId = "UNKNOWN";
179
+ if (!offline) {
180
+ try {
181
+ const out = execSync(`cargo info ${name} 2>/dev/null`, { encoding: "utf-8", timeout: 10000 });
182
+ const lMatch = out.match(/license:\s*(.+)/i);
183
+ if (lMatch) detectedLicense = normalizeSpdx(lMatch[1]);
184
+ } catch { /* skip */ }
185
+ }
186
+
187
+ results.push({
188
+ name,
189
+ source: `cargo:${name}`,
190
+ type: "cargo",
191
+ detectedLicense,
192
+ wasChanged: false,
193
+ isNew: true,
194
+ });
195
+ }
196
+
197
+ return results;
198
+ }
199
+
200
+ // ─── go scanning ───
201
+
202
+ function scanGoDeps(repoRoot: string, _offline: boolean): ScanResult[] {
203
+ const results: ScanResult[] = [];
204
+ const goModPath = join(repoRoot, "go.mod");
205
+ if (!existsSync(goModPath)) return results;
206
+
207
+ const content = readFileSync(goModPath, "utf-8");
208
+ const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/);
209
+ const lines = requireBlock ? requireBlock[1].split("\n") : [];
210
+
211
+ // Also handle single-line requires
212
+ const singleRequires = content.match(/^require\s+(\S+)\s+/gm) ?? [];
213
+ for (const sr of singleRequires) {
214
+ const m = sr.match(/^require\s+(\S+)/);
215
+ if (m) lines.push(m[1]);
216
+ }
217
+
218
+ for (const line of lines) {
219
+ const trimmed = line.trim();
220
+ if (!trimmed || trimmed.startsWith("//")) continue;
221
+ const parts = trimmed.split(/\s+/);
222
+ const name = parts[0];
223
+ if (!name || name.startsWith(")")) continue;
224
+
225
+ results.push({
226
+ name,
227
+ source: `go:${name}`,
228
+ type: "go",
229
+ detectedLicense: "UNKNOWN", // Go modules need network for license check
230
+ wasChanged: false,
231
+ isNew: true,
232
+ });
233
+ }
234
+
235
+ return results;
236
+ }
237
+
238
+ // ─── Fork/upstream scanning ───
239
+
240
+ function scanUpstreamLicense(repoRoot: string, offline: boolean): ScanResult | null {
241
+ // Check if there's an upstream remote
242
+ try {
243
+ const remote = execSync("git remote get-url upstream 2>/dev/null", {
244
+ cwd: repoRoot, encoding: "utf-8", timeout: 5000
245
+ }).trim();
246
+ if (!remote) return null;
247
+
248
+ // Fetch upstream (if online)
249
+ if (!offline) {
250
+ try {
251
+ execSync("git fetch upstream --quiet 2>/dev/null", { cwd: repoRoot, timeout: 30000 });
252
+ } catch { /* offline, use cached */ }
253
+ }
254
+
255
+ // Try to read LICENSE from upstream/main or upstream/master
256
+ for (const branch of ["upstream/main", "upstream/master"]) {
257
+ try {
258
+ const text = execSync(`git show ${branch}:LICENSE 2>/dev/null`, {
259
+ cwd: repoRoot, encoding: "utf-8", timeout: 5000
260
+ });
261
+ if (text) {
262
+ const license = detectLicenseFromText(text);
263
+ return {
264
+ name: "upstream",
265
+ source: remote,
266
+ type: "fork",
267
+ detectedLicense: license,
268
+ licenseText: text,
269
+ wasChanged: false,
270
+ isNew: true,
271
+ };
272
+ }
273
+ } catch { /* try next branch */ }
274
+ }
275
+ } catch { /* no upstream remote */ }
276
+
277
+ return null;
278
+ }
279
+
280
+ // ─── Main scan ───
281
+
282
+ export function scanAll(opts: ScanOptions): ScanResult[] {
283
+ const { repoRoot, offline = false } = opts;
284
+ const managers = detectPackageManagers(repoRoot);
285
+ const results: ScanResult[] = [];
286
+
287
+ // Always check upstream fork
288
+ const upstream = scanUpstreamLicense(repoRoot, offline);
289
+ if (upstream) results.push(upstream);
290
+
291
+ if (managers.includes("npm")) results.push(...scanNpmDeps(repoRoot, offline));
292
+ if (managers.includes("pip")) results.push(...scanPipDeps(repoRoot, offline));
293
+ if (managers.includes("cargo")) results.push(...scanCargoDeps(repoRoot, offline));
294
+ if (managers.includes("go")) results.push(...scanGoDeps(repoRoot, offline));
295
+
296
+ return results;
297
+ }
298
+
299
+ /**
300
+ * Run a full scan and update the ledger. Returns results with change detection.
301
+ */
302
+ export function scanAndUpdate(opts: ScanOptions): ScanResult[] {
303
+ const { repoRoot } = opts;
304
+ const ledger = readLedger(repoRoot);
305
+ const results = scanAll(opts);
306
+ const today = new Date().toISOString().slice(0, 10);
307
+
308
+ for (const result of results) {
309
+ const existing = findEntry(ledger, result.name);
310
+
311
+ if (existing) {
312
+ result.isNew = false;
313
+ existing.license_current = result.detectedLicense;
314
+ existing.last_checked = today;
315
+
316
+ if (hasLicenseChanged(existing)) {
317
+ existing.status = "changed";
318
+ result.wasChanged = true;
319
+ addAlert(ledger, result.name, existing.license_at_adoption, result.detectedLicense);
320
+ } else {
321
+ existing.status = "clean";
322
+ }
323
+
324
+ upsertEntry(ledger, existing);
325
+ } else {
326
+ // New dependency
327
+ const entry: LedgerEntry = {
328
+ name: result.name,
329
+ source: result.source,
330
+ type: result.type,
331
+ license_at_adoption: result.detectedLicense,
332
+ license_current: result.detectedLicense,
333
+ adopted_date: today,
334
+ last_checked: today,
335
+ status: "clean",
336
+ };
337
+ upsertEntry(ledger, entry);
338
+ }
339
+
340
+ // Archive license text if available
341
+ if (result.licenseText) {
342
+ archiveSnapshot(repoRoot, result.name, result.licenseText, today);
343
+ }
344
+ }
345
+
346
+ ledger.last_full_scan = new Date().toISOString();
347
+ writeLedger(repoRoot, ledger);
348
+
349
+ return results;
350
+ }
351
+
352
+ /**
353
+ * Gate check — returns true if safe to proceed, false if blocked.
354
+ */
355
+ export function gateCheck(repoRoot: string, offline: boolean): { safe: boolean; results: ScanResult[]; alerts: string[] } {
356
+ const results = scanAndUpdate({ repoRoot, offline });
357
+ const changed = results.filter((r) => r.wasChanged);
358
+ const alerts = changed.map(
359
+ (r) => `🚫 LICENSE CHANGED: ${r.name} — was adopted under different terms, now detected as ${r.detectedLicense}`
360
+ );
361
+
362
+ return {
363
+ safe: changed.length === 0,
364
+ results,
365
+ alerts,
366
+ };
367
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "declaration": true,
11
+ "sourceMap": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
@@ -0,0 +1,49 @@
1
+ ###### WIP Computer
2
+
3
+ # README Formatter
4
+
5
+ Generate or validate READMEs that follow the WIP Computer standard. Badges, title, tagline, "Teach Your AI" block, features, interface coverage table, license.
6
+
7
+ ## What it does
8
+
9
+ - Generates separate section files (README-init-badges.md, README-init-features.md, etc.) so you can edit any section independently
10
+ - Deploy assembles them into the final README
11
+ - Same pattern as release notes: staging, review, deploy
12
+ - Validates existing READMEs against the standard
13
+
14
+ ## Templates
15
+
16
+ All standard content lives in `ai/wip-templates/readme/`. Edit the templates, every tool picks up the changes. No code changes needed.
17
+
18
+ | Template | What it is |
19
+ |----------|-----------|
20
+ | `wip-lic-footer.md` | License section (plain text + markdown formats) |
21
+ | `cla.md` | Contributor License Agreement |
22
+ | `LICENSE.md` | Full dual MIT+AGPLv3 LICENSE file |
23
+ | `prompt.md` | Standard "Teach your AI" install prompt template |
24
+
25
+ Both `wip-readme-format` and `wip-license-guard` read from these templates at runtime.
26
+
27
+ ## Usage
28
+
29
+ ```bash
30
+ # Generate section files for review
31
+ node tools/wip-readme-format/format.mjs /path/to/repo
32
+
33
+ # Assemble sections into final README
34
+ node tools/wip-readme-format/format.mjs /path/to/repo --deploy
35
+
36
+ # Preview without writing
37
+ node tools/wip-readme-format/format.mjs /path/to/repo --dry-run
38
+ ```
39
+
40
+ ## Requirements
41
+
42
+ - node (18+)
43
+
44
+ ## Interfaces
45
+
46
+ - **CLI**: Command-line tool
47
+ - **Skill**: SKILL.md for agent instructions
48
+
49
+ ## Part of [AI DevOps Toolbox](https://github.com/wipcomputer/wip-ai-devops-toolbox)
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: wip-readme-format
3
+ description: Reformat any repo's README to follow the WIP Computer standard.
4
+ license: MIT
5
+ interface: [cli, skill]
6
+ metadata:
7
+ display-name: "README Formatter"
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
+ - readme-generation
14
+ - readme-validation
15
+ - section-staging
16
+ requires:
17
+ bins: [node]
18
+ openclaw:
19
+ requires:
20
+ bins: [node]
21
+ install:
22
+ - id: node
23
+ kind: node
24
+ package: "@wipcomputer/wip-readme-format"
25
+ bins: [wip-readme-format]
26
+ label: "Install via npm"
27
+ emoji: "📝"
28
+ compatibility: Requires node. Node.js 18+.
29
+ ---
30
+
31
+ # README Formatter
32
+
33
+ Reformats a repo's README to follow the WIP Computer standard. Agent-first, human-readable.
34
+
35
+ ## Commands
36
+
37
+ ```
38
+ wip-readme-format /path/to/repo # rewrite README.md
39
+ wip-readme-format /path/to/repo --dry-run # preview without writing
40
+ wip-readme-format /path/to/repo --check # validate, exit 0/1
41
+ ```
42
+
43
+ ## What happens
44
+
45
+ 1. Detects all interfaces (CLI, Module, MCP, OC Plugin, Skill, CC Hook)
46
+ 2. Reads package.json for name, description, repo URL
47
+ 3. Reads SKILL.md for features
48
+ 4. Generates the README following the standard:
49
+ - WIP Computer header + interface badges
50
+ - Title + tagline
51
+ - "Teach Your AI" onboarding block
52
+ - Features list
53
+ - Interface coverage table (toolbox repos only)
54
+ - More Info links
55
+ - License block
56
+ 5. Moves technical content to TECHNICAL.md (never deleted)
57
+
58
+ ## The standard
59
+
60
+ ```
61
+ [badges]
62
+ # Tool Name
63
+ Tagline.
64
+
65
+ ## Teach Your AI to [verb]
66
+ [onboarding prompt block]
67
+
68
+ ## Features
69
+ [feature list]
70
+
71
+ ## Interface Coverage (toolbox only)
72
+ [auto-generated table]
73
+
74
+ ## More Info
75
+ - Technical Documentation ... link
76
+ - Universal Interface Spec ... link
77
+
78
+ ## License
79
+ [standard block]
80
+ ```
81
+
82
+ ## Interfaces
83
+
84
+ CLI, Skill