| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- #!/usr/bin/env node
- import fs from 'node:fs';
- import path from 'node:path';
- import { spawnSync } from 'node:child_process';
- import { fileURLToPath } from 'node:url';
- const packageRoot = path.dirname(fileURLToPath(import.meta.url));
- const required = [
- '.claude-plugin/plugin.json', '.mcp.json', 'README.md', 'package.json', 'package-lock.json',
- 'skill-package-manifest.json', 'mcp/src/server.mjs', 'scripts/smoke-package.mjs',
- 'scripts/smoke-mcp.mjs', 'docs/capability-map.md', 'docs/customer-quickstart.md',
- 'docs/live-manual-acceptance-checklist.md'
- ];
- function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')); }
- export function checkPackage(root = packageRoot) {
- const errors = [];
- const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
- if (nodeMajor < 20 || (nodeMajor === 20 && nodeMinor < 11)) errors.push('node_version_requires_20.11');
- for (const item of required) if (!fs.existsSync(path.join(root, item))) errors.push(`missing:${item}`);
- if (errors.length) return { ok: false, errors };
- const pkg = readJson(path.join(root, 'package.json'));
- const plugin = readJson(path.join(root, '.claude-plugin', 'plugin.json'));
- const manifest = readJson(path.join(root, 'skill-package-manifest.json'));
- if (pkg.version !== plugin.version || pkg.version !== manifest.version) errors.push('version_mismatch');
- const skills = fs.readdirSync(path.join(root, 'skills'), { withFileTypes: true }).filter(item => item.isDirectory()).map(item => item.name).sort();
- if (JSON.stringify(skills) !== JSON.stringify([...manifest.skills].sort())) errors.push('skill_manifest_mismatch');
- for (const skill of manifest.skills) if (!fs.existsSync(path.join(root, 'skills', skill, 'SKILL.md'))) errors.push(`missing_skill:${skill}`);
- const serverText = fs.readFileSync(path.join(root, 'mcp', 'src', 'server.mjs'), 'utf8');
- for (const tool of manifest.mcpTools) if (!serverText.includes(`'${tool}'`)) errors.push(`missing_tool:${tool}`);
- return { ok: errors.length === 0, errors, version: pkg.version, skills: manifest.skills.length, tools: manifest.mcpTools.length };
- }
- function run(command, args, cwd = packageRoot) {
- const result = spawnSync(command, args, { cwd, stdio: 'inherit', shell: process.platform === 'win32' });
- if (result.status !== 0) process.exit(result.status || 1);
- }
- function help() {
- console.log('Usage: node install.js [--check] [--smoke] [--skip-install]');
- }
- async function main() {
- const args = new Set(process.argv.slice(2));
- if (args.has('--help')) return help();
- const before = checkPackage();
- if (!before.ok && !before.errors.every(item => item === 'missing:package-lock.json')) {
- console.error(JSON.stringify(before, null, 2));
- process.exit(1);
- }
- if (!args.has('--skip-install') && !fs.existsSync(path.join(packageRoot, 'node_modules'))) run('npm', ['ci']);
- const result = checkPackage();
- console.log(JSON.stringify(result, null, 2));
- if (!result.ok) process.exit(1);
- if (args.has('--smoke')) {
- run(process.execPath, ['scripts/smoke-package.mjs']);
- run(process.execPath, ['scripts/smoke-mcp.mjs']);
- }
- }
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();
|