{"version":3,"sources":["../extension-src/pi-rules/app/index.ts","../extension-src/pi-rules/shared/fs.ts","../extension-src/pi-rules/shared/id.ts","../extension-src/pi-rules/shared/time.ts","../extension-src/pi-rules/shared/glob.ts","../extension-src/pi-rules/shared/path.ts","../extension-src/pi-rules/domain/ordering.ts","../extension-src/pi-rules/domain/matcher.ts","../extension-src/pi-rules/domain/truncator.ts","../extension-src/pi-rules/domain/formatter.ts","../extension-src/pi-rules/shared/frontmatter.ts","../extension-src/pi-rules/shared/hash.ts","../extension-src/pi-rules/domain/errors.ts","../extension-src/pi-rules/domain/parser.ts","../extension-src/pi-rules/domain/scanner.ts","../extension-src/pi-rules/domain/project-root.ts","../extension-src/pi-rules/domain/engine.ts","../extension-src/pi-rules/app/config.ts","../extension-src/pi-rules/app/constants.ts","../extension-src/pi-rules/app/state.ts"],"sourcesContent":["export * from \"../domain/engine.js\";\nexport * from \"../domain/types.js\";\nexport * from \"./config.js\";\nexport * from \"./state.js\";\n","import { existsSync, statSync } from \"node:fs\";\nimport { appendFile, mkdir, readdir, readFile, realpath, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\n\nexport async function ensureDirectory(path: string): Promise<void> {\n\tawait mkdir(path, { recursive: true });\n}\n\nexport async function readTextFile(path: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await readFile(path, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport async function writeTextFile(path: string, content: string): Promise<void> {\n\tawait ensureDirectory(dirname(path));\n\tawait writeFile(path, content, \"utf8\");\n}\n\nexport async function appendTextFile(path: string, content: string): Promise<void> {\n\tawait ensureDirectory(dirname(path));\n\tawait appendFile(path, content, \"utf8\");\n}\n\nexport async function readJsonFile<T>(path: string, fallback: T): Promise<T> {\n\tconst content = await readTextFile(path);\n\tif (content === undefined) {\n\t\treturn fallback;\n\t}\n\ttry {\n\t\treturn JSON.parse(content) as T;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport async function writeJsonFile(path: string, value: unknown): Promise<void> {\n\tawait writeTextFile(path, `${JSON.stringify(value, null, 2)}\\n`);\n}\n\nexport async function listFilesRecursive(rootDir: string): Promise<string[]> {\n\tconst results: string[] = [];\n\tif (!existsSync(rootDir)) {\n\t\treturn results;\n\t}\n\tconst entries = await readdir(rootDir, { withFileTypes: true });\n\tfor (const entry of entries) {\n\t\tconst absolutePath = resolve(rootDir, entry.name);\n\t\tif (entry.isDirectory()) {\n\t\t\tresults.push(...(await listFilesRecursive(absolutePath)));\n\t\t\tcontinue;\n\t\t}\n\t\tresults.push(absolutePath);\n\t}\n\treturn results.sort((left, right) => left.localeCompare(right));\n}\n\nexport async function exists(path: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(path);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function removeFile(path: string): Promise<void> {\n\tawait rm(path, { force: true });\n}\n\nexport async function resolveRealPath(path: string): Promise<string> {\n\ttry {\n\t\treturn await realpath(path);\n\t} catch {\n\t\treturn path;\n\t}\n}\n\n/**\n * Compute a stat-based fingerprint for a file without reading its contents.\n * Uses mtimeNs, ctimeNs, and size. Returns \"missing\" when the file does not exist.\n */\nexport function fileStatFingerprint(filePath: string): string {\n\ttry {\n\t\tconst stats = statSync(filePath, { bigint: true });\n\t\treturn `${stats.mtimeNs}:${stats.ctimeNs}:${stats.size}`;\n\t} catch {\n\t\treturn \"missing\";\n\t}\n}\n","export function createId(prefix: string): string {\n\treturn `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n","export function now(): number {\n\treturn Date.now();\n}\n\nexport function toIsoDate(timestamp: number = Date.now()): string {\n\treturn new Date(timestamp).toISOString();\n}\n","/**\n * Zero-dependency glob pattern matcher.\n *\n * Supports the subset of glob syntax used in .pi/rules paths:\n * - * matches any characters except /\n * - ** matches any characters including / (recursive)\n * - ? matches exactly one character except /\n * - {a,b} matches either a or b (alternatives)\n * - Dot files are matched by default (consistent with picomatch dot:true)\n */\n\n/**\n * Test whether a file path matches a glob pattern.\n *\n * Both path and pattern are normalized (backslashes become forward slashes,\n * leading ./ and / stripped).\n *\n * @param filePath - Project-relative file path (e.g. src/index.ts)\n * @param pattern - Glob pattern (e.g. src/**\\/*.ts)\n * @returns true if the path matches the pattern\n */\nexport function globMatch(filePath: string, pattern: string): boolean {\n\tconst normalizedPath = normalizeGlobPath(filePath);\n\tconst normalizedPattern = normalizeGlobPath(pattern);\n\n\ttry {\n\t\tconst regex = globToRegex(normalizedPattern);\n\t\treturn regex.test(normalizedPath);\n\t} catch {\n\t\t// Invalid pattern means no match\n\t\treturn false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a path for glob matching:\n * - Backslashes become forward slashes\n * - Strip leading ./\n * - Strip leading /\n * - Strip trailing /\n */\nfunction normalizeGlobPath(value: string): string {\n\treturn value.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").replace(/^\\//, \"\").replace(/\\/$/, \"\");\n}\n\n/**\n * Convert a glob pattern string into a RegExp.\n *\n * Supports *, **, ?, and {a,b} alternatives.\n */\nfunction globToRegex(pattern: string): RegExp {\n\tlet regex = \"\";\n\tlet i = 0;\n\n\twhile (i < pattern.length) {\n\t\tconst ch = pattern[i];\n\t\tconst next = pattern[i + 1];\n\t\tconst afterNext = pattern[i + 2];\n\n\t\t// **/ means match any directory prefix (including none)\n\t\tif (ch === \"*\" && next === \"*\" && afterNext === \"/\") {\n\t\t\tregex += \"(?:.*?/)?\";\n\t\t\ti += 3;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ** at end or followed by non-/ means match anything\n\t\tif (ch === \"*\" && next === \"*\") {\n\t\t\tregex += \".*\";\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// * means match anything except /\n\t\tif (ch === \"*\") {\n\t\t\tregex += \"[^/]*\";\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// ? means match one char except /\n\t\tif (ch === \"?\") {\n\t\t\tregex += \"[^/]\";\n\t\t\ti += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// {a,b} means alternatives\n\t\tif (ch === \"{\") {\n\t\t\tconst closeIndex = pattern.indexOf(\"}\", i + 1);\n\t\t\tif (closeIndex !== -1) {\n\t\t\t\tconst alternatives = pattern.slice(i + 1, closeIndex).split(\",\");\n\t\t\t\tregex += \"(?:\" + alternatives.map(escapeRegex).join(\"|\") + \")\";\n\t\t\t\ti = closeIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Literal character (escape regex special chars)\n\t\tregex += escapeRegexChar(ch);\n\t\ti += 1;\n\t}\n\n\treturn new RegExp(`^${regex}$`);\n}\n\n/**\n * Escape all regex-special characters in a string.\n */\nfunction escapeRegex(str: string): string {\n\treturn str.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Escape a single character for use in a regex.\n */\nfunction escapeRegexChar(ch: string): string {\n\tif (/[.+^${}()|[\\]\\\\/]/.test(ch)) {\n\t\treturn `\\\\${ch}`;\n\t}\n\treturn ch;\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve } from \"node:path\";\n\nexport function normalizePath(input: string): string {\n\treturn input.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n}\n\nexport function toAbsolutePath(root: string, inputPath: string): string {\n\treturn isAbsolute(inputPath) ? resolve(inputPath) : resolve(root, inputPath);\n}\n\nexport function toRelativeProjectPath(projectRoot: string, inputPath: string): string {\n\treturn normalizePath(relative(projectRoot, toAbsolutePath(projectRoot, inputPath)));\n}\n\nexport function isSubPath(parentPath: string, childPath: string): boolean {\n\tconst relativePath = relative(resolve(parentPath), resolve(childPath));\n\treturn relativePath === \"\" || (!relativePath.startsWith(\"..\") && !isAbsolute(relativePath));\n}\n\nexport function findAncestorContaining(startDir: string, entryName: string): string | undefined {\n\tlet currentDir = resolve(startDir);\n\n\tfor (;;) {\n\t\tif (existsSync(resolve(currentDir, entryName))) {\n\t\t\treturn currentDir;\n\t\t}\n\t\tconst parentDir = dirname(currentDir);\n\t\tif (parentDir === currentDir) {\n\t\t\treturn undefined;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\n/**\n * Backward-compat wrapper preserved for callers that still import this from\n * `shared/path`. The enhanced, configurable implementation lives in\n * `domain/project-root.ts`; this version hard-codes the original marker\n * list and falls back to `startDir` when no marker is found.\n */\nexport function findProjectRoot(startDir: string): string {\n\treturn (\n\t\tfindAncestorContaining(startDir, \".git\") ??\n\t\tfindAncestorContaining(startDir, \".pi\") ??\n\t\tfindAncestorContaining(startDir, \"package.json\") ??\n\t\tresolve(startDir)\n\t);\n}\n\nexport function extractPathCandidatesFromText(input: string): string[] {\n\tconst matches = input.match(/(?:[A-Za-z0-9_.-]+\\/)+[A-Za-z0-9_.-]+|[A-Za-z0-9_.-]+\\.[A-Za-z0-9_-]+/g) ?? [];\n\treturn [...new Set(matches.map((match) => normalizePath(match)).filter(Boolean))];\n}\n","import type { ParsedRule } from \"./types.js\";\n\n/**\n * Comparator that orders {@link ParsedRule} entries for stable presentation\n * in injected context blocks.\n *\n * Sort key (highest precedence first):\n *\n *  1. `frontmatter.priority` descending. Rules with a higher numeric\n *     priority sort first. Rules without an explicit priority sort as\n *     `0`, equal to any other priority-less rule.\n *  2. Path depth ascending. Rules with a shorter `relativePath` sort\n *     first, so general rules (`.pi/rules/general.md`) appear before\n *     specific ones (`.pi/rules/src/auth/oauth.md`). Depth is the\n *     number of non-empty path segments.\n *  3. `relativePath` ascending (alphabetical) as the final tiebreaker.\n */\nexport function compareRulesByPriority(a: ParsedRule, b: ParsedRule): number {\n\tconst priorityDiff = (b.frontmatter.priority ?? 0) - (a.frontmatter.priority ?? 0);\n\tif (priorityDiff !== 0) return priorityDiff;\n\n\tconst depthDiff = pathDepth(a.relativePath) - pathDepth(b.relativePath);\n\tif (depthDiff !== 0) return depthDiff;\n\n\treturn a.relativePath.localeCompare(b.relativePath);\n}\n\n/**\n * Return a new array of {@link ParsedRule} entries sorted by\n * {@link compareRulesByPriority}. The input array is not mutated, so\n * callers can safely pass in shared / readonly rule lists.\n */\nexport function sortRules(rules: ReadonlyArray<ParsedRule>): ParsedRule[] {\n\treturn [...rules].sort(compareRulesByPriority);\n}\n\nfunction pathDepth(relativePath: string): number {\n\tconst normalized = relativePath.replaceAll(\"\\\\\", \"/\");\n\tlet depth = 0;\n\tlet inSegment = false;\n\tfor (let i = 0; i < normalized.length; i++) {\n\t\tconst code = normalized.charCodeAt(i);\n\t\tconst isSeparator = code === 47; // \"/\"\n\t\tif (!isSeparator && !inSegment) {\n\t\t\tdepth++;\n\t\t\tinSegment = true;\n\t\t} else if (isSeparator) {\n\t\t\tinSegment = false;\n\t\t}\n\t}\n\treturn depth;\n}\n","import { globMatch } from \"../shared/glob.js\";\nimport { normalizePath } from \"../shared/path.js\";\nimport { compareRulesByPriority } from \"./ordering.js\";\nimport type { MatchedRule, MatchReason, ParsedRule } from \"./types.js\";\n\nexport function matchRules(rules: ParsedRule[], targetPaths: string[]): MatchedRule[] {\n\tconst normalizedTargets = [...new Set(targetPaths.map((path) => normalizePath(path)).filter(Boolean))];\n\tconst matches: MatchedRule[] = [];\n\n\tfor (const rule of rules) {\n\t\tif (rule.frontmatter.alwaysApply === true) {\n\t\t\tmatches.push({ ...rule, matchReason: { type: \"alwaysApply\" } });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst patterns = normalizePatterns(rule.frontmatter.paths);\n\t\tfor (const targetPath of normalizedTargets) {\n\t\t\tconst matchedPattern = patterns.find((pattern) => safeMatch(pattern, targetPath));\n\t\t\tif (matchedPattern !== undefined) {\n\t\t\t\tmatches.push({\n\t\t\t\t\t...rule,\n\t\t\t\t\tmatchReason: { type: \"path\", targetPath, pattern: matchedPattern },\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn matches.sort(compareRulesByPriority);\n}\n\nfunction normalizePatterns(paths: string | string[] | undefined): string[] {\n\tif (paths === undefined) return [];\n\tconst values = Array.isArray(paths) ? paths : [paths];\n\treturn values.map((value) => normalizePath(value));\n}\n\nfunction safeMatch(pattern: string, targetPath: string): boolean {\n\ttry {\n\t\treturn globMatch(targetPath, pattern);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function matchByTriggers(rules: ParsedRule[], promptText: string): MatchedRule[] {\n\tconst promptLower = promptText.toLowerCase();\n\tconst matches: MatchedRule[] = [];\n\tfor (const rule of rules) {\n\t\tif (!rule.frontmatter.triggers) continue;\n\t\tfor (const trigger of rule.frontmatter.triggers) {\n\t\t\tif (promptLower.includes(trigger.toLowerCase())) {\n\t\t\t\tmatches.push({ ...rule, matchReason: { type: \"trigger\", trigger } });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn matches.sort(compareRulesByPriority);\n}\n\nexport function describeMatchReason(matchReason: MatchReason): string {\n\tif (matchReason.type === \"alwaysApply\") {\n\t\treturn \"alwaysApply: true\";\n\t}\n\tif (matchReason.type === \"trigger\") {\n\t\treturn `trigger: \"${matchReason.trigger}\"`;\n\t}\n\treturn `${matchReason.targetPath} matched ${JSON.stringify(matchReason.pattern)}`;\n}\n","/**\n * Truncation helpers for rule bodies and rule-context blocks.\n *\n * Two responsibilities:\n *\n *  - {@link truncateRuleBody} keeps a single rule body within a per-rule char\n *    budget. The slice is surrogate-pair safe so multibyte emoji and\n *    non-BMP code points are never split in half.\n *  - {@link truncateBudget} fits many rule bodies into a single context\n *    block. Earlier rules claim their full body, later rules are clipped or\n *    dropped when the running budget is exhausted.\n *\n * Both helpers accept a configurable notice template. The placeholder\n * `{path}` is replaced with the rule's relative path so the model can find\n * the full file on disk.\n */\n\n/**\n * Default notice template. `{path}` is replaced with the rule's relative\n * path; callers can override the wording by passing a custom `noticeTemplate`.\n */\nexport const DEFAULT_TRUNCATION_NOTICE = \"\\n\\n[... truncated by pi-rules: {path} ...]\";\n\n/**\n * Truncation result. `body` is the (possibly clipped) text. `truncated` is\n * `true` when the input did not fit and was clipped. `originalLength` is\n * always the input length in UTF-16 code units, regardless of truncation.\n */\nexport interface TruncationResult {\n\tbody: string;\n\ttruncated: boolean;\n\toriginalLength: number;\n}\n\nexport interface TruncateRuleOptions {\n\t/** Maximum number of UTF-16 code units the result is allowed to occupy. */\n\tmaxChars: number;\n\t/** Rule path inserted into the notice template. */\n\trelativePath: string;\n\t/**\n\t * Optional notice template override. The literal substring `{path}` is\n\t * replaced with `relativePath`. Defaults to\n\t * {@link DEFAULT_TRUNCATION_NOTICE}.\n\t */\n\tnoticeTemplate?: string;\n}\n\nexport interface TruncateBudgetRule {\n\tbody: string;\n\trelativePath: string;\n}\n\nexport interface TruncatedBudgetRule {\n\tbody: string;\n\ttruncated: boolean;\n\trelativePath: string;\n}\n\nexport interface TruncateBudgetOptions {\n\trules: ReadonlyArray<TruncateBudgetRule>;\n\t/** Total character budget shared across all rules in `rules`. */\n\tmaxResultChars: number;\n\t/** Optional notice template override (see {@link TruncateRuleOptions}). */\n\tnoticeTemplate?: string;\n}\n\nfunction renderNotice(template: string, relativePath: string): string {\n\treturn template.replaceAll(\"{path}\", relativePath);\n}\n\n/**\n * Return a slice end index that does not split a UTF-16 surrogate pair.\n *\n * JS strings are sequences of UTF-16 code units. Emoji and other non-BMP\n * code points are encoded as a high surrogate (`0xD800..0xDBFF`) followed by\n * a low surrogate (`0xDC00..0xDFFF`). Slicing between them produces an\n * invalid string that can corrupt downstream output; this helper backs off\n * one code unit when the boundary lands on a high surrogate.\n */\nfunction safeSliceEnd(body: string, end: number): number {\n\tif (end <= 0) {\n\t\treturn 0;\n\t}\n\tconst lastCodeUnit = body.charCodeAt(end - 1);\n\tif (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff) {\n\t\treturn end - 1;\n\t}\n\treturn end;\n}\n\n/**\n * Clip a single rule body to at most `maxChars` code units. When clipping is\n * required, the result is the clipped prefix + a notice indicating where\n * the full rule can be read.\n */\nexport function truncateRuleBody(body: string, options: TruncateRuleOptions): TruncationResult {\n\tconst noticeTemplate = options.noticeTemplate ?? DEFAULT_TRUNCATION_NOTICE;\n\tconst notice = renderNotice(noticeTemplate, options.relativePath);\n\n\tif (body.length <= options.maxChars) {\n\t\treturn { body, truncated: false, originalLength: body.length };\n\t}\n\n\tif (options.maxChars <= 0) {\n\t\treturn { body: \"\", truncated: true, originalLength: body.length };\n\t}\n\n\tif (options.maxChars < notice.length) {\n\t\t// Notice alone does not fit; surface it so the model can still discover\n\t\t// the full file location instead of receiving a silent empty body.\n\t\treturn { body: notice, truncated: true, originalLength: body.length };\n\t}\n\n\tconst sliceEnd = safeSliceEnd(body, options.maxChars - notice.length);\n\treturn { body: `${body.slice(0, sliceEnd)}${notice}`, truncated: true, originalLength: body.length };\n}\n\n/**\n * Fit an ordered list of rule bodies into a shared char budget. Earlier\n * rules are kept whole whenever they fit; later rules are clipped (with a\n * notice) or dropped once the running total exceeds `maxResultChars`. The\n * returned array preserves the input order; rules that did not fit are\n * simply absent from the result.\n */\nexport function truncateBudget(options: TruncateBudgetOptions): TruncatedBudgetRule[] {\n\tconst noticeTemplate = options.noticeTemplate ?? DEFAULT_TRUNCATION_NOTICE;\n\tconst results: TruncatedBudgetRule[] = [];\n\tlet remaining = options.maxResultChars;\n\n\tfor (const rule of options.rules) {\n\t\tif (remaining <= 0) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (rule.body.length <= remaining) {\n\t\t\tresults.push({ body: rule.body, truncated: false, relativePath: rule.relativePath });\n\t\t\tremaining -= rule.body.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst notice = renderNotice(noticeTemplate, rule.relativePath);\n\t\tif (remaining <= notice.length) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst sliceEnd = safeSliceEnd(rule.body, remaining - notice.length);\n\t\tconst body = `${rule.body.slice(0, sliceEnd)}${notice}`;\n\t\tresults.push({ body, truncated: true, relativePath: rule.relativePath });\n\t\tremaining -= body.length;\n\t}\n\n\treturn results;\n}\n","import { describeMatchReason } from \"./matcher.js\";\nimport { truncateRuleBody } from \"./truncator.js\";\nimport type { MatchedRule } from \"./types.js\";\n\nexport interface FormatOptions {\n\tmaxRuleChars: number;\n\tmaxContextChars: number;\n}\n\nexport interface FormattedRuleContext {\n\tprompt: string;\n\ttruncated: boolean;\n}\n\n/**\n * Separate matches into children (deeper paths) and parents (shallower paths).\n * A rule is a \"parent\" if its relativePath (without filename) is a prefix of\n * another rule's relativePath. Children are injected fully; parents get only\n * a summary paragraph.\n */\nexport function separateParentChild(matches: MatchedRule[]): { children: MatchedRule[]; parents: MatchedRule[] } {\n\tif (matches.length <= 1) return { children: matches, parents: [] };\n\n\tconst pathDirs = matches.map((m) => {\n\t\tconst normalized = m.relativePath.replaceAll(\"\\\\\", \"/\");\n\t\tconst lastSlash = normalized.lastIndexOf(\"/\");\n\t\treturn lastSlash >= 0 ? normalized.slice(0, lastSlash + 1) : \"\";\n\t});\n\n\tconst isParent = new Array<boolean>(matches.length).fill(false);\n\n\tfor (let i = 0; i < matches.length; i++) {\n\t\tfor (let j = 0; j < matches.length; j++) {\n\t\t\tif (i === j) continue;\n\t\t\t// Rule i is a parent of rule j if rule i's directory is a prefix of rule j's path\n\t\t\tif (pathDirs[i]!.length > 0 && matches[j]!.relativePath.startsWith(pathDirs[i]!) && pathDirs[i] !== pathDirs[j]) {\n\t\t\t\tisParent[i] = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst children: MatchedRule[] = [];\n\tconst parents: MatchedRule[] = [];\n\tfor (let i = 0; i < matches.length; i++) {\n\t\tif (isParent[i]) {\n\t\t\tparents.push(matches[i]!);\n\t\t} else {\n\t\t\tchildren.push(matches[i]!);\n\t\t}\n\t}\n\treturn { children, parents };\n}\n\nfunction extractFirstParagraph(body: string): string {\n\tconst lines = body.split(\"\\n\");\n\tconst paragraph: string[] = [];\n\tfor (const line of lines) {\n\t\tif (line.trim() === \"\" && paragraph.length > 0) break;\n\t\tparagraph.push(line);\n\t}\n\treturn paragraph.join(\"\\n\").trim();\n}\n\nexport function formatRuleContext(matches: MatchedRule[], options: FormatOptions): FormattedRuleContext {\n\tif (matches.length === 0) {\n\t\treturn { prompt: \"\", truncated: false };\n\t}\n\n\t// Separate rules from inventory\n\tconst rulesMatches = matches.filter((m) => m.frontmatter.kind !== \"inventory\");\n\tconst inventoryMatches = matches.filter((m) => m.frontmatter.kind === \"inventory\");\n\n\t// Group rules into parent/child for 2-tier injection\n\tconst { children, parents } = separateParentChild(rulesMatches);\n\n\tconst sections: string[] = [\n\t\t\"<pi-rules-context>\",\n\t\t\"The following project rules apply to this turn. Follow them when working with matching files.\",\n\t];\n\tlet totalChars = sections.join(\"\\n\").length;\n\tlet truncated = false;\n\n\t// Inject children fully\n\tfor (const match of children) {\n\t\tconst truncation = truncateRuleBody(match.body, {\n\t\t\tmaxChars: options.maxRuleChars,\n\t\t\trelativePath: match.relativePath,\n\t\t});\n\t\tif (truncation.truncated) truncated = true;\n\t\tconst block = [\n\t\t\t`## Rule: ${match.relativePath}`,\n\t\t\t`Matched because: ${describeMatchReason(match.matchReason)}`,\n\t\t\tmatch.frontmatter.summary ? `Summary: ${match.frontmatter.summary}` : undefined,\n\t\t\t\"\",\n\t\t\ttruncation.body,\n\t\t]\n\t\t\t.filter((value) => value !== undefined)\n\t\t\t.join(\"\\n\");\n\n\t\tconst projected = totalChars + block.length + 2;\n\t\tif (projected > options.maxContextChars) {\n\t\t\ttruncated = true;\n\t\t\tbreak;\n\t\t}\n\t\tsections.push(block);\n\t\ttotalChars = projected;\n\t}\n\n\t// Inject parents as summary only\n\tfor (const match of parents) {\n\t\tconst summaryText = match.frontmatter.summary ?? extractFirstParagraph(match.body);\n\t\tconst block = [\n\t\t\t`## Rule: ${match.relativePath} (parent summary)`,\n\t\t\t`Matched because: ${describeMatchReason(match.matchReason)}`,\n\t\t\t`Summary: ${summaryText}`,\n\t\t\t\"\",\n\t\t\t\"[Full rule available; read the file for complete details]\",\n\t\t]\n\t\t\t.filter((value) => value !== undefined)\n\t\t\t.join(\"\\n\");\n\n\t\tconst projected = totalChars + block.length + 2;\n\t\tif (projected > options.maxContextChars) {\n\t\t\ttruncated = true;\n\t\t\tbreak;\n\t\t}\n\t\tsections.push(block);\n\t\ttotalChars = projected;\n\t}\n\n\t// Inventory listing\n\tif (inventoryMatches.length > 0) {\n\t\tconst invLines = [\"## Available Inventories\"];\n\t\tfor (const inv of inventoryMatches) {\n\t\t\tconst label = inv.frontmatter.summary ? ` (${inv.frontmatter.summary})` : \"\";\n\t\t\tinvLines.push(`- ${inv.relativePath}${label}`);\n\t\t}\n\t\tconst invBlock = invLines.join(\"\\n\");\n\t\tconst projected = totalChars + invBlock.length + 2;\n\t\tif (projected <= options.maxContextChars) {\n\t\t\tsections.push(invBlock);\n\t\t\ttotalChars = projected;\n\t\t} else {\n\t\t\ttruncated = true;\n\t\t}\n\t}\n\n\tsections.push(\"</pi-rules-context>\");\n\treturn { prompt: sections.join(\"\\n\\n\"), truncated };\n}\n","/**\n * Zero-dependency YAML frontmatter parser.\n *\n * Supports the subset of YAML used in .pi/rules files:\n * - Scalar strings (quoted and unquoted)\n * - Booleans (true/false)\n * - Numbers (integers and floats)\n * - Block lists (- item)\n * - Comma-separated inline lists ([a, b, c] or a, b, c)\n */\n\n/** Parsed frontmatter data as a plain record. */\nexport type FrontmatterData = Record<string, unknown>;\n\n/** Result of parsing frontmatter from content. */\nexport interface FrontmatterResult {\n\t/** Parsed frontmatter key-value pairs. Empty object if no frontmatter found. */\n\tdata: FrontmatterData;\n\t/** Body content after the frontmatter delimiter. */\n\tcontent: string;\n}\n\n/**\n * Parse YAML frontmatter from markdown content.\n *\n * Expects content to start with --- delimiter. If no frontmatter is found,\n * returns empty data and the original content as-is.\n */\nexport function parseFrontmatter(content: string): FrontmatterResult {\n\tif (!content.startsWith(\"---\")) {\n\t\treturn { data: {}, content };\n\t}\n\n\tconst endIndex = content.indexOf(\"---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { data: {}, content };\n\t}\n\n\tconst rawBlock = content.slice(3, endIndex);\n\tconst body = content.slice(endIndex + 3).trim();\n\tconst data = parseYamlBlock(rawBlock);\n\n\treturn { data, content: body };\n}\n\n// ---------------------------------------------------------------------------\n// Internal YAML parser (minimal subset)\n// ---------------------------------------------------------------------------\n\nfunction parseYamlBlock(block: string): FrontmatterData {\n\tconst result: FrontmatterData = {};\n\tlet currentListKey: string | undefined;\n\n\tfor (const rawLine of block.split(/\\r?\\n/)) {\n\t\tconst line = rawLine.replace(/\\s+$/, \"\");\n\n\t\t// List item: \"  - value\"\n\t\tconst listItemMatch = line.match(/^\\s+-\\s+(.+?)\\s*$/);\n\t\tif (listItemMatch && currentListKey) {\n\t\t\tconst existing = result[currentListKey];\n\t\t\tif (Array.isArray(existing)) {\n\t\t\t\texisting.push(parseScalar(stripQuotes(listItemMatch[1])));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Key-value: \"key: value\" or \"key:\" (empty value = start of list)\n\t\tconst kvMatch = line.match(/^([A-Za-z0-9_-]+):\\s*(.*?)\\s*$/);\n\t\tif (kvMatch) {\n\t\t\tconst [, key, rawValue] = kvMatch;\n\n\t\t\tif (rawValue === \"\") {\n\t\t\t\t// Empty value means start a list\n\t\t\t\tresult[key] = [];\n\t\t\t\tcurrentListKey = key;\n\t\t\t} else {\n\t\t\t\t// Inline scalar or inline list\n\t\t\t\tresult[key] = parseValue(rawValue);\n\t\t\t\tcurrentListKey = undefined;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Non-indented non-empty line means reset list context\n\t\tif (/^\\S/.test(line) && line.length > 0) {\n\t\t\tcurrentListKey = undefined;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Parse a YAML value string into a JS value.\n *\n * Handles: booleans, numbers, inline arrays [a, b], comma-separated lists,\n * and quoted/unquoted strings.\n *\n * Throws on clearly invalid syntax (e.g. unclosed brackets).\n */\nfunction parseValue(rawValue: string): unknown {\n\tconst trimmed = rawValue.trim();\n\n\t// Inline array: [a, b, c]\n\tif (trimmed.startsWith(\"[\")) {\n\t\tif (!trimmed.endsWith(\"]\")) {\n\t\t\tthrow new Error(`Invalid YAML: unclosed bracket in \"${trimmed}\"`);\n\t\t}\n\t\tconst inner = trimmed.slice(1, -1);\n\t\tif (inner.trim() === \"\") return [];\n\t\treturn inner.split(\",\").map((item) => parseScalar(stripQuotes(item.trim())));\n\t}\n\n\t// Comma-separated list (only if contains unquoted commas and no quotes)\n\tif (trimmed.includes(\",\") && !trimmed.startsWith('\"') && !trimmed.startsWith(\"'\")) {\n\t\tconst items = trimmed.split(\",\").map((item) => parseScalar(stripQuotes(item.trim())));\n\t\tif (items.length > 1) return items;\n\t}\n\n\treturn parseScalar(trimmed);\n}\n\n/**\n * Parse a scalar value: boolean, number, or string.\n *\n * Strips surrounding quotes from string values.\n */\nfunction parseScalar(value: string): unknown {\n\t// Strip quotes first\n\tconst unquoted = stripQuotes(value);\n\n\tif (unquoted === \"true\") return true;\n\tif (unquoted === \"false\") return false;\n\tif (unquoted === \"null\" || unquoted === \"~\" || unquoted === \"\") return null;\n\n\t// Integer\n\tconst intMatch = unquoted.match(/^-?\\d+$/);\n\tif (intMatch) {\n\t\tconst num = Number.parseInt(unquoted, 10);\n\t\tif (Number.isSafeInteger(num)) return num;\n\t}\n\n\t// Float\n\tconst floatMatch = unquoted.match(/^-?\\d+\\.\\d+$/);\n\tif (floatMatch) {\n\t\tconst num = Number.parseFloat(unquoted);\n\t\tif (Number.isFinite(num)) return num;\n\t}\n\n\treturn unquoted;\n}\n\n/**\n * Strip surrounding single or double quotes from a value.\n */\nfunction stripQuotes(value: string): string {\n\treturn value.replace(/^['\"]|['\"]$/g, \"\");\n}\n","import { createHash } from \"node:crypto\";\n\nexport function sha256(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n","/**\n * Domain-level error types raised by the pi-rules parser, scanner, and\n * engine layers. Callers can `instanceof` these to react specifically to\n * rule-loading failures without conflating them with generic I/O or\n * configuration errors.\n */\n\n/**\n * Raised when a rule file's YAML frontmatter cannot be parsed at all\n * (i.e. the file is not a valid frontmatter document). Distinct from\n * `RuleDiagnosticError` because frontmatter parse failures indicate the\n * rule itself is unreadable, not merely misconfigured.\n */\nexport class RuleParseError extends Error {\n\treadonly filePath: string;\n\n\tconstructor(filePath: string, cause: unknown) {\n\t\tsuper(`Failed to parse rule: ${filePath}${cause instanceof Error ? `: ${cause.message}` : \"\"}`);\n\t\tthis.name = \"RuleParseError\";\n\t\tthis.filePath = filePath;\n\t}\n}\n\n/**\n * Raised for a soft failure observed while processing a single rule:\n * malformed frontmatter values, missing fields, etc. These mirror\n * {@link RuleDiagnostic} entries but as a thrown error so non-default\n * callers (commands, tests, scripts) can opt into strict handling.\n *\n * `severity` mirrors the `RuleDiagnostic.severity` union and `rulePath`\n * is `undefined` when the diagnostic is not associated with a specific\n * file.\n */\nexport class RuleDiagnosticError extends Error {\n\treadonly severity: \"warning\" | \"error\";\n\treadonly rulePath: string | undefined;\n\n\tconstructor(severity: \"warning\" | \"error\", rulePath: string | undefined, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RuleDiagnosticError\";\n\t\tthis.severity = severity;\n\t\tthis.rulePath = rulePath;\n\t}\n}\n\n/**\n * Raised when the project-root discovery walk cannot locate a marker\n * before reaching the filesystem root. Hosts that tolerate a fallback\n * (e.g. using the input path directly) should catch this and proceed;\n * strict callers can re-raise.\n */\nexport class ProjectRootNotFoundError extends Error {\n\treadonly startPath: string;\n\n\tconstructor(startPath: string) {\n\t\tsuper(`Project root not found from: ${startPath}`);\n\t\tthis.name = \"ProjectRootNotFoundError\";\n\t\tthis.startPath = startPath;\n\t}\n}\n","import { parseFrontmatter } from \"../shared/frontmatter.js\";\nimport { sha256 } from \"../shared/hash.js\";\nimport { RuleParseError } from \"./errors.js\";\nimport type { ParsedRule, RuleDiagnostic, RuleFile, RuleFrontmatter } from \"./types.js\";\n\nexport function parseRuleFile(ruleFile: RuleFile, content: string): ParsedRule {\n\tconst diagnostics: RuleDiagnostic[] = [];\n\tlet frontmatter: RuleFrontmatter = {};\n\tlet body = content;\n\n\ttry {\n\t\tconst parsed = parseFrontmatter(content);\n\t\tfrontmatter = normalizeFrontmatter(parsed.data, diagnostics, ruleFile.relativePath);\n\t\tbody = parsed.content;\n\t} catch (error) {\n\t\tthrow new RuleParseError(ruleFile.relativePath, error);\n\t}\n\n\treturn {\n\t\t...ruleFile,\n\t\tcontentHash: sha256(content),\n\t\tfrontmatter,\n\t\tbody,\n\t\tdiagnostics,\n\t};\n}\n\nfunction normalizeFrontmatter(\n\tinput: Record<string, unknown>,\n\tdiagnostics: RuleDiagnostic[],\n\trulePath: string,\n): RuleFrontmatter {\n\tconst frontmatter: RuleFrontmatter = {};\n\n\tif (typeof input.summary === \"string\") frontmatter.summary = input.summary;\n\tif (typeof input.alwaysApply === \"boolean\") frontmatter.alwaysApply = input.alwaysApply;\n\tif (typeof input.description === \"string\") frontmatter.description = input.description;\n\tif (typeof input.priority === \"number\") frontmatter.priority = input.priority;\n\tif (typeof input.guard === \"boolean\") frontmatter.guard = input.guard;\n\n\tfrontmatter.paths = normalizeStringOrArray(input.paths, \"paths\", diagnostics, rulePath);\n\tfrontmatter.paths = normalizeStringOrArray(input.paths, \"paths\", diagnostics, rulePath);\n\tfrontmatter.triggers = normalizeStringArray(input.triggers, \"triggers\", diagnostics, rulePath);\n\n\tif (input.kind !== undefined) {\n\t\tif (input.kind === \"rules\" || input.kind === \"inventory\") {\n\t\t\tfrontmatter.kind = input.kind;\n\t\t} else {\n\t\t\tdiagnostics.push({\n\t\t\t\tseverity: \"warning\",\n\t\t\t\trulePath,\n\t\t\t\tmessage: `Ignoring invalid kind; expected \"rules\" or \"inventory\", got \"${String(input.kind)}\"`,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn frontmatter;\n}\n\nfunction normalizeStringOrArray(\n\tvalue: unknown,\n\tfieldName: string,\n\tdiagnostics: RuleDiagnostic[],\n\trulePath: string,\n): string | string[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (typeof value === \"string\") return value;\n\tif (Array.isArray(value) && value.every((item) => typeof item === \"string\")) {\n\t\treturn value as string[];\n\t}\n\tdiagnostics.push({\n\t\tseverity: \"warning\",\n\t\trulePath,\n\t\tmessage: `Ignoring invalid ${fieldName}; expected string or string[]`,\n\t});\n\treturn undefined;\n}\n\nfunction normalizeStringArray(\n\tvalue: unknown,\n\tfieldName: string,\n\tdiagnostics: RuleDiagnostic[],\n\trulePath: string,\n): string[] | undefined {\n\tif (value === undefined) return undefined;\n\tif (Array.isArray(value) && value.every((item) => typeof item === \"string\")) {\n\t\treturn value as string[];\n\t}\n\tif (typeof value === \"string\") {\n\t\treturn [value];\n\t}\n\tdiagnostics.push({\n\t\tseverity: \"warning\",\n\t\trulePath,\n\t\tmessage: `Ignoring invalid ${fieldName}; expected string[]`,\n\t});\n\treturn undefined;\n}\n","import { resolve } from \"node:path\";\nimport { fileStatFingerprint, listFilesRecursive, readTextFile, resolveRealPath } from \"../shared/fs.js\";\nimport { sha256 } from \"../shared/hash.js\";\nimport { isSubPath, normalizePath } from \"../shared/path.js\";\nimport { findProjectRoot } from \"./project-root.js\";\nimport type { RuleDiagnostic, RuleFile, RuleScanResult } from \"./types.js\";\n\nexport async function scanRuleFiles(cwd: string): Promise<RuleScanResult> {\n\tconst projectRoot = findProjectRoot(cwd) ?? cwd;\n\tconst rulesDir = resolve(projectRoot, \".pi/rules\");\n\tconst diagnostics: RuleDiagnostic[] = [];\n\tconst files = await listFilesRecursive(rulesDir);\n\tconst ruleFiles: RuleFile[] = [];\n\tconst seenRealpaths = new Set<string>();\n\n\tfor (const absolutePath of files) {\n\t\tif (!absolutePath.endsWith(\".md\")) continue;\n\t\tif (!isSubPath(rulesDir, absolutePath)) continue;\n\t\tif (normalizePath(absolutePath).includes(\"/.pi/.pi-rules/\")) continue;\n\t\tconst realPath = await resolveRealPath(absolutePath);\n\t\t// Deduplicate symlinked rules by realpath\n\t\tif (seenRealpaths.has(realPath)) continue;\n\t\tseenRealpaths.add(realPath);\n\t\tconst fingerprint = fileStatFingerprint(absolutePath);\n\t\tif (fingerprint === \"missing\") {\n\t\t\tdiagnostics.push({ severity: \"warning\", rulePath: absolutePath, message: \"Unable to stat rule file\" });\n\t\t\tcontinue;\n\t\t}\n\t\tconst content = await readTextFile(absolutePath);\n\t\tif (content === undefined) {\n\t\t\tdiagnostics.push({ severity: \"warning\", rulePath: absolutePath, message: \"Unable to read rule file\" });\n\t\t\tcontinue;\n\t\t}\n\t\truleFiles.push({\n\t\t\tabsolutePath,\n\t\t\trealPath,\n\t\t\trelativePath: normalizePath(absolutePath.slice(projectRoot.length + 1)),\n\t\t\truleId: sha256(realPath),\n\t\t\tcontentHash: sha256(content),\n\t\t\tfingerprint,\n\t\t});\n\t}\n\n\truleFiles.sort((left, right) => left.relativePath.localeCompare(right.relativePath));\n\treturn { projectRoot, rulesDir, ruleFiles, diagnostics };\n}\n","import { existsSync, statSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Default marker files / directories used to locate the project root. The\n * search walks UP from `startPath` and returns the FIRST directory that\n * contains any of these markers.\n */\nexport const DEFAULT_PROJECT_MARKERS: readonly string[] = [\n\t\".git\",\n\t\"pnpm-workspace.yaml\",\n\t\"package.json\",\n\t\"pyproject.toml\",\n\t\".pi\",\n];\n\n/**\n * Locate the project root by walking up from `startPath` until a directory\n * containing any of the supplied markers is found. Returns `undefined` when\n * the path does not exist or no marker is found before reaching the\n * filesystem root.\n *\n * Order matters: the first marker in the list wins when several coexist at\n * the same level. Callers that need a different precedence (for example\n * preferring `.pi` over `.git` in pi-rules-only contexts) should reorder\n * the markers array before passing it in.\n *\n * @param startPath  Absolute or relative path to start the search from.\n * @param markers    Optional override for the marker list. Defaults to\n *                   {@link DEFAULT_PROJECT_MARKERS}.\n */\nexport function findProjectRoot(\n\tstartPath: string,\n\tmarkers: ReadonlyArray<string> = DEFAULT_PROJECT_MARKERS,\n): string | undefined {\n\tconst resolvedStart = resolve(startPath);\n\n\tif (!existsSync(resolvedStart)) {\n\t\treturn undefined;\n\t}\n\n\tconst startStats = statSync(resolvedStart);\n\tlet currentDir = startStats.isDirectory() ? resolvedStart : dirname(resolvedStart);\n\tconst filesystemRoot = resolve(\"/\");\n\n\tfor (;;) {\n\t\tfor (const marker of markers) {\n\t\t\tif (existsSync(`${currentDir}/${marker}`)) {\n\t\t\t\treturn currentDir;\n\t\t\t}\n\t\t}\n\n\t\tif (currentDir === filesystemRoot) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst parent = dirname(currentDir);\n\t\tif (parent === currentDir) {\n\t\t\treturn undefined;\n\t\t}\n\t\tcurrentDir = parent;\n\t}\n}\n","import { fileStatFingerprint, readTextFile } from \"../shared/fs.js\";\nimport { createId } from \"../shared/id.js\";\nimport { now } from \"../shared/time.js\";\nimport { formatRuleContext } from \"./formatter.js\";\nimport { matchByTriggers, matchRules } from \"./matcher.js\";\nimport { parseRuleFile } from \"./parser.js\";\nimport { scanRuleFiles } from \"./scanner.js\";\nimport type {\n\tInjectionRecord,\n\tMatchedRule,\n\tParsedRule,\n\tRuleContextResult,\n\tRuleFile,\n\tRuleLoadResult,\n\tRuleStatus,\n} from \"./types.js\";\n\nexport interface RulesEngineOptions {\n\tmaxRuleChars: number;\n\tmaxContextChars: number;\n}\n\n/**\n * Tracks per-turn injection state to prevent duplicate injections\n * within the same turn (when the same rule matches multiple target paths).\n */\ninterface TurnInjectionState {\n\t/** Set of `<realPath>::<contentHash>` keys injected this turn. */\n\tstaticInjected: Set<string>;\n\t/** Map of `targetPath → Set<realPath::contentHash>` for dynamic injection. */\n\tdynamicInjected: Map<string, Set<string>>;\n}\n\nfunction staticDedupKey(rule: ParsedRule): string {\n\treturn `${rule.realPath}::${rule.contentHash}`;\n}\n\n/**\n * Deduplicate matches by ruleId, keeping the first occurrence.\n * Prevents a rule from appearing twice when matched by both path and trigger.\n */\nfunction dedupeMatches(matches: MatchedRule[]): MatchedRule[] {\n\tconst seen = new Set<string>();\n\tconst result: MatchedRule[] = [];\n\tfor (const match of matches) {\n\t\tif (!seen.has(match.ruleId)) {\n\t\t\tseen.add(match.ruleId);\n\t\t\tresult.push(match);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction dynamicDedupKey(scopeKey: string, rule: ParsedRule): string {\n\treturn `${scopeKey}::${rule.realPath}::${rule.contentHash}`;\n}\n\nexport class RulesEngine {\n\tprivate cache?: RuleLoadResult;\n\tprivate lastContext?: InjectionRecord;\n\tprivate turnState: TurnInjectionState = { staticInjected: new Set(), dynamicInjected: new Map() };\n\n\tconstructor(private readonly options: RulesEngineOptions) {}\n\n\tget formatOptions(): RulesEngineOptions {\n\t\treturn { ...this.options };\n\t}\n\n\tasync loadRules(cwd: string, forceReload = false): Promise<RuleLoadResult> {\n\t\tif (this.cache !== undefined && !forceReload) {\n\t\t\treturn this.cache;\n\t\t}\n\n\t\t// Full reload path\n\t\tconst scanResult = await scanRuleFiles(cwd);\n\t\tconst rules: ParsedRule[] = [];\n\t\tconst diagnostics = [...scanResult.diagnostics];\n\t\tconst fingerprints = new Map<string, string>();\n\n\t\tfor (const ruleFile of scanResult.ruleFiles) {\n\t\t\tfingerprints.set(ruleFile.relativePath, ruleFile.fingerprint);\n\t\t\tconst content = await readTextFile(ruleFile.absolutePath);\n\t\t\tif (content === undefined) {\n\t\t\t\tdiagnostics.push({\n\t\t\t\t\tseverity: \"warning\",\n\t\t\t\t\trulePath: ruleFile.relativePath,\n\t\t\t\t\tmessage: \"Unable to read rule file\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst parsedRule = parseRuleFile(ruleFile, content);\n\t\t\trules.push(parsedRule);\n\t\t\tdiagnostics.push(...parsedRule.diagnostics);\n\t\t}\n\n\t\tthis.cache = {\n\t\t\tprojectRoot: scanResult.projectRoot,\n\t\t\trules,\n\t\t\tdiagnostics,\n\t\t\tscannedAt: now(),\n\t\t\tfingerprints,\n\t\t};\n\t\treturn this.cache;\n\t}\n\n\t/**\n\t * Quick-check whether cached rules are still up-to-date by comparing\n\t * on-disk stat fingerprints against stored fingerprints. Returns true\n\t * when no rule file has been modified, added, or removed.\n\t */\n\tasync fingerprintsMatch(cwd: string): Promise<boolean> {\n\t\tif (this.cache === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Re-scan to discover any added/removed files\n\t\tconst scanResult = await scanRuleFiles(cwd);\n\n\t\t// Quick check: same number of files?\n\t\tif (scanResult.ruleFiles.length !== this.cache.fingerprints.size) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check each file's current stat fingerprint against the stored value\n\t\tfor (const ruleFile of scanResult.ruleFiles) {\n\t\t\tconst storedFingerprint = this.cache.fingerprints.get(ruleFile.relativePath);\n\t\t\tif (storedFingerprint === undefined) {\n\t\t\t\treturn false; // New file appeared\n\t\t\t}\n\t\t\tconst currentFingerprint = fileStatFingerprint(ruleFile.absolutePath);\n\t\t\tif (currentFingerprint !== storedFingerprint) {\n\t\t\t\treturn false; // File modified\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Like loadRules but uses fingerprint matching to avoid re-reading files\n\t * when possible. Falls back to full load when fingerprints have changed.\n\t */\n\tasync loadRulesIfUnchanged(cwd: string): Promise<RuleLoadResult> {\n\t\tif (await this.fingerprintsMatch(cwd)) {\n\t\t\treturn this.cache!;\n\t\t}\n\t\treturn this.loadRules(cwd, true);\n\t}\n\n\tasync matchRulesForPaths(cwd: string, targetPaths: string[], promptText?: string): Promise<RuleContextResult> {\n\t\tconst loaded = await this.loadRulesIfUnchanged(cwd);\n\t\tconst pathMatches = matchRules(loaded.rules, targetPaths);\n\t\tconst triggerMatches = promptText ? matchByTriggers(loaded.rules, promptText) : [];\n\t\tconst matches = dedupeMatches([...pathMatches, ...triggerMatches]);\n\t\tconst formatted = formatRuleContext(matches, this.options);\n\t\treturn {\n\t\t\ttargetPaths,\n\t\t\tmatches,\n\t\t\tprompt: formatted.prompt,\n\t\t\ttruncated: formatted.truncated,\n\t\t};\n\t}\n\n\t/**\n\t * Like matchRulesForPaths but filters out rules already injected\n\t * in the current turn (context file dedup for static injection).\n\t */\n\tasync matchRulesForPathsStatic(\n\t\tcwd: string,\n\t\ttargetPaths: string[],\n\t\tcontextFileRealPaths: Set<string>,\n\t\tpromptText?: string,\n\t): Promise<RuleContextResult> {\n\t\tconst loaded = await this.loadRulesIfUnchanged(cwd);\n\t\t// Filter out rules whose file is already loaded as a native context file (AGENTS.md / CLAUDE.md overlap)\n\t\tconst filteredRules = loaded.rules.filter(\n\t\t\t(rule) => !contextFileRealPaths.has(rule.realPath) && !contextFileRealPaths.has(rule.absolutePath),\n\t\t);\n\t\t// Also filter out already-injected-in-this-turn rules\n\t\tconst dedupedRules = filteredRules.filter((rule) => !this.turnState.staticInjected.has(staticDedupKey(rule)));\n\t\tconst pathMatches = matchRules(dedupedRules, targetPaths);\n\t\tconst triggerMatches = promptText ? matchByTriggers(dedupedRules, promptText) : [];\n\t\tconst matches = dedupeMatches([...pathMatches, ...triggerMatches]);\n\t\tconst formatted = formatRuleContext(matches, this.options);\n\t\treturn {\n\t\t\ttargetPaths,\n\t\t\tmatches,\n\t\t\tprompt: formatted.prompt,\n\t\t\ttruncated: formatted.truncated,\n\t\t};\n\t}\n\n\t/**\n\t * Like matchRulesForPaths but filters out rules already dynamically injected\n\t * for a specific scope key (target path) in the current turn.\n\t */\n\tasync matchRulesForPathsDynamic(cwd: string, targetPaths: string[], promptText?: string): Promise<RuleContextResult> {\n\t\tconst loaded = await this.loadRulesIfUnchanged(cwd);\n\t\tconst scopeKey = targetPaths[0] ?? \"_all\";\n\t\tconst injectedInScope = this.turnState.dynamicInjected.get(scopeKey) ?? new Set();\n\t\tconst dedupedRules = loaded.rules.filter((rule) => !injectedInScope.has(staticDedupKey(rule)));\n\t\tconst pathMatches = matchRules(dedupedRules, targetPaths);\n\t\tconst triggerMatches = promptText ? matchByTriggers(dedupedRules, promptText) : [];\n\t\tconst matches = dedupeMatches([...pathMatches, ...triggerMatches]);\n\t\tconst formatted = formatRuleContext(matches, this.options);\n\t\treturn {\n\t\t\ttargetPaths,\n\t\t\tmatches,\n\t\t\tprompt: formatted.prompt,\n\t\t\ttruncated: formatted.truncated,\n\t\t};\n\t}\n\n\tmarkStaticInjected(rule: ParsedRule): void {\n\t\tthis.turnState.staticInjected.add(staticDedupKey(rule));\n\t}\n\n\tmarkDynamicInjected(scopeKey: string, rule: ParsedRule): void {\n\t\tconst set = this.turnState.dynamicInjected.get(scopeKey) ?? new Set();\n\t\tset.add(staticDedupKey(rule));\n\t\tthis.turnState.dynamicInjected.set(scopeKey, set);\n\t}\n\n\tmarkStaticInjectedBatch(rules: readonly MatchedRule[]): void {\n\t\tfor (const rule of rules) {\n\t\t\tthis.turnState.staticInjected.add(staticDedupKey(rule));\n\t\t}\n\t}\n\n\tmarkDynamicInjectedBatch(scopeKey: string, rules: readonly MatchedRule[]): void {\n\t\tconst set = this.turnState.dynamicInjected.get(scopeKey) ?? new Set();\n\t\tfor (const rule of rules) {\n\t\t\tset.add(staticDedupKey(rule));\n\t\t}\n\t\tthis.turnState.dynamicInjected.set(scopeKey, set);\n\t}\n\n\twasFullInjected(rule: ParsedRule): boolean {\n\t\tconst key = staticDedupKey(rule);\n\t\tif (this.turnState.staticInjected.has(key)) return true;\n\t\tfor (const injected of this.turnState.dynamicInjected.values()) {\n\t\t\tif (injected.has(key)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\trecordInjection(targetPaths: string[], context: RuleContextResult): InjectionRecord {\n\t\tconst record: InjectionRecord = {\n\t\t\tturnId: createId(\"turn\"),\n\t\t\ttargetPaths,\n\t\t\trules: context.matches.map((match) => ({\n\t\t\t\truleId: match.ruleId,\n\t\t\t\trelativePath: match.relativePath,\n\t\t\t\tsummary: match.frontmatter.summary,\n\t\t\t\tmatchReason: match.matchReason,\n\t\t\t})),\n\t\t\tinjectedAt: now(),\n\t\t\ttruncated: context.truncated,\n\t\t};\n\t\tthis.lastContext = record;\n\t\treturn record;\n\t}\n\n\tgetLastContext(): InjectionRecord | undefined {\n\t\treturn this.lastContext;\n\t}\n\n\tasync getStatus(cwd: string): Promise<RuleStatus> {\n\t\tconst loaded = await this.loadRulesIfUnchanged(cwd);\n\t\treturn {\n\t\t\tprojectRoot: loaded.projectRoot,\n\t\t\trulesDir: `${loaded.projectRoot}/.pi/rules`,\n\t\t\truleCount: loaded.rules.length,\n\t\t\tdiagnostics: loaded.diagnostics,\n\t\t\trules: loaded.rules,\n\t\t\tlastContext: this.lastContext,\n\t\t};\n\t}\n\n\tclearCache(): void {\n\t\tthis.cache = undefined;\n\t}\n\n\tresetTurn(): void {\n\t\tthis.turnState = { staticInjected: new Set(), dynamicInjected: new Map() };\n\t}\n}\n","import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, resolve } from \"node:path\";\nimport { DEFAULT_MAINTAINER_LOG_LINES, DEFAULT_MAX_CONTEXT_CHARS, DEFAULT_MAX_RULE_CHARS } from \"./constants.js\";\n\nconst CONFIG_DIR_NAME = \".pi\";\n\nexport type PiRulesMode = \"static\" | \"dynamic\" | \"both\" | \"off\";\nexport type PiRulesDynamicInjection = \"off\" | \"full\";\n\nexport interface PiRulesConfig {\n\tdisabled: boolean;\n\tmode: PiRulesMode;\n\trecommendationEnabled: boolean;\n\twidgetEnabled: boolean;\n\tmaxRuleChars: number;\n\tmaxContextChars: number;\n\tmaintainerLogLines: number;\n\twriteGuardEnabled: boolean;\n\tdynamicInjection: PiRulesDynamicInjection;\n}\n\nexport const DEFAULT_CONFIG: PiRulesConfig = {\n\tdisabled: false,\n\tmode: \"both\",\n\trecommendationEnabled: true,\n\twidgetEnabled: true,\n\tmaxRuleChars: DEFAULT_MAX_RULE_CHARS,\n\tmaxContextChars: DEFAULT_MAX_CONTEXT_CHARS,\n\tmaintainerLogLines: DEFAULT_MAINTAINER_LOG_LINES,\n\twriteGuardEnabled: false,\n\tdynamicInjection: \"full\",\n};\n\nexport function readConfigFromEnv(env: NodeJS.ProcessEnv = process.env): Partial<PiRulesConfig> {\n\treturn {\n\t\tdisabled: env.PI_RULES_DISABLED === \"1\" ? true : undefined,\n\t\trecommendationEnabled: env.PI_RULES_RECOMMENDATIONS_DISABLED === \"1\" ? false : undefined,\n\t\tmaxRuleChars: parsePositiveInteger(env.PI_RULES_MAX_RULE_CHARS),\n\t\tmaxContextChars: parsePositiveInteger(env.PI_RULES_MAX_CONTEXT_CHARS),\n\t\tmaintainerLogLines: parsePositiveInteger(env.PI_RULES_MAINTAINER_LOG_LINES),\n\t\twriteGuardEnabled: parseBooleanFlag(env.PI_RULES_WRITE_GUARD),\n\t\tdynamicInjection: parseDynamicInjection(env.PI_RULES_DYNAMIC_INJECTION),\n\t};\n}\n\nexport function readConfigFromFiles(projectRoot: string, homeDir = homedir()): Partial<PiRulesConfig> {\n\treturn mergePartialConfigFiles(\n\t\treadConfigJsonFile(resolve(homeDir, CONFIG_DIR_NAME, \"agent\", \"pi-rules\", \"config.json\")),\n\t\treadConfigJsonFile(resolve(projectRoot, CONFIG_DIR_NAME, \"pi-rules\", \"config.json\")),\n\t);\n}\n\nexport function writeProjectConfigPatch(projectRoot: string, patch: Partial<PiRulesConfig>): string {\n\tconst configPath = resolve(projectRoot, CONFIG_DIR_NAME, \"pi-rules\", \"config.json\");\n\tconst current = readJsonObject(configPath);\n\tconst next = { ...current, ...sanitizeConfigPatch(patch) };\n\tmkdirSync(dirname(configPath), { recursive: true });\n\twriteFileSync(configPath, `${JSON.stringify(next, null, 2)}\\n`, \"utf8\");\n\treturn configPath;\n}\n\nexport function mergeConfig(...parts: Array<Partial<PiRulesConfig>>): PiRulesConfig {\n\tconst config: PiRulesConfig = { ...DEFAULT_CONFIG };\n\tfor (const part of parts) {\n\t\tfor (const [key, value] of Object.entries(part) as Array<\n\t\t\t[keyof PiRulesConfig, PiRulesConfig[keyof PiRulesConfig] | undefined]\n\t\t>) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tconfig[key] = value as never;\n\t\t\t}\n\t\t}\n\t}\n\treturn config;\n}\n\nfunction readJsonObject(path: string): Record<string, unknown> {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(path, \"utf8\"));\n\t\treturn parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed) ? parsed : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction sanitizeConfigPatch(patch: Partial<PiRulesConfig>): Partial<PiRulesConfig> {\n\treturn stripUndefinedConfig(normalizeConfigObject(patch) ?? {});\n}\n\nfunction stripUndefinedConfig(config: Partial<PiRulesConfig>): Partial<PiRulesConfig> {\n\tconst result: Partial<PiRulesConfig> = {};\n\tfor (const [key, value] of Object.entries(config) as Array<\n\t\t[keyof PiRulesConfig, PiRulesConfig[keyof PiRulesConfig] | undefined]\n\t>) {\n\t\tif (value !== undefined) {\n\t\t\tresult[key] = value as never;\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction mergePartialConfigFiles(...parts: Array<Partial<PiRulesConfig> | undefined>): Partial<PiRulesConfig> {\n\tconst config: Partial<PiRulesConfig> = {};\n\tfor (const part of parts) {\n\t\tif (part === undefined) continue;\n\t\tfor (const [key, value] of Object.entries(part) as Array<\n\t\t\t[keyof PiRulesConfig, PiRulesConfig[keyof PiRulesConfig] | undefined]\n\t\t>) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tconfig[key] = value as never;\n\t\t\t}\n\t\t}\n\t}\n\treturn config;\n}\n\nfunction readConfigJsonFile(path: string): Partial<PiRulesConfig> | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn normalizeConfigObject(parsed);\n}\n\nfunction normalizeConfigObject(value: unknown): Partial<PiRulesConfig> | undefined {\n\tif (value === null || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n\tconst input = value as Record<string, unknown>;\n\treturn {\n\t\tdisabled: typeof input.disabled === \"boolean\" ? input.disabled : undefined,\n\t\tmode: isMode(input.mode) ? input.mode : undefined,\n\t\trecommendationEnabled: typeof input.recommendationEnabled === \"boolean\" ? input.recommendationEnabled : undefined,\n\t\twidgetEnabled: typeof input.widgetEnabled === \"boolean\" ? input.widgetEnabled : undefined,\n\t\tmaxRuleChars: typeof input.maxRuleChars === \"number\" && input.maxRuleChars > 0 ? input.maxRuleChars : undefined,\n\t\tmaxContextChars:\n\t\t\ttypeof input.maxContextChars === \"number\" && input.maxContextChars > 0 ? input.maxContextChars : undefined,\n\t\tmaintainerLogLines:\n\t\t\ttypeof input.maintainerLogLines === \"number\" && input.maintainerLogLines > 0\n\t\t\t\t? input.maintainerLogLines\n\t\t\t\t: undefined,\n\t\twriteGuardEnabled: typeof input.writeGuardEnabled === \"boolean\" ? input.writeGuardEnabled : undefined,\n\t\tdynamicInjection: parseDynamicInjection(\n\t\t\ttypeof input.dynamicInjection === \"string\" ? input.dynamicInjection : undefined,\n\t\t),\n\t};\n}\n\nfunction isMode(value: unknown): value is PiRulesMode {\n\treturn value === \"static\" || value === \"dynamic\" || value === \"both\" || value === \"off\";\n}\n\nfunction parsePositiveInteger(value: string | undefined): number | undefined {\n\tif (value === undefined) return undefined;\n\tconst parsed = Number.parseInt(value, 10);\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nfunction parseBooleanFlag(value: string | undefined): boolean | undefined {\n\tif (value === undefined) return undefined;\n\treturn value === \"1\" || value.toLowerCase() === \"true\";\n}\n\nfunction parseDynamicInjection(value: string | undefined): PiRulesDynamicInjection | undefined {\n\treturn value === \"off\" || value === \"full\" ? value : undefined;\n}\n","/**\n * Static constants used across the pi-rules extension.\n *\n * These are pure, dependency-free values that other modules may import without\n * triggering the layered dependency-cruiser rules. Anything that requires\n * runtime configuration (env vars, flags, etc.) should live in `config.ts`\n * instead.\n */\n\n/** Per-rule body cap. Rules longer than this are truncated when formatted. */\nexport const DEFAULT_MAX_RULE_CHARS = 12_000;\n\n/** Per-injection cap. Total context prompt size cannot exceed this. */\nexport const DEFAULT_MAX_CONTEXT_CHARS = 40_000;\n\n/** Default number of lines to show from the maintainer log tail. */\nexport const DEFAULT_MAINTAINER_LOG_LINES = 100;\n\n/** YAML frontmatter delimiter used by parser and the create_rule tool. */\nexport const FRONTMATTER_DELIMITER = \"---\";\n\n/** Directory names that the rules scanner should ignore. */\nexport const SCANNER_EXCLUDED_DIRS: readonly string[] = [\n\t\"node_modules\",\n\t\".git\",\n\t\"dist\",\n\t\"build\",\n\t\".next\",\n\t\".turbo\",\n\t\"coverage\",\n];\n\n/** Placeholder inserted when a rule body is truncated. */\nexport const TRUNCATION_NOTICE_TEMPLATE = \"\\n\\n[... truncated by pi-rules: {path} ...]\";\n\n/** Default width for the TUI banner widget when stdout width is unknown. */\nexport const DEFAULT_BANNER_WIDTH = 80;\n","import type { InjectionRecord } from \"../domain/types.js\";\n\n/**\n * Maximum number of paths retained in {@link RuntimeState.sessionHotPaths}.\n * The set is FIFO-evicted when the cap is reached so a long session cannot\n * grow the working-set memory without bound.\n */\nexport const SESSION_HOT_PATHS_MAX = 100;\n\nexport interface RuntimeState {\n\tprojectRoot: string;\n\t/** Paths touched by read/grep/find/ls tool calls within the current turn. Cleared by `resetTurnState`. */\n\trecentReadPaths: Set<string>;\n\t/** Paths touched by write/edit/bash tool calls within the current turn. Cleared by `resetTurnState`. */\n\trecentChangedPaths: Set<string>;\n\t/**\n\t * Across-turn session memory. Paths touched by any tool call in the\n\t * current session. Cleared only on session boundary (`resetSessionState`)\n\t * so that rule matching survives both turn boundaries and `session_compact`.\n\t */\n\tsessionHotPaths: Set<string>;\n\tgitStatusBeforeTurn: string;\n\tlastContext?: InjectionRecord;\n}\n\nexport function createRuntimeState(projectRoot: string): RuntimeState {\n\treturn {\n\t\tprojectRoot,\n\t\trecentReadPaths: new Set<string>(),\n\t\trecentChangedPaths: new Set<string>(),\n\t\tsessionHotPaths: new Set<string>(),\n\t\tgitStatusBeforeTurn: \"\",\n\t};\n}\n\n/**\n * Reset only per-turn state. `sessionHotPaths` and `lastContext` are\n * preserved so that rule matching stays coherent across turns and across\n * `session_compact`.\n */\nexport function resetTurnState(state: RuntimeState): void {\n\tstate.recentReadPaths.clear();\n\tstate.recentChangedPaths.clear();\n\tstate.gitStatusBeforeTurn = \"\";\n}\n\n/**\n * Reset the full runtime state. Called on session boundary (initial\n * `session_start`) to give the extension a clean working set.\n */\nexport function resetSessionState(state: RuntimeState): void {\n\tresetTurnState(state);\n\tstate.sessionHotPaths.clear();\n\tstate.lastContext = undefined;\n}\n\n/**\n * Add paths to `sessionHotPaths`, evicting the oldest entry (FIFO) when the\n * cap is reached. Re-adding an existing path is a no-op and does not change\n * its position in the insertion order.\n */\nexport function addSessionHotPaths(state: RuntimeState, paths: Iterable<string>): void {\n\tfor (const path of paths) {\n\t\tif (path.length === 0) continue;\n\t\tif (state.sessionHotPaths.has(path)) continue;\n\t\tif (state.sessionHotPaths.size >= SESSION_HOT_PATHS_MAX) {\n\t\t\tconst oldest = state.sessionHotPaths.values().next().value;\n\t\t\tif (oldest !== undefined) state.sessionHotPaths.delete(oldest);\n\t\t}\n\t\tstate.sessionHotPaths.add(path);\n\t}\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path"]}