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

155 lines (154 loc) 4.46 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); const HIGH_COMPLEXITY_KEYWORDS = [ /\barchitect/i, /\brefactor\b/i, /\bredesign/i, /\bmigrat/i, /\bsecurity\b/i, /\bvulnerabilit/i, /\bconcurrency/i, /\brace\s+condition/i, /\bdistributed/i, /\bconsensus/i, /\btrade-?offs?\b/i, /\bbackward.?compat/i, /\bbreaking\s+change/i, /\bperformance\s+(?:optimi|critical|bottleneck)/i, /\bscalability/i, /\bcrypto/i, /\bencrypt/i, /\bauth(?:entication|orization)\b/i, /\bOWASP/i ]; const LOW_COMPLEXITY_KEYWORDS = [ /\bfix\s+typo/i, /\brename\b/i, /\bformat(?:ting)?\s+(?:the\s+)?(?:code|file|project|source)/i, /\bprettier/i, /\blint\s*(?:fix|error|warning)/i, /\bupdate\s+(?:version|dep)/i, /\badd\s+comment/i, /\bremove\s+unused/i, /\bimport\s+(?:sort|order)/i, /\bconsole\.log/i, /\btodo\b/i, /\bcleanup\b/i ]; const MEDIUM_COMPLEXITY_KEYWORDS = [ /\bwrite\b/i, /\bimplement/i, /\bcreate\b/i, /\bbuild\b/i, /\bparse/i, /\bhandle\b/i, /\bvalidat/i, /\btransform/i, /\bconvert/i, /\bgenerat/i, /\bintegrat/i, /\boptimiz/i ]; const REASONING_INDICATORS = [ /\bstep\s*(?:by|1|2|3)/i, /\bfirst.*then.*finally/i, /\bcompare\s+(?:and|options|approaches)/i, /\banalyze/i, /\bevaluate/i, /\bdiagnos/i, /\bdebug.*(?:complex|intermittent|flaky)/i, /\broot\s+cause/i, /\bsystem\s*design/i ]; const LOW_THRESHOLD = 0.25; const HIGH_THRESHOLD = 0.6; const SHORT_PROMPT = 200; const MEDIUM_PROMPT = 800; const LONG_PROMPT = 2e3; function scoreComplexity(task, context) { const signals = []; let score = 0; const highHits = HIGH_COMPLEXITY_KEYWORDS.filter((re) => re.test(task)); if (highHits.length >= 5) { score += 0.6; signals.push(`${highHits.length} high-complexity keywords`); } else if (highHits.length >= 3) { score += 0.5; signals.push(`${highHits.length} high-complexity keywords`); } else if (highHits.length >= 1) { score += 0.2 * Math.min(highHits.length, 2); signals.push(`${highHits.length} high-complexity keyword(s)`); } const lowHits = LOW_COMPLEXITY_KEYWORDS.filter((re) => re.test(task)); if (lowHits.length >= 2) { score -= 0.25; signals.push(`${lowHits.length} low-complexity keywords`); } else if (lowHits.length === 1) { score -= 0.15; signals.push("1 low-complexity keyword"); } const medHits = MEDIUM_COMPLEXITY_KEYWORDS.filter((re) => re.test(task)); if (medHits.length >= 2) { score += 0.3; signals.push(`${medHits.length} medium-complexity keywords`); } else if (medHits.length === 1) { score += 0.15; signals.push("1 medium-complexity keyword"); } const hasSubstance = highHits.length > 0 || medHits.length > 0; const len = task.length; if (len > LONG_PROMPT && hasSubstance) { score += 0.2; signals.push(`long prompt (${len} chars)`); } else if (len > MEDIUM_PROMPT && hasSubstance) { score += 0.1; signals.push(`medium prompt (${len} chars)`); } else if (len < SHORT_PROMPT) { score -= 0.03; } const reasoningHits = REASONING_INDICATORS.filter((re) => re.test(task)); if (reasoningHits.length >= 2) { score += 0.25; signals.push(`${reasoningHits.length} reasoning indicators`); } else if (reasoningHits.length === 1) { score += 0.15; signals.push("1 reasoning indicator"); } if (context) { const files = context["files"]; if (Array.isArray(files)) { if (files.length >= 10) { score += 0.15; signals.push(`${files.length} files in context`); } else if (files.length >= 4) { score += 0.08; signals.push(`${files.length} files in context`); } } const codeSize = context["codeSize"]; if (typeof codeSize === "number" && codeSize > 5e3) { score += 0.1; signals.push(`large code context (${codeSize} chars)`); } } const questionMarks = (task.match(/\?/g) || []).length; if (questionMarks >= 3) { score += 0.1; signals.push(`${questionMarks} questions`); } score = Math.max(0, Math.min(1, score)); let tier; if (score < LOW_THRESHOLD) { tier = "low"; } else if (score >= HIGH_THRESHOLD) { tier = "high"; } else { tier = "medium"; } return { score: Math.round(score * 100) / 100, tier, signals }; } export { scoreComplexity };