@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
130 lines (129 loc) • 3.84 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 { exec } from "child_process";
import { promisify } from "util";
import {
BaseVerifier
} from "./base-verifier.js";
import { logger } from "../../core/monitoring/logger.js";
const execAsync = promisify(exec);
class FormatterVerifier extends BaseVerifier {
formatters = /* @__PURE__ */ new Map();
constructor(config) {
super({
id: "formatter",
name: "Code Formatter",
type: "style",
enabled: true,
stopOnError: false,
timeout: 1e4,
...config
});
this.initializeFormatters();
}
initializeFormatters() {
this.formatters.set("typescript", {
checkCommand: "npx prettier --check",
fixCommand: "npx prettier --write",
patterns: [/\[error\]/gi, /File not formatted/gi]
});
this.formatters.set("javascript", {
checkCommand: "npx prettier --check",
fixCommand: "npx prettier --write",
patterns: [/\[error\]/gi, /File not formatted/gi]
});
this.formatters.set("python", {
checkCommand: "black --check",
fixCommand: "black",
patterns: [/would reformat/gi, /File not formatted/gi]
});
this.formatters.set("rust", {
checkCommand: "cargo fmt -- --check",
fixCommand: "cargo fmt",
patterns: [/Diff in/gi, /File not formatted/gi]
});
this.formatters.set("go", {
checkCommand: "gofmt -l",
fixCommand: "gofmt -w",
patterns: [/.+\.go$/gm]
// gofmt lists unformatted files
});
}
shouldActivate(context) {
if (!context.language || !context.filePath) {
return false;
}
return this.formatters.has(context.language.toLowerCase());
}
async verify(input, context) {
if (!context.language || !context.filePath) {
return this.createResult(
false,
"Missing language or file path in context",
"error"
);
}
const formatter = this.formatters.get(context.language.toLowerCase());
if (!formatter) {
return this.createResult(
true,
`No formatter configured for ${context.language}`,
"info"
);
}
try {
return await this.withTimeout(async () => {
return await this.withRetry(
() => this.runFormatter(formatter, context),
context.filePath
);
});
} catch (error) {
logger.error(
"Formatter verification failed",
error instanceof Error ? error : void 0
);
return this.createResult(
false,
`Formatter error: ${error instanceof Error ? error.message : String(error)}`,
"error"
);
}
}
async runFormatter(formatter, context) {
const command = `${formatter.checkCommand} "${context.filePath}"`;
try {
const { _stdout, _stderr } = await execAsync(command, {
cwd: process.cwd(),
timeout: this.config.timeout
});
return this.createResult(true, "Code is properly formatted", "info");
} catch (error) {
if (error.code === 1) {
const output = error.stdout + error.stderr;
const errors = this.extractRelevantErrors(output, formatter.patterns);
return this.createResult(
false,
this.generateFeedback(errors, context),
"warning",
{
code: output.substring(0, 500),
suggestion: "Run formatter to fix issues"
},
{
command: `${formatter.fixCommand} "${context.filePath}"`,
description: "Auto-format the file",
safe: true,
confidence: 0.95
}
);
}
throw error;
}
}
}
export {
FormatterVerifier
};