#!/usr/bin/env node /** * scripts/extract-agent-prompts.js * * 从 docs/prd/newfunction/agents-{role}.html 抽取 §2.2 "完整 System Prompt" * 区块,写到 system/agents/devloop-{role}/prompt.md。 * * role ∈ pm | architect | coder | tester | integrator | triager * * 抽取规则(本脚本只做规则正向匹配,不依赖 cheerio 来保留原始换行): * 1. 定位

2.2 完整 System Prompt

* 2. 取其后第一个
...
的内层 * 3. 去除全部 ... 包装,保留文本 * 4. 反转义 HTML entity (< > & ") * * 规格: docs/prd/newfunction/plan-migration.md · P0.B */ const fs = require('fs'); const path = require('path'); const ROLES = ['pm', 'architect', 'coder', 'tester', 'integrator', 'triager']; const SRC_DIR = path.resolve(__dirname, '../docs/prd/newfunction'); const OUT_DIR = require('../server/services/agent-locator').AGENTS_ROOT; function decodeHtmlEntities(s) { return s .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, "'") .replace(/'/g, "'") .replace(/&/g, '&'); } function stripSpans(s) { // 去除 ...,保留内部文本(可能嵌套也用同一规则处理) let prev; let cur = s; do { prev = cur; cur = cur.replace(/]*>([\s\S]*?)<\/span>/g, '$1'); } while (cur !== prev); // 去除残留的开闭标签 (容错) cur = cur.replace(/<\/?span[^>]*>/g, ''); return cur; } function extractPrompt(html, role) { const anchorRe = /]*>\s*2\.2\s+完整\s*System Prompt[^<]*<\/h3>/; const am = html.match(anchorRe); if (!am) throw new Error(`§2.2 header not found in agents-${role}.html`); const after = html.slice(am.index + am[0].length); const preRe = /]*>([\s\S]*?)<\/pre>/; const pm = after.match(preRe); if (!pm) throw new Error(`
 after §2.2 not found in agents-${role}.html`);
  const inner = pm[1];
  const stripped = stripSpans(inner);
  const decoded = decodeHtmlEntities(stripped);
  return decoded.trim();
}

function run() {
  fs.mkdirSync(OUT_DIR, { recursive: true });
  const results = [];
  for (const role of ROLES) {
    const src = path.join(SRC_DIR, `agents-${role}.html`);
    if (!fs.existsSync(src)) {
      results.push({ role, ok: false, reason: 'source missing: ' + src });
      continue;
    }
    try {
      const html = fs.readFileSync(src, 'utf8');
      const prompt = extractPrompt(html, role);
      const outDir = path.join(OUT_DIR, `devloop-${role}`);
      fs.mkdirSync(outDir, { recursive: true });
      const outPath = path.join(outDir, 'prompt.md');
      const header = `\n\n`;
      fs.writeFileSync(outPath, header + prompt + '\n');
      results.push({ role, ok: true, bytes: prompt.length, path: outPath });
    } catch (e) {
      results.push({ role, ok: false, reason: e.message });
    }
  }
  for (const r of results) {
    if (r.ok) console.log(`✓ devloop-${r.role}.md (${r.bytes} bytes)`);
    else console.error(`✗ ${r.role}: ${r.reason}`);
  }
  const allOk = results.every(r => r.ok);
  if (!allOk) process.exit(1);
}

if (require.main === module) run();

module.exports = { extractPrompt, stripSpans, decodeHtmlEntities };