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

88 lines (87 loc) 2.71 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { filterByScope } from "./built-in-rules.js"; function violation(ruleId, ruleName, severity, message, file, line, suggestion) { return { ruleId, ruleName, severity, message, file, line, suggestion }; } const PR_REVIEW_PATTERNS = [ { id: "console-log", pattern: /console\.(log|debug|info)\(/, message: "console.log left in code", suggestion: "Remove or replace with structured logger", severity: "warn" }, { id: "todo-fixme", pattern: /\/\/\s*(TODO|FIXME|HACK|XXX)(?!\s*\()/, message: "TODO/FIXME without attribution", suggestion: "Add author and ticket: // TODO(user): STA-XXX description", severity: "info" }, { id: "hardcoded-secret", pattern: /(api[_-]?key|secret|password|token)\s*[:=]\s*['"][^'"]{8,}['"]/i, message: "Possible hardcoded secret", suggestion: "Move to environment variable", severity: "error" }, { id: "any-type", pattern: /:\s*any\b/, message: "Explicit any type", suggestion: "Use a specific type or unknown", severity: "warn" }, { id: "empty-catch", pattern: /catch\s*\{[\s]*\}/, message: "Empty catch block (swallows errors silently)", suggestion: "Log the error or add a comment explaining why it is safe to ignore", severity: "warn" } ]; const prReviewRule = { id: "pr-review-patterns", name: "PR Review Patterns", description: "Catch common issues that PR reviewers flag (console.log, TODO, secrets, any type)", trigger: "pre-commit", severity: "warn", scope: "src/**/*.{ts,js,tsx,jsx}", enabled: true, builtin: true, check(ctx) { const violations = []; const files = filterByScope(ctx.files, this.scope); for (const file of files) { const content = ctx.content.get(file); if (!content) continue; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (!line) continue; for (const check of PR_REVIEW_PATTERNS) { if (check.pattern.test(line)) { violations.push( violation( this.id, `${this.name}: ${check.id}`, check.severity, check.message, file, i + 1, check.suggestion ) ); } } } } return violations.length > 0 ? { passed: false, violations } : { passed: true, violations: [] }; } }; export { prReviewRule };