|
37 | 37 | from pathlib import Path |
38 | 38 | from typing import Any |
39 | 39 |
|
| 40 | +from cppmega_mlx.data.build_parsers.base import ParsedDomainDocument |
| 41 | +from cppmega_mlx.data.diagnostic_parsers import ( |
| 42 | + parse_build_error, |
| 43 | + parse_clang_diagnostic, |
| 44 | + parse_linker_error, |
| 45 | +) |
| 46 | +from cppmega_mlx.data.shell_parsers import parse_bash, parse_sh, parse_tcsh, parse_zsh |
| 47 | + |
40 | 48 | # --------------------------------------------------------------------------- # |
41 | 49 | # Action-kind classification |
42 | 50 | # --------------------------------------------------------------------------- # |
@@ -116,6 +124,59 @@ def classify_action(tool_name: str, command: str | None) -> str: |
116 | 124 | return ACTION_OTHER |
117 | 125 |
|
118 | 126 |
|
| 127 | +def parse_shell_action_domain( |
| 128 | + command: str, |
| 129 | + *, |
| 130 | + shell_kind: str | None = None, |
| 131 | +) -> ParsedDomainDocument: |
| 132 | + """Parse a trajectory shell action into the matching shell domain. |
| 133 | +
|
| 134 | + Unknown shell stays POSIX ``sh``. We do not label it bash unless the caller |
| 135 | + or command itself gives evidence, because bash/zsh/tcsh have different |
| 136 | + syntax and should remain separate domains for training. |
| 137 | + """ |
| 138 | + |
| 139 | + kind = (shell_kind or "").strip().lower() |
| 140 | + stripped = command.lstrip() |
| 141 | + if not kind: |
| 142 | + if stripped.startswith("#!"): |
| 143 | + first = stripped.splitlines()[0] |
| 144 | + if "zsh" in first: |
| 145 | + kind = "zsh" |
| 146 | + elif "tcsh" in first or "csh" in first: |
| 147 | + kind = "tcsh" |
| 148 | + elif "bash" in first: |
| 149 | + kind = "bash" |
| 150 | + else: |
| 151 | + kind = "sh" |
| 152 | + else: |
| 153 | + head = stripped.split(maxsplit=1)[0] if stripped else "" |
| 154 | + kind = head if head in {"bash", "zsh", "tcsh", "sh"} else "sh" |
| 155 | + |
| 156 | + if kind == "bash": |
| 157 | + return parse_bash(command) |
| 158 | + if kind == "zsh": |
| 159 | + return parse_zsh(command) |
| 160 | + if kind == "tcsh": |
| 161 | + return parse_tcsh(command) |
| 162 | + return parse_sh(command) |
| 163 | + |
| 164 | + |
| 165 | +def parse_result_diagnostic_domain(result_text: str) -> ParsedDomainDocument | None: |
| 166 | + """Parse a build/test/tool result into a diagnostic domain when possible.""" |
| 167 | + |
| 168 | + if not result_text.strip(): |
| 169 | + return None |
| 170 | + lower = result_text.lower() |
| 171 | + if "undefined reference" in lower or "unresolved external symbol" in lower: |
| 172 | + return parse_linker_error(result_text) |
| 173 | + if re.search(r"^[^\n:]+:\d+:\d+:\s+(fatal error|error|warning|note):", result_text, re.MULTILINE): |
| 174 | + return parse_clang_diagnostic(result_text) |
| 175 | + if "cmake error" in lower or "ninja:" in lower or "build stopped" in lower: |
| 176 | + return parse_build_error(result_text) |
| 177 | + return None |
| 178 | + |
| 179 | + |
119 | 180 | # --------------------------------------------------------------------------- # |
120 | 181 | # Transition record (flat parquet-friendly schema) |
121 | 182 | # --------------------------------------------------------------------------- # |
@@ -799,6 +860,8 @@ def write_parquet(transitions: list[AgentTransition], out_path: Path) -> Path: |
799 | 860 | "enumerate_remote_sessions", |
800 | 861 | "extract_all", |
801 | 862 | "extract_session", |
| 863 | + "parse_result_diagnostic_domain", |
| 864 | + "parse_shell_action_domain", |
802 | 865 | "walk_claude", |
803 | 866 | "walk_codex", |
804 | 867 | "write_parquet", |
|
0 commit comments