aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
167 lines • 6.12 kB
JavaScript
/**
* Scoring Configuration Loader
*
* Loads validation scoring weights and thresholds from the writing-quality addon.
* Provides typed access to scoring parameters with documented defaults.
*
* @source @agentic/code/addons/writing-quality/validation/scoring-config.json
*/
import { readFile } from 'fs/promises';
import { existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Default scoring configuration values
* These match the original hardcoded values and are used as fallback
*/
const DEFAULTS = {
authenticity: {
humanMarkerWeight: 10,
aiTellPenalty: 15,
baseScore: 30,
},
issueScoring: {
criticalPenalty: 10,
warningPenalty: 3,
aiPatternMultiplier: 0.5,
aiPatternNormalizer: 20,
},
voiceDetection: {
mixedVoiceThreshold: 1.5,
defaultMixedConfidence: 50,
markerWeights: {
strong: 3,
moderate: 2,
weak: 1,
},
},
thresholds: {
passScore: 70,
lowScoreWarning: 50,
highAIPatternWarning: 30,
},
};
let cachedConfig = null;
/**
* Known locations for scoring configuration
*/
function getConfigPaths() {
return [
// Project-level override
join(process.cwd(), 'scoring-config.json'),
join(process.cwd(), '.aiwg', 'scoring-config.json'),
// Writing-quality addon location (relative to this file when installed)
join(__dirname, '..', '..', 'agentic', 'code', 'addons', 'writing-quality', 'validation', 'scoring-config.json'),
// Writing-quality addon location (from repo root)
join(process.cwd(), 'agentic', 'code', 'addons', 'writing-quality', 'validation', 'scoring-config.json'),
];
}
/**
* Parse raw JSON config into typed values
*/
function parseConfig(content) {
const raw = JSON.parse(content);
return {
authenticity: raw.authenticity ? {
humanMarkerWeight: raw.authenticity.humanMarkerWeight?.value ?? raw.authenticity.humanMarkerWeight,
aiTellPenalty: raw.authenticity.aiTellPenalty?.value ?? raw.authenticity.aiTellPenalty,
baseScore: raw.authenticity.baseScore?.value ?? raw.authenticity.baseScore,
} : undefined,
issueScoring: raw.issueScoring ? {
criticalPenalty: raw.issueScoring.criticalPenalty?.value ?? raw.issueScoring.criticalPenalty,
warningPenalty: raw.issueScoring.warningPenalty?.value ?? raw.issueScoring.warningPenalty,
aiPatternMultiplier: raw.issueScoring.aiPatternMultiplier?.value ?? raw.issueScoring.aiPatternMultiplier,
aiPatternNormalizer: raw.issueScoring.aiPatternNormalizer?.value ?? raw.issueScoring.aiPatternNormalizer,
} : undefined,
voiceDetection: raw.voiceDetection ? {
mixedVoiceThreshold: raw.voiceDetection.mixedVoiceThreshold?.value ?? raw.voiceDetection.mixedVoiceThreshold,
defaultMixedConfidence: raw.voiceDetection.defaultMixedConfidence?.value ?? raw.voiceDetection.defaultMixedConfidence,
markerWeights: {
strong: raw.voiceDetection.markerWeights?.strong?.value ?? raw.voiceDetection.markerWeights?.strong ?? DEFAULTS.voiceDetection.markerWeights.strong,
moderate: raw.voiceDetection.markerWeights?.moderate?.value ?? raw.voiceDetection.markerWeights?.moderate ?? DEFAULTS.voiceDetection.markerWeights.moderate,
weak: raw.voiceDetection.markerWeights?.weak?.value ?? raw.voiceDetection.markerWeights?.weak ?? DEFAULTS.voiceDetection.markerWeights.weak,
},
} : undefined,
thresholds: raw.thresholds ? {
passScore: raw.thresholds.passScore?.value ?? raw.thresholds.passScore,
lowScoreWarning: raw.thresholds.lowScoreWarning?.value ?? raw.thresholds.lowScoreWarning,
highAIPatternWarning: raw.thresholds.highAIPatternWarning?.value ?? raw.thresholds.highAIPatternWarning,
} : undefined,
};
}
/**
* Deep merge config with defaults
*/
function mergeConfig(defaults, overrides) {
return {
authenticity: {
...defaults.authenticity,
...overrides.authenticity,
},
issueScoring: {
...defaults.issueScoring,
...overrides.issueScoring,
},
voiceDetection: {
...defaults.voiceDetection,
...overrides.voiceDetection,
markerWeights: {
...defaults.voiceDetection.markerWeights,
...overrides.voiceDetection?.markerWeights,
},
},
thresholds: {
...defaults.thresholds,
...overrides.thresholds,
},
};
}
/**
* Load scoring configuration asynchronously
* Searches known locations and merges with defaults
*/
export async function loadScoringConfig() {
if (cachedConfig) {
return cachedConfig;
}
for (const configPath of getConfigPaths()) {
if (existsSync(configPath)) {
try {
const content = await readFile(configPath, 'utf-8');
cachedConfig = mergeConfig(DEFAULTS, parseConfig(content));
return cachedConfig;
}
catch {
// Try next location
continue;
}
}
}
// No config found, use defaults
cachedConfig = DEFAULTS;
return cachedConfig;
}
/**
* Get scoring config synchronously
* Returns cached config or defaults if not yet loaded
*/
export function getScoringConfig() {
return cachedConfig || DEFAULTS;
}
/**
* Clear cached configuration
* Useful for testing or when config file has changed
*/
export function clearScoringConfigCache() {
cachedConfig = null;
}
/**
* Get default scoring configuration
* Useful for documentation or reset functionality
*/
export function getDefaultScoringConfig() {
return { ...DEFAULTS };
}
//# sourceMappingURL=scoring-config-loader.js.map