@wipcomputer/wip-release 1.9.11 → 1.9.13
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 +8 -1
- package/core.mjs +146 -10
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -125,6 +125,12 @@ Release notes (highest priority wins, files ALWAYS beat --notes flag):
|
|
|
125
125
|
4. --notes="text" Fallback only (use for repos without release notes files)
|
|
126
126
|
Written notes on disk always take priority over a CLI one-liner.
|
|
127
127
|
|
|
128
|
+
Skill publish to website:
|
|
129
|
+
Add .publish-skill.json to repo root: { "name": "my-tool" }
|
|
130
|
+
Set WIP_WEBSITE_REPO env var to your website repo path.
|
|
131
|
+
After release, SKILL.md is copied to {website}/wip.computer/install/{name}.txt
|
|
132
|
+
and deploy.sh is run to push to VPS.
|
|
133
|
+
|
|
128
134
|
Pipeline:
|
|
129
135
|
1. Bump package.json version
|
|
130
136
|
2. Sync SKILL.md version (if exists)
|
|
@@ -133,7 +139,8 @@ Pipeline:
|
|
|
133
139
|
5. Push to remote
|
|
134
140
|
6. npm publish (via 1Password)
|
|
135
141
|
7. GitHub Packages publish
|
|
136
|
-
8. GitHub release create
|
|
142
|
+
8. GitHub release create
|
|
143
|
+
9. Publish SKILL.md to website (if configured)`);
|
|
137
144
|
process.exit(level ? 0 : 1);
|
|
138
145
|
}
|
|
139
146
|
|
package/core.mjs
CHANGED
|
@@ -273,19 +273,23 @@ function checkProductDocs(repoPath) {
|
|
|
273
273
|
const aiDir = join(repoPath, 'ai');
|
|
274
274
|
if (!existsSync(aiDir)) return { missing: [], ok: true, skipped: true };
|
|
275
275
|
|
|
276
|
-
// 1. Dev update: file
|
|
276
|
+
// 1. Dev update: must have a file modified since last release tag.
|
|
277
|
+
// Old check ("any file from last 3 days") let the same stale file pass
|
|
278
|
+
// across 11 releases in one session. Now uses the same git-based check
|
|
279
|
+
// as roadmap and readme-first: was the file actually changed since the tag?
|
|
277
280
|
const devUpdatesDir = join(aiDir, 'dev-updates');
|
|
278
281
|
if (existsSync(devUpdatesDir)) {
|
|
279
|
-
const now = new Date();
|
|
280
|
-
const recentDates = [];
|
|
281
|
-
for (let i = 0; i < 3; i++) {
|
|
282
|
-
const d = new Date(now);
|
|
283
|
-
d.setDate(d.getDate() - i);
|
|
284
|
-
recentDates.push(d.toISOString().split('T')[0]);
|
|
285
|
-
}
|
|
286
282
|
const files = readdirSync(devUpdatesDir).filter(f => f.endsWith('.md'));
|
|
287
|
-
|
|
288
|
-
|
|
283
|
+
if (files.length === 0) {
|
|
284
|
+
missing.push('ai/dev-updates/ (no dev update files)');
|
|
285
|
+
} else {
|
|
286
|
+
const anyModified = files.some(f =>
|
|
287
|
+
fileModifiedSinceLastTag(repoPath, `ai/dev-updates/${f}`)
|
|
288
|
+
);
|
|
289
|
+
if (!anyModified) {
|
|
290
|
+
missing.push('ai/dev-updates/ (no dev update modified since last release)');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
289
293
|
}
|
|
290
294
|
|
|
291
295
|
// 2. Roadmap: modified since last tag
|
|
@@ -458,6 +462,100 @@ export function publishClawHub(repoPath, newVersion, notes) {
|
|
|
458
462
|
return true;
|
|
459
463
|
}
|
|
460
464
|
|
|
465
|
+
// ── Skill Publish ────────────────────────────────────────────────────
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Publish SKILL.md to website as plain text.
|
|
469
|
+
*
|
|
470
|
+
* Auto-detects: if SKILL.md exists and WIP_WEBSITE_REPO is set,
|
|
471
|
+
* publishes automatically. No config file needed.
|
|
472
|
+
*
|
|
473
|
+
* Name resolution (first match wins):
|
|
474
|
+
* 1. .publish-skill.json { "name": "memory-crystal" }
|
|
475
|
+
* 2. SKILL.md frontmatter name: field
|
|
476
|
+
* 3. Directory name (basename of repoPath)
|
|
477
|
+
*
|
|
478
|
+
* Copies SKILL.md to {website}/wip.computer/install/{name}.txt
|
|
479
|
+
* Then runs deploy.sh to push to VPS.
|
|
480
|
+
*
|
|
481
|
+
* Non-blocking: returns result, never throws.
|
|
482
|
+
*/
|
|
483
|
+
export function publishSkillToWebsite(repoPath) {
|
|
484
|
+
const websiteRepo = process.env.WIP_WEBSITE_REPO;
|
|
485
|
+
if (!websiteRepo) return { skipped: true, reason: 'WIP_WEBSITE_REPO not set' };
|
|
486
|
+
|
|
487
|
+
// Find SKILL.md: check root, then skills/*/SKILL.md
|
|
488
|
+
let skillFile = join(repoPath, 'SKILL.md');
|
|
489
|
+
if (!existsSync(skillFile)) {
|
|
490
|
+
const skillsDir = join(repoPath, 'skills');
|
|
491
|
+
if (existsSync(skillsDir)) {
|
|
492
|
+
for (const sub of readdirSync(skillsDir)) {
|
|
493
|
+
const candidate = join(skillsDir, sub, 'SKILL.md');
|
|
494
|
+
if (existsSync(candidate)) { skillFile = candidate; break; }
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (!existsSync(skillFile)) return { skipped: true, reason: 'no SKILL.md found' };
|
|
499
|
+
|
|
500
|
+
// Resolve target name: config > package.json > directory name
|
|
501
|
+
// SKILL.md frontmatter name is skipped because it's a short slug
|
|
502
|
+
// (e.g., "memory") not the full install name (e.g., "memory-crystal").
|
|
503
|
+
let targetName;
|
|
504
|
+
|
|
505
|
+
// 1. Explicit config (optional, overrides auto-detect)
|
|
506
|
+
const configPath = join(repoPath, '.publish-skill.json');
|
|
507
|
+
if (existsSync(configPath)) {
|
|
508
|
+
try {
|
|
509
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
510
|
+
if (config.name) targetName = config.name;
|
|
511
|
+
} catch {}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// 2. package.json name (strip @scope/ prefix, most reliable)
|
|
515
|
+
if (!targetName) {
|
|
516
|
+
const pkgPath = join(repoPath, 'package.json');
|
|
517
|
+
if (existsSync(pkgPath)) {
|
|
518
|
+
try {
|
|
519
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
520
|
+
if (pkg.name) targetName = pkg.name.replace(/^@[^/]+\//, '');
|
|
521
|
+
} catch {}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// 3. Directory name fallback (strip -private suffix)
|
|
526
|
+
if (!targetName) {
|
|
527
|
+
targetName = basename(repoPath).replace(/-private$/, '').toLowerCase();
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Copy to website install dir
|
|
531
|
+
const installDir = join(websiteRepo, 'wip.computer', 'install');
|
|
532
|
+
if (!existsSync(installDir)) {
|
|
533
|
+
try { mkdirSync(installDir, { recursive: true }); } catch {}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const targetFile = join(installDir, `${targetName}.txt`);
|
|
537
|
+
try {
|
|
538
|
+
const content = readFileSync(skillFile, 'utf8');
|
|
539
|
+
writeFileSync(targetFile, content);
|
|
540
|
+
} catch (e) {
|
|
541
|
+
return { ok: false, error: `copy failed: ${e.message}` };
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Deploy to VPS (non-blocking ... warn on failure)
|
|
545
|
+
const deployScript = join(websiteRepo, 'deploy.sh');
|
|
546
|
+
if (existsSync(deployScript)) {
|
|
547
|
+
try {
|
|
548
|
+
execSync(`bash deploy.sh`, { cwd: websiteRepo, stdio: 'pipe', timeout: 30000 });
|
|
549
|
+
} catch (e) {
|
|
550
|
+
return { ok: true, deployed: false, target: targetName, error: `deploy failed: ${e.message}` };
|
|
551
|
+
}
|
|
552
|
+
} else {
|
|
553
|
+
return { ok: true, deployed: false, target: targetName, error: 'no deploy.sh found' };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return { ok: true, deployed: true, target: targetName };
|
|
557
|
+
}
|
|
558
|
+
|
|
461
559
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
462
560
|
|
|
463
561
|
function getNpmToken() {
|
|
@@ -745,6 +843,28 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
745
843
|
console.log(` [dry run] Would publish to GitHub Packages`);
|
|
746
844
|
console.log(` [dry run] Would create GitHub release v${newVersion}`);
|
|
747
845
|
if (hasSkill) console.log(` [dry run] Would publish to ClawHub`);
|
|
846
|
+
// Skill-to-website dry run (auto-detects SKILL.md, no config needed)
|
|
847
|
+
if (hasSkill) {
|
|
848
|
+
const envSet = !!process.env.WIP_WEBSITE_REPO;
|
|
849
|
+
if (envSet) {
|
|
850
|
+
// Resolve name same way as publishSkillToWebsite
|
|
851
|
+
let dryName;
|
|
852
|
+
const publishConfig = join(repoPath, '.publish-skill.json');
|
|
853
|
+
if (existsSync(publishConfig)) {
|
|
854
|
+
try { dryName = JSON.parse(readFileSync(publishConfig, 'utf8')).name; } catch {}
|
|
855
|
+
}
|
|
856
|
+
if (!dryName) {
|
|
857
|
+
const pkgPath = join(repoPath, 'package.json');
|
|
858
|
+
if (existsSync(pkgPath)) {
|
|
859
|
+
try { dryName = JSON.parse(readFileSync(pkgPath, 'utf8')).name?.replace(/^@[^/]+\//, ''); } catch {}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (!dryName) dryName = basename(repoPath).replace(/-private$/, '').toLowerCase();
|
|
863
|
+
console.log(` [dry run] Would publish SKILL.md to website: install/${dryName}.txt`);
|
|
864
|
+
} else {
|
|
865
|
+
console.log(` [dry run] Would publish SKILL.md to website but WIP_WEBSITE_REPO not set`);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
748
868
|
}
|
|
749
869
|
console.log('');
|
|
750
870
|
console.log(` Dry run complete. No changes made.`);
|
|
@@ -878,6 +998,22 @@ export async function release({ repoPath, level, notes, notesSource, dryRun, noP
|
|
|
878
998
|
}
|
|
879
999
|
}
|
|
880
1000
|
}
|
|
1001
|
+
|
|
1002
|
+
// 9.5. Publish SKILL.md to website as plain text
|
|
1003
|
+
const skillWebResult = publishSkillToWebsite(repoPath);
|
|
1004
|
+
if (skillWebResult.skipped) {
|
|
1005
|
+
// Silent skip ... no config or env var
|
|
1006
|
+
} else if (skillWebResult.ok) {
|
|
1007
|
+
const deployNote = skillWebResult.deployed ? '' : ' (copied, deploy skipped)';
|
|
1008
|
+
distResults.push({ target: 'Website', status: 'ok', detail: `install/${skillWebResult.target}.txt${deployNote}` });
|
|
1009
|
+
console.log(` ✓ Published to website: install/${skillWebResult.target}.txt${deployNote}`);
|
|
1010
|
+
if (!skillWebResult.deployed && skillWebResult.error) {
|
|
1011
|
+
console.log(` ! ${skillWebResult.error}`);
|
|
1012
|
+
}
|
|
1013
|
+
} else {
|
|
1014
|
+
distResults.push({ target: 'Website', status: 'failed', detail: skillWebResult.error });
|
|
1015
|
+
console.log(` ✗ Website publish failed: ${skillWebResult.error}`);
|
|
1016
|
+
}
|
|
881
1017
|
}
|
|
882
1018
|
|
|
883
1019
|
// Distribution summary (#104)
|
package/package.json
CHANGED