UNPKG

@stackmemoryai/stackmemory

Version:

Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a

86 lines (85 loc) 2.31 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { RuleStore } from "./rule-store.js"; import { BUILT_IN_RULES, getBuiltinRows, filterByScope } from "./built-in-rules.js"; class RuleEngine { store; checkFns = /* @__PURE__ */ new Map(); constructor(db) { this.store = new RuleStore(db); this.store.seedBuiltins(getBuiltinRows()); for (const rule of BUILT_IN_RULES) { this.checkFns.set(rule.id, rule.check.bind(rule)); } } registerRule(rule) { this.store.upsert({ id: rule.id, name: rule.name, description: rule.description, trigger_type: rule.trigger, severity: rule.severity, scope: rule.scope, enabled: rule.enabled ? 1 : 0, builtin: rule.builtin ? 1 : 0 }); this.checkFns.set(rule.id, rule.check.bind(rule)); } evaluate(context) { const rows = this.store.getByTrigger(context.trigger); return this.runRules(rows, context); } evaluateAll(context) { const rows = this.store.getEnabled(); return this.runRules(rows, context); } listRules(filter) { if (filter?.trigger) { return this.store.getByTrigger(filter.trigger); } if (filter?.enabled === false) { return this.store.getAll(); } return this.store.getEnabled(); } enableRule(id) { return this.store.setEnabled(id, true); } disableRule(id) { return this.store.setEnabled(id, false); } getStore() { return this.store; } runRules(rows, context) { const allViolations = []; for (const row of rows) { const checkFn = this.checkFns.get(row.id); if (!checkFn) continue; const scopedFiles = filterByScope(context.files, row.scope); if (scopedFiles.length === 0 && row.trigger_type !== "commit" && row.trigger_type !== "pre-commit") continue; const scopedCtx = { ...context, files: scopedFiles }; const result = checkFn(scopedCtx); if (!result.passed) { allViolations.push(...result.violations); } } return { passed: allViolations.length === 0, violations: allViolations }; } } export { RuleEngine };