@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
133 lines (132 loc) • 3.44 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { logger } from "../../core/monitoring/logger.js";
class BaseVerifier {
config;
retryCount = /* @__PURE__ */ new Map();
constructor(config) {
this.config = config;
}
/**
* Extract most relevant error messages (Spotify pattern)
*/
extractRelevantErrors(output, patterns) {
const errors = [];
const lines = output.split("\n");
for (const line of lines) {
for (const pattern of patterns) {
const match = line.match(pattern);
if (match) {
errors.push(match[0]);
break;
}
}
}
return errors.slice(0, 5);
}
/**
* Generate incremental feedback
*/
generateFeedback(errors, context) {
if (errors.length === 0) {
return `${this.config.name} verification passed`;
}
const prefix = context.filePath ? `In ${context.filePath}: ` : "";
const errorList = errors.map((e) => ` - ${e}`).join("\n");
return `${prefix}${this.config.name} found ${errors.length} issue(s):
${errorList}`;
}
/**
* Retry logic for transient failures
*/
async withRetry(operation, key) {
const maxRetries = this.config.maxRetries || 0;
const currentRetries = this.retryCount.get(key) || 0;
try {
const result = await operation();
this.retryCount.delete(key);
return result;
} catch (error) {
if (currentRetries < maxRetries) {
this.retryCount.set(key, currentRetries + 1);
logger.warn(`Retrying ${this.config.name} verification`, {
attempt: currentRetries + 1,
maxRetries,
error: error instanceof Error ? error.message : String(error)
});
await this.delay(Math.pow(2, currentRetries) * 1e3);
return this.withRetry(operation, key);
}
this.retryCount.delete(key);
throw error;
}
}
/**
* Apply timeout to verification
*/
async withTimeout(operation) {
if (!this.config.timeout) {
return operation();
}
return Promise.race([
operation(),
new Promise(
(_, reject) => setTimeout(
() => reject(
new Error(
`${this.config.name} timeout after ${this.config.timeout}ms`
)
),
this.config.timeout
)
)
]);
}
/**
* Helper to create a standard result
*/
createResult(passed, message, severity = "info", details, autoFix) {
return {
verifierId: this.config.id,
verifierType: this.config.type,
passed,
message,
severity,
timestamp: /* @__PURE__ */ new Date(),
details: details || void 0,
autoFix: autoFix || void 0
};
}
/**
* Delay helper for retries
*/
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Get verifier configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Check if verifier should stop execution on error
*/
shouldStopOnError() {
return this.config.stopOnError;
}
/**
* Update verifier configuration
*/
updateConfig(updates) {
this.config = { ...this.config, ...updates };
logger.info(`Updated ${this.config.name} configuration`, {
updates
});
}
}
export {
BaseVerifier
};