baa-conductor


baa-conductor / apps / conductor-daemon / src / instructions
im_wower  ·  2026-03-29

extract.ts

 1import type { BaaExtractedBlock } from "./types.js";
 2
 3const CODE_FENCE_OPEN_PATTERN = /^[ \t]{0,3}```([^\n]*)$/u;
 4const CODE_FENCE_CLOSE_PATTERN = /^[ \t]{0,3}```[ \t]*$/u;
 5
 6export class BaaInstructionExtractError extends Error {
 7  readonly stage = "extract";
 8
 9  constructor(message: string) {
10    super(message);
11  }
12}
13
14export function extractBaaInstructionBlocks(text: string): BaaExtractedBlock[] {
15  const normalizedText = text.replace(/\r\n?/gu, "\n");
16  const lines = normalizedText.split("\n");
17  const blocks: BaaExtractedBlock[] = [];
18  let pending:
19    | {
20        contentLines: string[];
21        isBaa: boolean;
22        openingLine: string;
23      }
24    | null = null;
25
26  for (const line of lines) {
27    if (pending == null) {
28      const match = line.match(CODE_FENCE_OPEN_PATTERN);
29
30      if (!match) {
31        continue;
32      }
33
34      pending = {
35        contentLines: [],
36        isBaa: /^baa(?:\s|$)/u.test((match[1] ?? "").trim()),
37        openingLine: line
38      };
39      continue;
40    }
41
42    if (CODE_FENCE_CLOSE_PATTERN.test(line)) {
43      if (pending.isBaa) {
44        blocks.push({
45          blockIndex: blocks.length,
46          content: pending.contentLines.join("\n"),
47          rawBlock: `${pending.openingLine}\n${pending.contentLines.join("\n")}\n${line}`
48        });
49      }
50
51      pending = null;
52      continue;
53    }
54
55    pending.contentLines.push(line);
56  }
57
58  return blocks;
59}