core/fragment/eslint/no-inline-context-literal.eslint.rule.ts
core/fragment/eslint/no-inline-context-literal.eslint.rule.ts is a file in GovLab Quality. 46 lines of code and 8 definitions.
import type { Rule } from "eslint";
import { isNode } from "#core/predicates/context.fragment.predicate";
import { stringValue } from "#core/selectors/context.fragment.selector";
const SUFFIXES = ["_PROMPT", "_SECTION", "_INSTRUCTION", "_REMINDER", "_RULE", "_GOAL", "_DISCIPLINE"];
const isPromptPositionName = function isPromptPositionName(name: string): boolean {
return SUFFIXES.some((suffix) => name.endsWith(suffix));
};
const isContextLiteral = function isContextLiteral(init: unknown): boolean {
if (!isNode(init)) {
return false;
}
if (init.type === "TemplateLiteral" || stringValue(init) !== null) {
return true;
}
if (init.type === "ArrayExpression" && Array.isArray(init["elements"])) {
const { elements } = init;
return elements.length > 0 && elements.every((element) => stringValue(element) !== null);
}
return false;
};
export default {
create(context) {
const onDeclarator = function onDeclarator(node: Rule.Node): void {
if (node.type !== "VariableDeclarator" || node.id.type !== "Identifier") {
return;
}
const { name } = node.id;
if (isPromptPositionName(name) && isContextLiteral(node.init)) {
context.report({ data: { name }, messageId: "inline", node });
}
};
const handlers: [string, (node: Rule.Node) => void][] = [["VariableDeclarator", onDeclarator]];
return Object.fromEntries(handlers);
},
meta: {
docs: {
description:
"Disallow authoring AI-context prose as a frozen prompt-defining const — compose it as a defineContextFragment.",
},
messages: {
inline: "'{{name}}' authors AI context as a frozen const. Context prose belongs in a defineContextFragment (concern + applies + params), not a monolithic literal re-sent every turn. [no_inline_context_literal]",
},
schema: [],
type: "problem",
},
} satisfies Rule.RuleModule;