@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
JavaScript
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
};