#!/usr/bin/env node const fs = require('fs'); const os = require('os'); const path = require('path'); const { spawnSync } = require('child_process'); const root = path.resolve(__dirname, '..'); const npmCache = path.join(root, '.npm-cache-acceptance'); function main() { fs.mkdirSync(npmCache, { recursive: true }); run('npm', ['run', 'fmode:image:smoke'], root); run('npm', ['run', 'smoke:package'], root); run('npm', ['run', 'live:normalization:smoke'], root); run('npm', ['run', 'smoke:briefs'], root); run('npm', ['run', 'hard-rules:smoke'], root); run('npm', ['run', 'reference-rules:smoke'], root); run('npm', ['run', 'platform:strategy:smoke'], root); run('npm', ['run', 'feedback:smoke'], root); run('npm', ['run', 'feedback:roundtrip'], root); run('npm', ['run', 'feedback:closure:smoke'], root); run('npm', ['run', 'review:metrics:smoke'], root); run('npm', ['run', 'manual-review:label-guide:smoke'], root); run('npm', ['run', 'skill:refs:smoke'], root); run('npm', ['run', 'customer-effect:smoke'], root); run('npm', ['run', 'software:smoke'], root); run('npm', ['run', 'proof-gap:software-form:smoke'], root); run('npm', ['run', 'plan:consistency:smoke'], root); run('npm', ['run', 'plan:execution-status:smoke'], root); run('npm', ['run', 'hygiene:smoke'], root); run('npm', ['run', 'candidate-pool:smoke'], root); run('npm', ['run', 'history:from-csv:smoke'], root); run('npm', ['run', 'history:audit:smoke'], root); run('npm', ['run', 'data:intake-template:smoke'], root); run('npm', ['run', 'video:intake-template:smoke'], root); run('npm', ['run', 'intake:readiness:smoke'], root); run('npm', ['run', 'intake:field-checklist:smoke'], root); run('npm', ['run', 'video:resource-readiness:smoke'], root); run('npm', ['run', 'longrun:readiness:smoke'], root); run('npm', ['run', 'local-seed-material:index:smoke'], root); run('npm', ['run', 'local-seed-to-intake:worklist:smoke'], root); run('npm', ['run', 'experience-transcript:index:smoke'], root); run('npm', ['run', 'real-gap:materials:smoke'], root); run('npm', ['run', 'optimization:pipeline:smoke'], root); run('npm', ['run', 'optimization:completion:smoke'], root); run('npm', ['run', 'handoff:summary:smoke'], root); run('npm', ['run', 'proof-gap:request:smoke'], root); run('npm', ['run', 'proof-gap:closure:smoke'], root); run('npm', ['run', 'proof-gap:intake-bundle:smoke'], root); run('npm', ['run', 'real-proof:work-order:smoke'], root); run('npm', ['run', 'business-proof:progress:smoke'], root); run('npm', ['run', 'business-proof:next-actions:smoke'], root); run('npm', ['run', 'business-proof:next-actions:missing-proof:smoke'], root); run('npm', ['run', 'business-execution:index:smoke'], root); run('npm', ['run', 'latest-form:index:smoke'], root); run('npm', ['run', 'round:refresh:smoke'], root); run('npm', ['run', 'round:deposition:smoke'], root); run('npm', ['run', 'task:handoff-index:smoke'], root); run('npm', ['run', 'evidence:index:smoke'], root); run('npm', ['run', 'acceptance:video-ab:smoke'], root); run('npm', ['run', 'overnight:review:smoke'], root); run('npm', ['run', 'optimization:status:smoke'], root); run('npm', ['run', 'homepage:smoke'], root); run('npm', ['run', 'homepage:readiness:smoke'], root); run('npm', ['run', 'mcp:smoke'], root); run('npm', ['run', 'acceptance:pm'], root); run('npm', ['run', 'acceptance:providers:mock'], root); run('npm', ['run', 'package:contents:smoke'], root); run('npm', ['pack', '--dry-run'], root); const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-workspace-install-')); const legacyPluginDir = path.join(workspace, '.claude', 'plugins', 'tihao-sourcing'); const legacySkillDir = path.join(workspace, '.claude', 'skills', 'tihao-creator-sourcing'); fs.mkdirSync(legacyPluginDir, { recursive: true }); fs.writeFileSync(path.join(legacyPluginDir, 'legacy.txt'), 'legacy tihao-sourcing plugin\n', 'utf8'); fs.mkdirSync(legacySkillDir, { recursive: true }); fs.writeFileSync(path.join(legacySkillDir, 'SKILL.md'), 'name: tihao-creator-sourcing\n', 'utf8'); fs.writeFileSync(path.join(workspace, '.mcp.json'), JSON.stringify({ mcpServers: { 'tihao-sourcing': { command: 'node', args: [path.join(legacyPluginDir, 'mcp', 'src', 'server.js')] } } }, null, 2), 'utf8'); run(process.execPath, [path.join(root, 'install.js'), '--workspace', '--smoke'], workspace); const mcpFile = path.join(workspace, '.mcp.json'); const skillFile = path.join(workspace, '.claude', 'skills', 'tihao', 'SKILL.md'); const imageSkillFile = path.join(workspace, '.claude', 'skills', 'fmode-image-analysis', 'SKILL.md'); const pluginServer = path.join(workspace, '.claude', 'plugins', 'tihao', 'mcp', 'src', 'server.js'); assert(fs.existsSync(mcpFile), 'workspace install should write .mcp.json'); assert(fs.existsSync(skillFile), 'workspace install should write skill entry'); assert(fs.existsSync(imageSkillFile), 'workspace install should write fmode image analysis skill entry'); assert(!fs.existsSync(legacySkillDir), 'workspace install should remove legacy tihao-creator-sourcing skill entry'); assert(!fs.existsSync(legacyPluginDir), 'workspace install should remove legacy tihao-sourcing plugin entry'); assert(fs.existsSync(pluginServer), 'workspace install should copy plugin server'); const mcp = JSON.parse(fs.readFileSync(mcpFile, 'utf8')); assert(mcp.mcpServers && mcp.mcpServers.tihao, 'workspace .mcp.json should register tihao server'); assert(!mcp.mcpServers['tihao-sourcing'], 'workspace .mcp.json should remove legacy tihao-sourcing server'); assert(path.isAbsolute(mcp.mcpServers.tihao.args[0]), 'workspace MCP server path should be absolute'); console.log(`acceptance ok: ${workspace}`); } function run(command, args, cwd) { const resolved = resolveCommand(command, args); const result = spawnSync(resolved.command, resolved.args, { cwd, stdio: 'inherit', shell: resolved.shell, env: { ...process.env, npm_config_cache: npmCache, NPM_CONFIG_CACHE: npmCache } }); if (result.status !== 0) { throw new Error(`${command} ${args.join(' ')} failed in ${cwd}`); } } function resolveCommand(command, args) { if (command === 'npm' && process.env.npm_execpath) { return { command: process.execPath, args: [process.env.npm_execpath, ...args], shell: false }; } if (command === 'npm' && process.platform === 'win32') { return { command: 'npm.cmd', args, shell: true }; } return { command, args, shell: false }; } function assert(condition, message) { if (!condition) throw new Error(message); } main();