automatically reorder sections on agents.md by priority

This commit is contained in:
Pedro Rodrigues
2026-01-22 08:48:47 +00:00
parent a1fbd23d4b
commit 7b1a65007b
7 changed files with 238 additions and 194 deletions

View File

@@ -18,6 +18,10 @@ function parseSections(): Section[] {
const content = readFileSync(sectionsFile, "utf-8");
const sections: Section[] = [];
// Match format: Impact and Description on separate lines
// ## 1. Query Performance (query)
// **Impact:** CRITICAL
// **Description:** Description text
const sectionMatches = content.matchAll(
/##\s+(\d+)\.\s+([^\n(]+)\s*\((\w+)\)\s*\n\*\*Impact:\*\*\s*(\w+(?:-\w+)?)\s*\n\*\*Description:\*\*\s*([^\n]+)/g,
);
@@ -56,25 +60,25 @@ function getDefaultSections(): Section[] {
},
{
number: 3,
title: "Security & RLS",
prefix: "security",
impact: "CRITICAL",
description: "Row-Level Security, privileges, auth patterns",
},
{
number: 4,
title: "Schema Design",
prefix: "schema",
impact: "HIGH",
description: "Table design, indexes, partitioning, data types",
},
{
number: 4,
number: 5,
title: "Concurrency & Locking",
prefix: "lock",
impact: "MEDIUM-HIGH",
description: "Transactions, isolation, deadlocks",
},
{
number: 5,
title: "Security & RLS",
prefix: "security",
impact: "MEDIUM-HIGH",
description: "Row-Level Security, privileges, auth patterns",
},
{
number: 6,
title: "Data Access Patterns",
@@ -129,6 +133,19 @@ function toAnchor(text: string): string {
.replace(/\s+/g, "-");
}
/**
* Generate SECTION_MAP from parsed sections
*/
export function generateSectionMap(
sections: Section[],
): Record<string, number> {
const map: Record<string, number> = {};
for (const section of sections) {
map[section.prefix] = section.number;
}
return map;
}
/**
* Build AGENTS.md from all rule files
*/
@@ -138,6 +155,7 @@ function buildAgents(): void {
// Load metadata and sections
const metadata = loadMetadata();
const sections = parseSections();
const sectionMap = generateSectionMap(sections);
// Get all rule files
const ruleFiles = readdirSync(RULES_DIR)
@@ -152,7 +170,7 @@ function buildAgents(): void {
const rules: Rule[] = [];
for (const file of ruleFiles) {
const validation = validateRuleFile(file);
const validation = validateRuleFile(file, sectionMap);
if (!validation.valid) {
console.error(`Skipping invalid file ${basename(file)}:`);
for (const e of validation.errors) {
@@ -161,7 +179,7 @@ function buildAgents(): void {
continue;
}
const result = parseRuleFile(file);
const result = parseRuleFile(file, sectionMap);
if (result.success && result.rule) {
rules.push(result.rule);
}
@@ -303,4 +321,4 @@ if (isMainModule) {
buildAgents();
}
export { buildAgents };
export { buildAgents, parseSections };

View File

@@ -20,14 +20,17 @@ export const RULES_DIR = join(SKILL_DIR, "rules");
export const AGENTS_OUTPUT = join(SKILL_DIR, "AGENTS.md");
export const METADATA_FILE = join(SKILL_DIR, "metadata.json");
// Section prefix to number mapping
// Section prefix to number mapping (DEPRECATED)
// This is kept as a fallback, but the build system now generates
// the section map dynamically from _sections.md.
// To reorder sections, simply change the order in _sections.md.
export const SECTION_MAP: Record<string, number> = {
query: 1,
conn: 2,
connection: 2,
schema: 3,
lock: 4,
security: 5,
security: 3,
schema: 4,
lock: 5,
data: 6,
monitor: 7,
advanced: 8,

View File

@@ -1,6 +1,6 @@
import { readFileSync } from "node:fs";
import { basename } from "node:path";
import { IMPACT_LEVELS, SECTION_MAP } from "./config.js";
import { IMPACT_LEVELS } from "./config.js";
import type { CodeExample, ImpactLevel, ParseResult, Rule } from "./types.js";
/**
@@ -48,10 +48,13 @@ function parseFrontmatter(content: string): {
/**
* Extract section number from filename prefix
*/
function getSectionFromFilename(filename: string): number | null {
function getSectionFromFilename(
filename: string,
sectionMap: Record<string, number>,
): number | null {
const base = basename(filename, ".md");
const prefix = base.split("-")[0];
return SECTION_MAP[prefix] ?? null;
return sectionMap[prefix] ?? null;
}
/**
@@ -207,7 +210,10 @@ function extractReferences(body: string): string[] {
/**
* Parse a rule file and return structured data
*/
export function parseRuleFile(filePath: string): ParseResult {
export function parseRuleFile(
filePath: string,
sectionMap: Record<string, number>,
): ParseResult {
const errors: string[] = [];
const warnings: string[] = [];
@@ -216,7 +222,7 @@ export function parseRuleFile(filePath: string): ParseResult {
const { frontmatter, body } = parseFrontmatter(content);
// Extract section from filename
const section = getSectionFromFilename(filePath);
const section = getSectionFromFilename(filePath, sectionMap);
if (section === null) {
errors.push(
`Could not determine section from filename: ${basename(filePath)}`,

View File

@@ -1,5 +1,6 @@
import { readdirSync } from "node:fs";
import { basename, join } from "node:path";
import { generateSectionMap, parseSections } from "./build.js";
import { IMPACT_LEVELS, RULES_DIR } from "./config.js";
import { parseRuleFile } from "./parser.js";
import type { ValidationResult } from "./types.js";
@@ -34,11 +35,20 @@ function isGoodExample(label: string): boolean {
/**
* Validate a single rule file
*/
export function validateRuleFile(filePath: string): ValidationResult {
export function validateRuleFile(
filePath: string,
sectionMap?: Record<string, number>,
): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
const result = parseRuleFile(filePath);
// Generate section map if not provided
if (!sectionMap) {
const sections = parseSections();
sectionMap = generateSectionMap(sections);
}
const result = parseRuleFile(filePath, sectionMap);
// Add parser errors and warnings
errors.push(...result.errors);