codex@macbookpro
·
2026-03-27
parse.ts
1import type {
2 BaaExtractedBlock,
3 BaaInstructionParams,
4 BaaInstructionParamsKind,
5 BaaParsedInstruction
6} from "./types.js";
7import { isBaaJsonValue } from "./types.js";
8
9const INSTRUCTION_HEADER_PATTERN =
10 /^@(?<target>[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*)::(?<tool>[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*)(?:::(?<inline>.*))?$/u;
11
12export class BaaInstructionParseError extends Error {
13 readonly blockIndex: number;
14 readonly stage = "parse";
15
16 constructor(blockIndex: number, message: string) {
17 super(message);
18 this.blockIndex = blockIndex;
19 }
20}
21
22function normalizeInlineParams(
23 blockIndex: number,
24 rawInlineParams: string
25): { params: BaaInstructionParams; paramsKind: BaaInstructionParamsKind } {
26 const trimmedInlineParams = rawInlineParams.trim();
27
28 if (trimmedInlineParams.startsWith("{") || trimmedInlineParams.startsWith("[")) {
29 try {
30 const parsed = JSON.parse(trimmedInlineParams) as unknown;
31
32 if (!isBaaJsonValue(parsed)) {
33 throw new Error("Inline JSON params must resolve to a JSON value.");
34 }
35
36 return {
37 params: parsed,
38 paramsKind: "inline_json"
39 };
40 } catch (error) {
41 const message = error instanceof Error ? error.message : String(error);
42 throw new BaaInstructionParseError(
43 blockIndex,
44 `Failed to parse inline JSON params: ${message}`
45 );
46 }
47 }
48
49 return {
50 params: rawInlineParams,
51 paramsKind: "inline_string"
52 };
53}
54
55export function parseBaaInstructionBlock(block: BaaExtractedBlock): BaaParsedInstruction {
56 const instructionText = block.content.replace(/\r\n?/gu, "\n");
57 const lines = instructionText.split("\n");
58 const firstLine = lines[0]?.trim() ?? "";
59
60 if (firstLine === "") {
61 throw new BaaInstructionParseError(block.blockIndex, "BAA instruction block is empty.");
62 }
63
64 const headerMatch = firstLine.match(INSTRUCTION_HEADER_PATTERN);
65
66 if (!headerMatch?.groups) {
67 throw new BaaInstructionParseError(
68 block.blockIndex,
69 `Invalid BAA instruction header: ${firstLine}`
70 );
71 }
72
73 const target = headerMatch.groups.target;
74 const tool = headerMatch.groups.tool;
75 const rawInlineParams = headerMatch.groups.inline;
76 const rawBody = lines.length > 1 ? lines.slice(1).join("\n") : null;
77 const hasBody = rawBody != null && rawBody.trim() !== "";
78
79 if (!target || !tool) {
80 throw new BaaInstructionParseError(block.blockIndex, "BAA instruction target and tool are required.");
81 }
82
83 if (rawInlineParams != null && hasBody) {
84 throw new BaaInstructionParseError(
85 block.blockIndex,
86 "BAA instruction cannot mix inline params with a multiline body."
87 );
88 }
89
90 if (rawInlineParams != null) {
91 const normalizedInlineParams = normalizeInlineParams(block.blockIndex, rawInlineParams);
92
93 return {
94 blockIndex: block.blockIndex,
95 params: normalizedInlineParams.params,
96 paramsKind: normalizedInlineParams.paramsKind,
97 rawBlock: block.rawBlock,
98 rawInstruction: instructionText,
99 target,
100 tool
101 };
102 }
103
104 if (hasBody) {
105 return {
106 blockIndex: block.blockIndex,
107 params: rawBody!,
108 paramsKind: "body",
109 rawBlock: block.rawBlock,
110 rawInstruction: instructionText,
111 target,
112 tool
113 };
114 }
115
116 return {
117 blockIndex: block.blockIndex,
118 params: null,
119 paramsKind: "none",
120 rawBlock: block.rawBlock,
121 rawInstruction: instructionText,
122 target,
123 tool
124 };
125}