acceptance.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #!/usr/bin/env node
  2. const fs = require('fs');
  3. const os = require('os');
  4. const path = require('path');
  5. const { spawnSync } = require('child_process');
  6. const ROOT = path.resolve(__dirname, '..');
  7. const npmCache = path.join(os.tmpdir(), 'claude-voc-npm-cache-acceptance');
  8. function main() {
  9. fs.mkdirSync(npmCache, { recursive: true });
  10. run('npm', ['run', 'audit:index'], ROOT);
  11. run('npm', ['run', 'fmode:image:smoke'], ROOT);
  12. run('npm', ['run', 'smoke:budget'], ROOT);
  13. run('npm', ['run', 'smoke:trend-budget'], ROOT);
  14. run('npm', ['run', 'smoke:package'], ROOT);
  15. run('npm', ['run', 'mcp:smoke'], ROOT);
  16. run('npm', ['run', 'acceptance:installer'], ROOT);
  17. run('npm', ['pack', '--dry-run'], ROOT);
  18. const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'voc-workspace-install-'));
  19. run(process.execPath, [path.join(ROOT, 'bin', 'claude-voc.js'), 'workspace', '--smoke'], workspace);
  20. const mcpFile = path.join(workspace, '.mcp.json');
  21. const pluginRoot = path.join(workspace, '.claude', 'plugins', 'voc-intelligence');
  22. const pluginServer = path.join(pluginRoot, 'mcp', 'src', 'server.js');
  23. const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'skill-package-manifest.json'), 'utf8'));
  24. const skillNames = Array.isArray(manifest.skills) ? manifest.skills : [];
  25. assert(skillNames.length > 0, 'manifest should list at least one skill');
  26. assert(fs.existsSync(mcpFile), 'workspace install should write .mcp.json');
  27. assert(fs.existsSync(pluginServer), 'workspace install should copy plugin server');
  28. for (const skillName of skillNames) {
  29. assert(
  30. fs.existsSync(path.join(workspace, '.claude', 'skills', skillName, 'SKILL.md')),
  31. `workspace install should write skill entry: ${skillName}`
  32. );
  33. }
  34. const mcp = JSON.parse(fs.readFileSync(mcpFile, 'utf8'));
  35. const server = mcp.mcpServers && mcp.mcpServers.voc;
  36. assert(server, 'workspace .mcp.json should register voc server');
  37. assert(Array.isArray(server.args) && path.isAbsolute(server.args[0]), 'workspace MCP server path should be absolute');
  38. assert(path.resolve(server.args[0]) === path.resolve(pluginServer), 'workspace MCP server path should point to installed plugin server');
  39. console.log(`acceptance ok: ${workspace}`);
  40. }
  41. function run(command, args, cwd) {
  42. const resolved = resolveCommand(command, args);
  43. const result = spawnSync(resolved.command, resolved.args, {
  44. cwd,
  45. stdio: 'inherit',
  46. shell: resolved.shell,
  47. env: {
  48. ...process.env,
  49. npm_config_cache: npmCache,
  50. NPM_CONFIG_CACHE: npmCache
  51. },
  52. maxBuffer: 1024 * 1024 * 100
  53. });
  54. if (result.status !== 0) {
  55. throw new Error(`${command} ${args.join(' ')} failed in ${cwd}`);
  56. }
  57. }
  58. function resolveCommand(command, args) {
  59. if (command === 'npm' && process.env.npm_execpath) {
  60. return { command: process.execPath, args: [process.env.npm_execpath, ...args], shell: false };
  61. }
  62. if (command === 'npm' && process.platform === 'win32') {
  63. return { command: 'npm.cmd', args, shell: true };
  64. }
  65. return { command, args, shell: false };
  66. }
  67. function assert(condition, message) {
  68. if (!condition) throw new Error(message);
  69. }
  70. try {
  71. main();
  72. } catch (error) {
  73. console.error(error && error.stack ? error.stack : String(error));
  74. process.exit(1);
  75. }