@wipcomputer/wip-release 1.9.40 → 1.9.42
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/cli.js +6 -1
- package/core.mjs +275 -3
- package/mcp-server.mjs +4 -0
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -21,6 +21,8 @@ const noPublish = args.includes('--no-publish');
|
|
|
21
21
|
const skipProductCheck = args.includes('--skip-product-check');
|
|
22
22
|
const skipStaleCheck = args.includes('--skip-stale-check');
|
|
23
23
|
const skipWorktreeCheck = args.includes('--skip-worktree-check');
|
|
24
|
+
const skipTechDocsCheck = args.includes('--skip-tech-docs-check');
|
|
25
|
+
const skipCoverageCheck = args.includes('--skip-coverage-check');
|
|
24
26
|
const notesFilePath = flag('notes-file');
|
|
25
27
|
let notes = flag('notes');
|
|
26
28
|
// Bug fix #121: use strict check, not truthiness. --notes="" is empty, not absent.
|
|
@@ -75,7 +77,8 @@ let notesSource = (notes !== null && notes !== undefined && notes !== '') ? 'fla
|
|
|
75
77
|
const { readdirSync } = await import('node:fs');
|
|
76
78
|
const devUpdatesDir = join(process.cwd(), 'ai', 'dev-updates');
|
|
77
79
|
if (existsSync(devUpdatesDir)) {
|
|
78
|
-
const
|
|
80
|
+
const d = new Date();
|
|
81
|
+
const today = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
|
|
79
82
|
const todayFiles = readdirSync(devUpdatesDir)
|
|
80
83
|
.filter(f => f.startsWith(today) && f.endsWith('.md'))
|
|
81
84
|
.sort()
|
|
@@ -164,6 +167,8 @@ release({
|
|
|
164
167
|
skipProductCheck,
|
|
165
168
|
skipStaleCheck,
|
|
166
169
|
skipWorktreeCheck,
|
|
170
|
+
skipTechDocsCheck,
|
|
171
|
+
skipCoverageCheck,
|
|
167
172
|
}).catch(err => {
|
|
168
173
|
console.error(` ✗ ${err.message}`);
|
|
169
174
|
process.exit(1);
|
package/core.mjs
CHANGED
|
@@ -89,7 +89,8 @@ export function syncSkillVersion(repoPath, newVersion) {
|
|
|
89
89
|
*/
|
|
90
90
|
export function updateChangelog(repoPath, newVersion, notes) {
|
|
91
91
|
const changelogPath = join(repoPath, 'CHANGELOG.md');
|
|
92
|
-
const
|
|
92
|
+
const d = new Date();
|
|
93
|
+
const date = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
|
|
93
94
|
|
|
94
95
|
// Bug fix #121: never silently default to "Release." when notes are empty.
|
|
95
96
|
// If notes are empty at this point, warn loudly.
|
|
@@ -383,6 +384,194 @@ function checkProductDocs(repoPath) {
|
|
|
383
384
|
return { missing, ok: missing.length === 0, skipped: false };
|
|
384
385
|
}
|
|
385
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Check that technical docs (SKILL.md, TECHNICAL.md) were updated
|
|
389
|
+
* when source code changed since last release tag.
|
|
390
|
+
* Returns { missing: string[], ok: boolean, skipped: boolean }.
|
|
391
|
+
*/
|
|
392
|
+
function checkTechnicalDocs(repoPath) {
|
|
393
|
+
try {
|
|
394
|
+
let lastTag;
|
|
395
|
+
try {
|
|
396
|
+
lastTag = execFileSync('git', ['describe', '--tags', '--abbrev=0'],
|
|
397
|
+
{ cwd: repoPath, encoding: 'utf8' }).trim();
|
|
398
|
+
} catch {
|
|
399
|
+
return { missing: [], ok: true, skipped: true }; // No tags yet
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const diff = execFileSync('git', ['diff', '--name-only', lastTag, 'HEAD'],
|
|
403
|
+
{ cwd: repoPath, encoding: 'utf8' });
|
|
404
|
+
const changedFiles = diff.split('\n').map(f => f.trim()).filter(Boolean);
|
|
405
|
+
|
|
406
|
+
// Find source code changes (*.mjs, *.js, *.ts) excluding non-source dirs
|
|
407
|
+
const excludePattern = /\/(node_modules|dist|_trash|examples)\//;
|
|
408
|
+
const sourcePattern = /\.(mjs|js|ts)$/;
|
|
409
|
+
const sourceChanges = changedFiles.filter(f =>
|
|
410
|
+
sourcePattern.test(f) && !excludePattern.test(f) && !f.startsWith('ai/')
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
if (sourceChanges.length === 0) {
|
|
414
|
+
return { missing: [], ok: true, skipped: false }; // No source changes
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Check if any doc files were also modified
|
|
418
|
+
const docChanges = changedFiles.filter(f =>
|
|
419
|
+
f === 'SKILL.md' || f === 'TECHNICAL.md' ||
|
|
420
|
+
/^tools\/[^/]+\/SKILL\.md$/.test(f) ||
|
|
421
|
+
/^tools\/[^/]+\/TECHNICAL\.md$/.test(f)
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
if (docChanges.length > 0) {
|
|
425
|
+
return { missing: [], ok: true, skipped: false }; // Docs updated
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Source changed but no doc updates
|
|
429
|
+
const missing = [];
|
|
430
|
+
const preview = sourceChanges.slice(0, 5).join(', ');
|
|
431
|
+
const more = sourceChanges.length > 5 ? ` (and ${sourceChanges.length - 5} more)` : '';
|
|
432
|
+
missing.push('Source files changed since last tag but no SKILL.md or TECHNICAL.md was updated');
|
|
433
|
+
missing.push(`Changed: ${preview}${more}`);
|
|
434
|
+
missing.push('Update SKILL.md or TECHNICAL.md to document these changes');
|
|
435
|
+
|
|
436
|
+
return { missing, ok: false, skipped: false };
|
|
437
|
+
} catch {
|
|
438
|
+
return { missing: [], ok: true, skipped: true }; // Graceful fallback
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Parse the interface coverage table from a markdown file.
|
|
444
|
+
* Returns array of { name, cli, module, mcp, openclaw, skill, ccHook } or null.
|
|
445
|
+
*/
|
|
446
|
+
function parseInterfaceCoverageTable(filePath) {
|
|
447
|
+
if (!existsSync(filePath)) return null;
|
|
448
|
+
const content = readFileSync(filePath, 'utf8');
|
|
449
|
+
const lines = content.split('\n');
|
|
450
|
+
|
|
451
|
+
const headerIdx = lines.findIndex(l => /^\|\s*#\s*\|\s*Tool\s*\|/i.test(l));
|
|
452
|
+
if (headerIdx === -1) return null;
|
|
453
|
+
|
|
454
|
+
const rows = [];
|
|
455
|
+
for (let i = headerIdx + 2; i < lines.length; i++) {
|
|
456
|
+
const line = lines[i].trim();
|
|
457
|
+
if (!line.startsWith('|')) break;
|
|
458
|
+
const cells = line.split('|').map(c => c.trim()).filter(c => c !== '');
|
|
459
|
+
if (cells.length < 8) continue;
|
|
460
|
+
// Skip category header rows (# cell is empty, non-numeric, or bold)
|
|
461
|
+
const num = cells[0].trim();
|
|
462
|
+
if (!num || /^\*\*/.test(num) || isNaN(parseInt(num))) continue;
|
|
463
|
+
rows.push({
|
|
464
|
+
name: cells[1].trim(),
|
|
465
|
+
cli: /^Y$/i.test(cells[2]),
|
|
466
|
+
module: /^Y$/i.test(cells[3]),
|
|
467
|
+
mcp: /^Y$/i.test(cells[4]),
|
|
468
|
+
openclaw: /^Y$/i.test(cells[5]),
|
|
469
|
+
skill: /^Y$/i.test(cells[6]),
|
|
470
|
+
ccHook: /^Y$/i.test(cells[7]),
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
return rows.length > 0 ? rows : null;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Read display name from a tool's SKILL.md frontmatter.
|
|
478
|
+
* Tries display-name, then name field. Falls back to null.
|
|
479
|
+
*/
|
|
480
|
+
function getToolDisplayName(toolPath) {
|
|
481
|
+
const skillPath = join(toolPath, 'SKILL.md');
|
|
482
|
+
if (!existsSync(skillPath)) return null;
|
|
483
|
+
try {
|
|
484
|
+
const content = readFileSync(skillPath, 'utf8');
|
|
485
|
+
const displayMatch = content.match(/^\s*display-name:\s*"?([^"\n]+)"?/m);
|
|
486
|
+
if (displayMatch) return displayMatch[1].trim();
|
|
487
|
+
const nameMatch = content.match(/^name:\s*"?([^"\n]+)"?/m);
|
|
488
|
+
if (nameMatch) return nameMatch[1].trim();
|
|
489
|
+
} catch {}
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Check that the interface coverage table in README.md and SKILL.md
|
|
495
|
+
* matches the actual interfaces detected in tools/*/.
|
|
496
|
+
* Returns { missing: string[], ok: boolean, skipped: boolean }.
|
|
497
|
+
*/
|
|
498
|
+
function checkInterfaceCoverage(repoPath) {
|
|
499
|
+
try {
|
|
500
|
+
// Only applies to toolbox repos
|
|
501
|
+
const toolsDir = join(repoPath, 'tools');
|
|
502
|
+
if (!existsSync(toolsDir)) return { missing: [], ok: true, skipped: true };
|
|
503
|
+
|
|
504
|
+
const entries = readdirSync(toolsDir, { withFileTypes: true });
|
|
505
|
+
const tools = entries
|
|
506
|
+
.filter(e => e.isDirectory() && existsSync(join(toolsDir, e.name, 'package.json')))
|
|
507
|
+
.map(e => ({ name: e.name, path: join(toolsDir, e.name) }));
|
|
508
|
+
|
|
509
|
+
if (tools.length === 0) return { missing: [], ok: true, skipped: true };
|
|
510
|
+
|
|
511
|
+
// Detect actual interfaces for each tool
|
|
512
|
+
const actualMap = {};
|
|
513
|
+
for (const tool of tools) {
|
|
514
|
+
const pkg = JSON.parse(readFileSync(join(tool.path, 'package.json'), 'utf8'));
|
|
515
|
+
actualMap[tool.name] = {
|
|
516
|
+
displayName: getToolDisplayName(tool.path) || tool.name,
|
|
517
|
+
cli: !!(pkg.bin),
|
|
518
|
+
module: !!(pkg.main || pkg.exports),
|
|
519
|
+
mcp: ['mcp-server.mjs', 'mcp-server.js', 'dist/mcp-server.js'].some(f => existsSync(join(tool.path, f))),
|
|
520
|
+
openclaw: existsSync(join(tool.path, 'openclaw.plugin.json')),
|
|
521
|
+
skill: existsSync(join(tool.path, 'SKILL.md')),
|
|
522
|
+
ccHook: !!(pkg.claudeCode?.hook) || existsSync(join(tool.path, 'guard.mjs')),
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const missing = [];
|
|
527
|
+
|
|
528
|
+
// Check both README.md and SKILL.md tables
|
|
529
|
+
for (const [label, filePath] of [['README.md', join(repoPath, 'README.md')], ['SKILL.md', join(repoPath, 'SKILL.md')]]) {
|
|
530
|
+
const tableRows = parseInterfaceCoverageTable(filePath);
|
|
531
|
+
if (!tableRows) continue;
|
|
532
|
+
|
|
533
|
+
// Tool count
|
|
534
|
+
if (tools.length !== tableRows.length) {
|
|
535
|
+
missing.push(`${label}: tool count mismatch (${tools.length} in tools/, ${tableRows.length} in table)`);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Check each actual tool against the table
|
|
539
|
+
for (const tool of tools) {
|
|
540
|
+
const actual = actualMap[tool.name];
|
|
541
|
+
const displayName = actual.displayName;
|
|
542
|
+
const tableRow = tableRows.find(r =>
|
|
543
|
+
r.name === displayName ||
|
|
544
|
+
r.name.toLowerCase() === displayName.toLowerCase() ||
|
|
545
|
+
r.name.toLowerCase().includes(tool.name.replace(/^wip-/, '').replace(/-/g, ' '))
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
if (!tableRow) {
|
|
549
|
+
missing.push(`${label}: ${tool.name} (${displayName}) missing from coverage table`);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const ifaceMap = [
|
|
554
|
+
['cli', 'CLI'], ['module', 'Module'], ['mcp', 'MCP'],
|
|
555
|
+
['openclaw', 'OC Plugin'], ['skill', 'Skill'], ['ccHook', 'CC Hook']
|
|
556
|
+
];
|
|
557
|
+
|
|
558
|
+
for (const [key, name] of ifaceMap) {
|
|
559
|
+
if (actual[key] && !tableRow[key]) {
|
|
560
|
+
missing.push(`${label}: ${displayName} has ${name} but table says no`);
|
|
561
|
+
}
|
|
562
|
+
if (tableRow[key] && !actual[key]) {
|
|
563
|
+
missing.push(`${label}: ${displayName} marked ${name} in table but not detected`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return { missing, ok: missing.length === 0, skipped: false };
|
|
570
|
+
} catch {
|
|
571
|
+
return { missing: [], ok: true, skipped: true }; // Graceful fallback
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
386
575
|
/**
|
|
387
576
|
* Auto-update version/date lines in product docs before the release commit.
|
|
388
577
|
* Updates roadmap.md "Current version" and "Last updated",
|
|
@@ -391,7 +580,8 @@ function checkProductDocs(repoPath) {
|
|
|
391
580
|
*/
|
|
392
581
|
function syncProductDocs(repoPath, newVersion) {
|
|
393
582
|
let updated = 0;
|
|
394
|
-
const
|
|
583
|
+
const td = new Date();
|
|
584
|
+
const today = `${td.getFullYear()}-${String(td.getMonth()+1).padStart(2,'0')}-${String(td.getDate()).padStart(2,'0')}`;
|
|
395
585
|
|
|
396
586
|
// 1. roadmap.md
|
|
397
587
|
const roadmapPath = join(repoPath, 'ai', 'product', 'plans-prds', 'roadmap.md');
|
|
@@ -818,7 +1008,7 @@ export function checkStaleBranches(repoPath, level) {
|
|
|
818
1008
|
/**
|
|
819
1009
|
* Run the full release pipeline.
|
|
820
1010
|
*/
|
|
821
|
-
export async function release({ repoPath, level, notes, notesSource, dryRun, noPublish, skipProductCheck, skipStaleCheck, skipWorktreeCheck }) {
|
|
1011
|
+
export async function release({ repoPath, level, notes, notesSource, dryRun, noPublish, skipProductCheck, skipStaleCheck, skipWorktreeCheck, skipTechDocsCheck, skipCoverageCheck }) {
|
|
822
1012
|
repoPath = repoPath || process.cwd();
|
|
823
1013
|
const currentVersion = detectCurrentVersion(repoPath);
|
|
824
1014
|
const newVersion = bumpSemver(currentVersion, level);
|
|
@@ -962,6 +1152,50 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
962
1152
|
}
|
|
963
1153
|
}
|
|
964
1154
|
|
|
1155
|
+
// 0.85. Technical docs check
|
|
1156
|
+
if (!skipTechDocsCheck) {
|
|
1157
|
+
const techDocsCheck = checkTechnicalDocs(repoPath);
|
|
1158
|
+
if (!techDocsCheck.skipped) {
|
|
1159
|
+
if (techDocsCheck.ok) {
|
|
1160
|
+
console.log(' ✓ Technical docs up to date');
|
|
1161
|
+
} else {
|
|
1162
|
+
const isMinorOrMajor = level === 'minor' || level === 'major';
|
|
1163
|
+
const prefix = isMinorOrMajor ? '✗' : '!';
|
|
1164
|
+
console.log(` ${prefix} Technical docs need attention:`);
|
|
1165
|
+
for (const m of techDocsCheck.missing) console.log(` - ${m}`);
|
|
1166
|
+
if (isMinorOrMajor) {
|
|
1167
|
+
console.log('');
|
|
1168
|
+
console.log(' Update SKILL.md or TECHNICAL.md before a minor/major release.');
|
|
1169
|
+
console.log(' Use --skip-tech-docs-check to override.');
|
|
1170
|
+
console.log('');
|
|
1171
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// 0.9. Interface coverage check
|
|
1178
|
+
if (!skipCoverageCheck) {
|
|
1179
|
+
const coverageCheck = checkInterfaceCoverage(repoPath);
|
|
1180
|
+
if (!coverageCheck.skipped) {
|
|
1181
|
+
if (coverageCheck.ok) {
|
|
1182
|
+
console.log(' ✓ Interface coverage table matches');
|
|
1183
|
+
} else {
|
|
1184
|
+
const isMinorOrMajor = level === 'minor' || level === 'major';
|
|
1185
|
+
const prefix = isMinorOrMajor ? '✗' : '!';
|
|
1186
|
+
console.log(` ${prefix} Interface coverage table has mismatches:`);
|
|
1187
|
+
for (const m of coverageCheck.missing) console.log(` - ${m}`);
|
|
1188
|
+
if (isMinorOrMajor) {
|
|
1189
|
+
console.log('');
|
|
1190
|
+
console.log(' Update the coverage table in README.md and SKILL.md.');
|
|
1191
|
+
console.log(' Use --skip-coverage-check to override.');
|
|
1192
|
+
console.log('');
|
|
1193
|
+
return { currentVersion, newVersion, dryRun: false, failed: true };
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
965
1199
|
if (dryRun) {
|
|
966
1200
|
// Product docs check (dry-run)
|
|
967
1201
|
if (!skipProductCheck) {
|
|
@@ -999,6 +1233,32 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
999
1233
|
console.log(' [dry run] ✓ No stale remote branches');
|
|
1000
1234
|
}
|
|
1001
1235
|
}
|
|
1236
|
+
// Technical docs check (dry-run)
|
|
1237
|
+
if (!skipTechDocsCheck) {
|
|
1238
|
+
const techDocsCheck = checkTechnicalDocs(repoPath);
|
|
1239
|
+
if (!techDocsCheck.skipped) {
|
|
1240
|
+
if (techDocsCheck.ok) {
|
|
1241
|
+
console.log(' [dry run] ✓ Technical docs up to date');
|
|
1242
|
+
} else {
|
|
1243
|
+
const isMinorOrMajor = level === 'minor' || level === 'major';
|
|
1244
|
+
console.log(` [dry run] ${isMinorOrMajor ? '✗ Would BLOCK' : '! Would WARN'}: technical docs need updates`);
|
|
1245
|
+
for (const m of techDocsCheck.missing) console.log(` - ${m}`);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
// Interface coverage check (dry-run)
|
|
1250
|
+
if (!skipCoverageCheck) {
|
|
1251
|
+
const coverageCheck = checkInterfaceCoverage(repoPath);
|
|
1252
|
+
if (!coverageCheck.skipped) {
|
|
1253
|
+
if (coverageCheck.ok) {
|
|
1254
|
+
console.log(' [dry run] ✓ Interface coverage table matches');
|
|
1255
|
+
} else {
|
|
1256
|
+
const isMinorOrMajor = level === 'minor' || level === 'major';
|
|
1257
|
+
console.log(` [dry run] ${isMinorOrMajor ? '✗ Would BLOCK' : '! Would WARN'}: interface coverage mismatches`);
|
|
1258
|
+
for (const m of coverageCheck.missing) console.log(` - ${m}`);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1002
1262
|
const hasSkill = existsSync(join(repoPath, 'SKILL.md'));
|
|
1003
1263
|
console.log(` [dry run] Would bump package.json to ${newVersion}`);
|
|
1004
1264
|
if (hasSkill) console.log(` [dry run] Would update SKILL.md version`);
|
|
@@ -1312,6 +1572,18 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
1312
1572
|
console.log(` ! Branch prune skipped: ${e.message}`);
|
|
1313
1573
|
}
|
|
1314
1574
|
|
|
1575
|
+
// Write release marker so branch guard blocks immediate install (#73)
|
|
1576
|
+
try {
|
|
1577
|
+
const markerDir = join(process.env.HOME || '', '.ldm', 'state');
|
|
1578
|
+
const { mkdirSync, writeFileSync } = await import('node:fs');
|
|
1579
|
+
mkdirSync(markerDir, { recursive: true });
|
|
1580
|
+
writeFileSync(join(markerDir, '.last-release'), JSON.stringify({
|
|
1581
|
+
repo: repoName,
|
|
1582
|
+
version: newVersion,
|
|
1583
|
+
timestamp: new Date().toISOString(),
|
|
1584
|
+
}) + '\n');
|
|
1585
|
+
} catch {}
|
|
1586
|
+
|
|
1315
1587
|
console.log('');
|
|
1316
1588
|
console.log(` Done. ${repoName} v${newVersion} released.`);
|
|
1317
1589
|
console.log('');
|
package/mcp-server.mjs
CHANGED
|
@@ -31,6 +31,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
31
31
|
dryRun: { type: 'boolean', description: 'Preview only, no changes', default: false },
|
|
32
32
|
noPublish: { type: 'boolean', description: 'Bump + tag only, skip npm/GitHub publish', default: false },
|
|
33
33
|
skipProductCheck: { type: 'boolean', description: 'Skip product doc freshness check', default: false },
|
|
34
|
+
skipTechDocsCheck: { type: 'boolean', description: 'Skip technical docs freshness check', default: false },
|
|
35
|
+
skipCoverageCheck: { type: 'boolean', description: 'Skip interface coverage table check', default: false },
|
|
34
36
|
},
|
|
35
37
|
required: ['level', 'notes'],
|
|
36
38
|
},
|
|
@@ -65,6 +67,8 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
65
67
|
notesSource: 'flag', // MCP always passes notes directly
|
|
66
68
|
noPublish: args.noPublish || false,
|
|
67
69
|
skipProductCheck: args.skipProductCheck || false,
|
|
70
|
+
skipTechDocsCheck: args.skipTechDocsCheck || false,
|
|
71
|
+
skipCoverageCheck: args.skipCoverageCheck || false,
|
|
68
72
|
});
|
|
69
73
|
return {
|
|
70
74
|
content: [{
|
package/package.json
CHANGED