@@ -1556,7 +1556,9 @@ function buildCreatedPlanLog(planId: string, title: string, timestamp: string):
15561556}
15571557
15581558function isArchitectRuntimeIdentityQuestion(message: string): boolean {
1559- return /\b(model|provider|adapter|runtime|route|session)\b/i.test(message);
1559+ const compact = message.replace(/\s+/g, " ").trim();
1560+ if (!compact.endsWith("?")) return false;
1561+ return /\b(what|which|who)\b.*\b(model|provider|adapter|runtime|route|session)\b/i.test(compact);
15601562}
15611563
15621564function shouldApplyPlanChatUpdate(message: string, plan: ArchitectPlanProjection | null): boolean {
@@ -1576,11 +1578,101 @@ function normalizePlanChatUpdateContent(message: string): string {
15761578 .slice(0, 24_000);
15771579}
15781580
1581+ function splitMarkdownTableRow(line: string): string[] {
1582+ return line
1583+ .trim()
1584+ .replace(/^\|/, "")
1585+ .replace(/\|$/, "")
1586+ .split("|")
1587+ .map((cell) => cell.trim());
1588+ }
1589+
1590+ function isMarkdownTableDivider(line: string): boolean {
1591+ return /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(line);
1592+ }
1593+
1594+ function normalizeSliceTitle(sliceCell: string, changeCell: string): string {
1595+ const cleanSlice = sliceCell.replace(/^slice\s+/i, "").trim();
1596+ const ordinal = cleanSlice || "1";
1597+ const firstSentence = changeCell
1598+ .replace(/`([^`]+)`/g, "$1")
1599+ .split(/[.!?]/)[0]
1600+ ?.replace(/\s+/g, " ")
1601+ .trim();
1602+ const title = firstSentence && firstSentence.length > 0 ? firstSentence.slice(0, 80) : "Implementation Work";
1603+ return `Slice ${ordinal} - ${title}`;
1604+ }
1605+
1606+ function normalizeArchitectNativePlanMarkdown(markdown: string): string {
1607+ const lines = markdown.split("\n");
1608+ const nextLines: string[] = [];
1609+ for (let index = 0; index < lines.length; index += 1) {
1610+ const headingMatch = lines[index].match(/^(##\s+)(\d+\.\s+)?Planned Slices\s*$/i);
1611+ if (!headingMatch) {
1612+ nextLines.push(lines[index]);
1613+ continue;
1614+ }
1615+
1616+ nextLines.push(`${headingMatch[1]}${headingMatch[2] ?? ""}Implementation Slices`);
1617+ index += 1;
1618+ const sectionLines: string[] = [];
1619+ while (index < lines.length && !/^##\s+/.test(lines[index])) {
1620+ sectionLines.push(lines[index]);
1621+ index += 1;
1622+ }
1623+ index -= 1;
1624+
1625+ const tableHeaderIndex = sectionLines.findIndex((line) => /^\s*\|\s*Slice\s*\|/i.test(line));
1626+ if (tableHeaderIndex < 0 || tableHeaderIndex + 1 >= sectionLines.length || !isMarkdownTableDivider(sectionLines[tableHeaderIndex + 1])) {
1627+ nextLines.push(...sectionLines);
1628+ continue;
1629+ }
1630+
1631+ nextLines.push(...sectionLines.slice(0, tableHeaderIndex));
1632+ const headers = splitMarkdownTableRow(sectionLines[tableHeaderIndex]).map((cell) => cell.toLowerCase());
1633+ const sliceIndex = headers.indexOf("slice");
1634+ const changeIndex = headers.indexOf("change");
1635+ const providerIndex = headers.findIndex((cell) => cell.includes("provider") || cell.includes("adapter"));
1636+ const filesIndex = headers.indexOf("files");
1637+ const verificationIndex = headers.indexOf("verification");
1638+
1639+ for (let tableIndex = tableHeaderIndex + 2; tableIndex < sectionLines.length; tableIndex += 1) {
1640+ const line = sectionLines[tableIndex];
1641+ if (!/^\s*\|/.test(line)) {
1642+ if (line.trim()) nextLines.push(line);
1643+ continue;
1644+ }
1645+ const cells = splitMarkdownTableRow(line);
1646+ const sliceCell = sliceIndex >= 0 ? cells[sliceIndex] ?? "" : "";
1647+ const changeCell = changeIndex >= 0 ? cells[changeIndex] ?? "" : "";
1648+ if (!sliceCell || !changeCell) continue;
1649+
1650+ nextLines.push("");
1651+ nextLines.push(`### ${normalizeSliceTitle(sliceCell, changeCell)}`);
1652+ nextLines.push("");
1653+ nextLines.push(changeCell);
1654+ if (providerIndex >= 0 && cells[providerIndex]) {
1655+ nextLines.push("");
1656+ nextLines.push(`Provider/adapter specifics: ${cells[providerIndex]}`);
1657+ }
1658+ if (filesIndex >= 0 && cells[filesIndex]) {
1659+ nextLines.push("");
1660+ nextLines.push(`Files: ${cells[filesIndex]}`);
1661+ }
1662+ if (verificationIndex >= 0 && cells[verificationIndex]) {
1663+ nextLines.push("");
1664+ nextLines.push(`Verification: ${cells[verificationIndex]}`);
1665+ }
1666+ }
1667+ }
1668+ return nextLines.join("\n");
1669+ }
1670+
15791671function applyPlanChatUpdateToMarkdown(markdown: string, content: string, timestamp: string): { markdown: string; mode: ArchitectPlanChatUpdateResult["update_mode"]; sectionTitle: string } {
15801672 const placeholder = "Describe the goal for this Architect plan.";
15811673 if (markdown.includes(placeholder)) {
15821674 return {
1583- markdown: markdown.replace(placeholder, content),
1675+ markdown: normalizeArchitectNativePlanMarkdown( markdown.replace(placeholder, content) ),
15841676 mode: "replace_goal_placeholder",
15851677 sectionTitle: "0. Goal",
15861678 };
@@ -1597,7 +1689,7 @@ function applyPlanChatUpdateToMarkdown(markdown: string, content: string, timest
15971689 content,
15981690 "",
15991691 ].join("\n");
1600- return { markdown: nextMarkdown, mode: "append_chat_update", sectionTitle };
1692+ return { markdown: normalizeArchitectNativePlanMarkdown( nextMarkdown) , mode: "append_chat_update", sectionTitle };
16011693}
16021694
16031695async function applyPlanChatUpdate(
0 commit comments