acceptance.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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(root, '.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', 'live:normalization:smoke'], root);
  13. run('npm', ['run', 'smoke:briefs'], root);
  14. run('npm', ['run', 'hard-rules:smoke'], root);
  15. run('npm', ['run', 'reference-rules:smoke'], root);
  16. run('npm', ['run', 'platform:strategy:smoke'], root);
  17. run('npm', ['run', 'feedback:smoke'], root);
  18. run('npm', ['run', 'feedback:roundtrip'], root);
  19. run('npm', ['run', 'feedback:closure:smoke'], root);
  20. run('npm', ['run', 'review:metrics:smoke'], root);
  21. run('npm', ['run', 'manual-review:label-guide:smoke'], root);
  22. run('npm', ['run', 'skill:refs:smoke'], root);
  23. run('npm', ['run', 'customer-effect:smoke'], root);
  24. run('npm', ['run', 'software:smoke'], root);
  25. run('npm', ['run', 'proof-gap:software-form:smoke'], root);
  26. run('npm', ['run', 'plan:consistency:smoke'], root);
  27. run('npm', ['run', 'plan:execution-status:smoke'], root);
  28. run('npm', ['run', 'hygiene:smoke'], root);
  29. run('npm', ['run', 'candidate-pool:smoke'], root);
  30. run('npm', ['run', 'history:from-csv:smoke'], root);
  31. run('npm', ['run', 'history:audit:smoke'], root);
  32. run('npm', ['run', 'data:intake-template:smoke'], root);
  33. run('npm', ['run', 'video:intake-template:smoke'], root);
  34. run('npm', ['run', 'intake:readiness:smoke'], root);
  35. run('npm', ['run', 'intake:field-checklist:smoke'], root);
  36. run('npm', ['run', 'video:resource-readiness:smoke'], root);
  37. run('npm', ['run', 'longrun:readiness:smoke'], root);
  38. run('npm', ['run', 'local-seed-material:index:smoke'], root);
  39. run('npm', ['run', 'local-seed-to-intake:worklist:smoke'], root);
  40. run('npm', ['run', 'experience-transcript:index:smoke'], root);
  41. run('npm', ['run', 'real-gap:materials:smoke'], root);
  42. run('npm', ['run', 'optimization:pipeline:smoke'], root);
  43. run('npm', ['run', 'optimization:completion:smoke'], root);
  44. run('npm', ['run', 'handoff:summary:smoke'], root);
  45. run('npm', ['run', 'proof-gap:request:smoke'], root);
  46. run('npm', ['run', 'proof-gap:closure:smoke'], root);
  47. run('npm', ['run', 'proof-gap:intake-bundle:smoke'], root);
  48. run('npm', ['run', 'real-proof:work-order:smoke'], root);
  49. run('npm', ['run', 'business-proof:progress:smoke'], root);
  50. run('npm', ['run', 'business-proof:next-actions:smoke'], root);
  51. run('npm', ['run', 'business-proof:next-actions:missing-proof:smoke'], root);
  52. run('npm', ['run', 'business-execution:index:smoke'], root);
  53. run('npm', ['run', 'latest-form:index:smoke'], root);
  54. run('npm', ['run', 'round:refresh:smoke'], root);
  55. run('npm', ['run', 'round:deposition:smoke'], root);
  56. run('npm', ['run', 'task:handoff-index:smoke'], root);
  57. run('npm', ['run', 'evidence:index:smoke'], root);
  58. run('npm', ['run', 'acceptance:video-ab:smoke'], root);
  59. run('npm', ['run', 'overnight:review:smoke'], root);
  60. run('npm', ['run', 'optimization:status:smoke'], root);
  61. run('npm', ['run', 'homepage:smoke'], root);
  62. run('npm', ['run', 'homepage:readiness:smoke'], root);
  63. run('npm', ['run', 'mcp:smoke'], root);
  64. run('npm', ['run', 'acceptance:pm'], root);
  65. run('npm', ['run', 'acceptance:providers:mock'], root);
  66. run('npm', ['run', 'package:contents:smoke'], root);
  67. run('npm', ['pack', '--dry-run'], root);
  68. const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-workspace-install-'));
  69. const legacyPluginDir = path.join(workspace, '.claude', 'plugins', 'tihao-sourcing');
  70. const legacySkillDir = path.join(workspace, '.claude', 'skills', 'tihao-creator-sourcing');
  71. fs.mkdirSync(legacyPluginDir, { recursive: true });
  72. fs.writeFileSync(path.join(legacyPluginDir, 'legacy.txt'), 'legacy tihao-sourcing plugin\n', 'utf8');
  73. fs.mkdirSync(legacySkillDir, { recursive: true });
  74. fs.writeFileSync(path.join(legacySkillDir, 'SKILL.md'), 'name: tihao-creator-sourcing\n', 'utf8');
  75. fs.writeFileSync(path.join(workspace, '.mcp.json'), JSON.stringify({
  76. mcpServers: {
  77. 'tihao-sourcing': {
  78. command: 'node',
  79. args: [path.join(legacyPluginDir, 'mcp', 'src', 'server.js')]
  80. }
  81. }
  82. }, null, 2), 'utf8');
  83. run(process.execPath, [path.join(root, 'install.js'), '--workspace', '--smoke'], workspace);
  84. const mcpFile = path.join(workspace, '.mcp.json');
  85. const skillFile = path.join(workspace, '.claude', 'skills', 'tihao', 'SKILL.md');
  86. const imageSkillFile = path.join(workspace, '.claude', 'skills', 'fmode-image-analysis', 'SKILL.md');
  87. const pluginServer = path.join(workspace, '.claude', 'plugins', 'tihao', 'mcp', 'src', 'server.js');
  88. assert(fs.existsSync(mcpFile), 'workspace install should write .mcp.json');
  89. assert(fs.existsSync(skillFile), 'workspace install should write skill entry');
  90. assert(fs.existsSync(imageSkillFile), 'workspace install should write fmode image analysis skill entry');
  91. assert(!fs.existsSync(legacySkillDir), 'workspace install should remove legacy tihao-creator-sourcing skill entry');
  92. assert(!fs.existsSync(legacyPluginDir), 'workspace install should remove legacy tihao-sourcing plugin entry');
  93. assert(fs.existsSync(pluginServer), 'workspace install should copy plugin server');
  94. const mcp = JSON.parse(fs.readFileSync(mcpFile, 'utf8'));
  95. assert(mcp.mcpServers && mcp.mcpServers.tihao, 'workspace .mcp.json should register tihao server');
  96. assert(!mcp.mcpServers['tihao-sourcing'], 'workspace .mcp.json should remove legacy tihao-sourcing server');
  97. assert(path.isAbsolute(mcp.mcpServers.tihao.args[0]), 'workspace MCP server path should be absolute');
  98. console.log(`acceptance ok: ${workspace}`);
  99. }
  100. function run(command, args, cwd) {
  101. const resolved = resolveCommand(command, args);
  102. const result = spawnSync(resolved.command, resolved.args, {
  103. cwd,
  104. stdio: 'inherit',
  105. shell: resolved.shell,
  106. env: {
  107. ...process.env,
  108. npm_config_cache: npmCache,
  109. NPM_CONFIG_CACHE: npmCache
  110. }
  111. });
  112. if (result.status !== 0) {
  113. throw new Error(`${command} ${args.join(' ')} failed in ${cwd}`);
  114. }
  115. }
  116. function resolveCommand(command, args) {
  117. if (command === 'npm' && process.env.npm_execpath) {
  118. return { command: process.execPath, args: [process.env.npm_execpath, ...args], shell: false };
  119. }
  120. if (command === 'npm' && process.platform === 'win32') {
  121. return { command: 'npm.cmd', args, shell: true };
  122. }
  123. return { command, args, shell: false };
  124. }
  125. function assert(condition, message) {
  126. if (!condition) throw new Error(message);
  127. }
  128. main();