acceptance.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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', 'fmode:image:smoke'], ROOT);
  11. run('npm', ['run', 'smoke:package'], ROOT);
  12. run('npm', ['run', 'mcp:smoke'], ROOT);
  13. run('npm', ['run', 'acceptance:installer'], ROOT);
  14. run('npm', ['pack', '--dry-run'], ROOT);
  15. const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'voc-workspace-install-'));
  16. run(process.execPath, [path.join(ROOT, 'bin', 'claude-voc.js'), 'workspace', '--smoke'], workspace);
  17. const mcpFile = path.join(workspace, '.mcp.json');
  18. const pluginRoot = path.join(workspace, '.claude', 'plugins', 'voc-intelligence');
  19. const pluginServer = path.join(pluginRoot, 'mcp', 'src', 'server.js');
  20. const skillNames = [
  21. 'xiaohongshu-trend-intelligence',
  22. 'douyin-trend-intelligence',
  23. 'voc-issue-pool',
  24. 'voc-problem-deep-dive',
  25. 'voc-content-plan',
  26. 'voc-speaking-script',
  27. 'voc-competitor-map',
  28. 'voc-business-workflow',
  29. 'fmode-image-analysis'
  30. ];
  31. assert(fs.existsSync(mcpFile), 'workspace install should write .mcp.json');
  32. assert(fs.existsSync(pluginServer), 'workspace install should copy plugin server');
  33. for (const skillName of skillNames) {
  34. assert(
  35. fs.existsSync(path.join(workspace, '.claude', 'skills', skillName, 'SKILL.md')),
  36. `workspace install should write skill entry: ${skillName}`
  37. );
  38. }
  39. const mcp = JSON.parse(fs.readFileSync(mcpFile, 'utf8'));
  40. const server = mcp.mcpServers && mcp.mcpServers.voc;
  41. assert(server, 'workspace .mcp.json should register voc server');
  42. assert(Array.isArray(server.args) && path.isAbsolute(server.args[0]), 'workspace MCP server path should be absolute');
  43. assert(path.resolve(server.args[0]) === path.resolve(pluginServer), 'workspace MCP server path should point to installed plugin server');
  44. console.log(`acceptance ok: ${workspace}`);
  45. }
  46. function run(command, args, cwd) {
  47. const resolved = resolveCommand(command, args);
  48. const result = spawnSync(resolved.command, resolved.args, {
  49. cwd,
  50. stdio: 'inherit',
  51. shell: resolved.shell,
  52. env: {
  53. ...process.env,
  54. npm_config_cache: npmCache,
  55. NPM_CONFIG_CACHE: npmCache
  56. },
  57. maxBuffer: 1024 * 1024 * 100
  58. });
  59. if (result.status !== 0) {
  60. throw new Error(`${command} ${args.join(' ')} failed in ${cwd}`);
  61. }
  62. }
  63. function resolveCommand(command, args) {
  64. if (command === 'npm' && process.env.npm_execpath) {
  65. return { command: process.execPath, args: [process.env.npm_execpath, ...args], shell: false };
  66. }
  67. if (command === 'npm' && process.platform === 'win32') {
  68. return { command: 'npm.cmd', args, shell: true };
  69. }
  70. return { command, args, shell: false };
  71. }
  72. function assert(condition, message) {
  73. if (!condition) throw new Error(message);
  74. }
  75. try {
  76. main();
  77. } catch (error) {
  78. console.error(error && error.stack ? error.stack : String(error));
  79. process.exit(1);
  80. }