191 lines
6.1 KiB
JavaScript
191 lines
6.1 KiB
JavaScript
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, "\\$&");
|
|
}
|