import fs from "node:fs/promises"; import path from "node:path"; import { skillHome } from "./cli.mjs"; export async function readSkillCatalogs() { const [skill, scriptCatalog, initialContext, apiMap] = await Promise.all([ fs.readFile(path.join(skillHome, "SKILL.md"), "utf8"), fs.readFile(path.join(skillHome, "references", "script-catalog.md"), "utf8"), fs.readFile(path.join(skillHome, "references", "initial-context-catalog.md"), "utf8"), fs.readFile(path.join(skillHome, "references", "api-map.md"), "utf8"), ]); return { skill, scriptCatalog, initialContext, apiMap, allText: [skill, scriptCatalog, initialContext, apiMap].join("\n"), }; } export function documentedFlags(catalogs) { const flags = new Set(); const regex = /`(--[a-z0-9-]+)(?:\s+[^`]*)?`/gi; let match = regex.exec(catalogs.allText); while (match) { flags.add(match[1]); match = regex.exec(catalogs.allText); } return flags; } export function catalogMentionsIntent(catalogs, intentName) { return new RegExp(`\\b${escapeRegExp(intentName)}\\b`).test(catalogs.skill + "\n" + catalogs.scriptCatalog); } export function catalogMentionsView(catalogs, viewName) { return new RegExp(`\\b${escapeRegExp(viewName)}\\b`).test(catalogs.skill + "\n" + catalogs.scriptCatalog); } export function commandAppearsInCatalog(catalogs, args) { const normalizedCatalog = normalizeWhitespace(catalogs.skill + "\n" + catalogs.scriptCatalog + "\n" + catalogs.apiMap); const normalizedArgs = normalizeWhitespace(args.join(" ")); return normalizedCatalog.includes(normalizedArgs); } export function parseDefaultWeekYear(scriptCatalog) { const match = /-\s*`week-year\s+(\d{4})`/i.exec(scriptCatalog); if (!match) { throw new Error("Could not parse default week-year from references/script-catalog.md."); } return Number(match[1]); } export function parseWeekRow(scriptCatalog, weekLabel) { const normalizedLabel = String(weekLabel).toUpperCase().replace(/^W?/, "W"); const regex = new RegExp(`\\|\\s*${escapeRegExp(normalizedLabel)}\\s*\\|\\s*(\\d{4}-\\d{2}-\\d{2})\\s*\\|\\s*(\\d{4}-\\d{2}-\\d{2})\\s*\\|`, "i"); const match = regex.exec(scriptCatalog); if (!match) { throw new Error(`Could not parse ${normalizedLabel} from references/script-catalog.md.`); } return { weekLabel: normalizedLabel, fromDate: match[1], toDate: match[2], }; } export function parseSaAttachDefaults(initialContext) { const line = findLine(initialContext, "SA Attach defaults:"); const rawDefaults = line.slice(line.indexOf(":") + 1).split(";").map((part) => part.trim()).filter(Boolean); const defaults = {}; for (const part of rawDefaults) { const match = /`--([^=]+)=([^`]+)`/.exec(part); if (!match) continue; defaults[toCamelKey(match[1])] = splitCatalogCsv(match[2]); } return defaults; } export function parseSaAttachAliases(initialContext) { const line = findLine(initialContext, "SA Attach aliases:"); const aliases = {}; const rawAliases = line.slice(line.indexOf(":") + 1).split(";").map((part) => part.trim()).filter(Boolean); for (const item of rawAliases) { const match = /`([^`]+)`\s*->\s*`([^`]+)`/.exec(item); if (!match) continue; aliases[match[1].toLowerCase()] = splitCatalogCsv(match[2]); } return aliases; } export function catalogSaysCurrentWorkloadDefaults(catalogs, intentName) { try { parseCurrentWorkloadDefaults(catalogs, intentName); return true; } catch { return false; } } export function parseCurrentWorkloadDefaults(catalogs, intentName) { const source = catalogs.skill + "\n" + catalogs.scriptCatalog; const pattern = new RegExp( `\\\`${escapeRegExp(intentName)}\\\`[\\s\\S]{0,500}?defaults? to \\\`([^\\\`]+)\\\`, \\\`([^\\\`]+)\\\`, and \\\`([^\\\`]+)\\\``, "i" ); const match = pattern.exec(source); if (!match) { throw new Error(`Could not parse documented defaults for ${intentName}.`); } return { quarter: match[1], solution: match[2], revenueTypeGroup: match[3], }; } export function catalogSaysUseRevenueLineCloseDate(catalogs) { try { parseSaAttachDateWindowField(catalogs); return true; } catch { return false; } } export function parseSaAttachDateWindowField(catalogs) { const match = /date-window flags now constrain `([^`]+)` directly/i.exec(catalogs.skill) || /date-window flags are applied to `([^`]+)`/i.exec(catalogs.scriptCatalog); if (!match) { throw new Error("Could not parse the SA Attach date-window field from references."); } return match[1]; } export function parsePrimaryCommercialSubjectArea(apiMap) { const match = /Primary commercial subject area:[\s\S]*?`([^`]+)`/i.exec(apiMap); if (!match) { throw new Error("Could not parse the primary commercial subject area from references/api-map.md."); } return match[1]; } export function parsePrimaryCommercialSourceName(apiMap) { const subjectArea = parsePrimaryCommercialSubjectArea(apiMap); const match = /\.'([^']+)'\)$/i.exec(subjectArea); if (!match) { throw new Error("Could not parse the primary commercial source name from references/api-map.md."); } return match[1]; } export function catalogSaysPreserveMixedRows(catalogs) { return /preserve every org-wide opportunity\/workload row/i.test(catalogs.skill); } export function catalogSaysApiMetricsAreRawRows(catalogs) { return /apiMetrics\.totalRowsPulledFromApi/i.test(catalogs.skill) && /Do not add local-cache-only rows/i.test(catalogs.skill); } export function splitCatalogCsv(value) { return String(value || "") .split(",") .map((item) => item.trim()) .filter(Boolean); } function findLine(text, marker) { const line = String(text) .split(/\r?\n/) .find((candidate) => candidate.includes(marker)); if (!line) { throw new Error(`Could not find "${marker}" in catalog text.`); } return line; } function toCamelKey(flagName) { return flagName.replace(/-([a-z])/g, (_, char) => char.toUpperCase()); } function normalizeWhitespace(value) { return String(value).replace(/\s+/g, " ").trim(); } function escapeRegExp(value) { return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }