2026-06-15 08:06:08 +03:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
/**
|
|
|
|
|
|
* negotiation-section — парсер раздела «## Переговоры» план-файла. Возвращает круги
|
|
|
|
|
|
* [{round, position}] с ДОСЛОВНЫМ текстом позиции контроллера (для наставника и арбитража).
|
|
|
|
|
|
* Раздел = «## Переговоры» до следующего «## », внутри — «### Круг N» блоки.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function parseNegotiationSection(md) {
|
|
|
|
|
|
const text = typeof md === 'string' ? md : '';
|
|
|
|
|
|
// БЕЗ флага m: `$` = конец ТЕКСТА (не строки), иначе нежадный захват обрывается на 1-й строке.
|
|
|
|
|
|
// Заголовки матчим через (?:^|\n), терминатор — следующий «\n## » или конец текста.
|
|
|
|
|
|
const sec = text.match(/(?:^|\n)##\s*Переговоры[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
|
|
|
|
if (!sec) return [];
|
|
|
|
|
|
const body = sec[1];
|
|
|
|
|
|
const out = [];
|
|
|
|
|
|
const re = /(?:^|\n)###\s*Круг\s*(\d+)[^\n]*\n([\s\S]*?)(?=\n###\s|$)/gi;
|
|
|
|
|
|
let m;
|
|
|
|
|
|
while ((m = re.exec(body)) !== null) {
|
2026-06-16 15:59:39 +03:00
|
|
|
|
const position = m[2].trim();
|
|
|
|
|
|
out.push({
|
|
|
|
|
|
round: Number(m[1]),
|
|
|
|
|
|
position,
|
|
|
|
|
|
mentor: extractAddressee(position, 'Наставнику'),
|
|
|
|
|
|
judge: extractAddressee(position, 'Судье'),
|
|
|
|
|
|
});
|
2026-06-15 08:06:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
return out;
|
|
|
|
|
|
}
|
2026-06-16 15:59:39 +03:00
|
|
|
|
|
|
|
|
|
|
/** Достаёт текст, адресованный стороне (`**Наставнику:**` / `**Судье:**`), до следующего
|
|
|
|
|
|
* адресата или конца. Нет адресата → пустая строка (обратная совместимость с position). */
|
|
|
|
|
|
function extractAddressee(body, label) {
|
|
|
|
|
|
const re = new RegExp(`\\*\\*${label}:\\*\\*\\s*([\\s\\S]*?)(?=\\n?\\*\\*(?:Наставнику|Судье):\\*\\*|$)`, 'i');
|
|
|
|
|
|
const mm = String(body || '').match(re);
|
|
|
|
|
|
return mm ? mm[1].trim() : '';
|
|
|
|
|
|
}
|
2026-06-16 17:48:13 +03:00
|
|
|
|
|
|
|
|
|
|
/** SP2d: контроллер просит арбитраж на ЛЮБОМ круге — маркер `**Арбитраж:** <причина>` в
|
|
|
|
|
|
* артефакте (раздел «Переговоры»). Возвращает причину (truthy) или '' если маркера нет.
|
|
|
|
|
|
* Карточка арбитража показывается при этом маркере независимо от круга (потолок ≤3 остаётся). */
|
|
|
|
|
|
export function arbitrationRequested(md) {
|
|
|
|
|
|
const text = typeof md === 'string' ? md : '';
|
|
|
|
|
|
const m = text.match(/\*\*Арбитраж:\*\*\s*([^\n]*)/i);
|
|
|
|
|
|
if (!m) return '';
|
|
|
|
|
|
return m[1].trim() || 'арбитраж запрошен контроллером';
|
|
|
|
|
|
}
|