@endlessblink/like-i-said-v2
Version:
Task Management & Memory for Claude - Track tasks, remember context, and maintain continuity across sessions with 27 powerful tools. Works with Claude Desktop and Claude Code.
379 lines (348 loc) • 13.5 kB
JavaScript
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
/**
* Standards Configuration Parser
* Reads quality standards from markdown configuration file
*/
class StandardsConfigParser {
constructor(configPath = null) {
// IMPORTANT: memory-quality-standards.md is located in docs/ directory
// If you move this file, also update dashboard-server-bridge.js:524
this.configPath = configPath || path.join(__dirname, '..', 'docs', 'memory-quality-standards.md');
this.config = null;
this.lastModified = null;
}
/**
* Load and parse the configuration file
*/
loadConfig() {
try {
const content = fs.readFileSync(this.configPath, 'utf8');
const stats = fs.statSync(this.configPath);
// Check if file has been modified
if (this.lastModified && stats.mtime.getTime() === this.lastModified) {
return this.config; // Return cached config
}
this.lastModified = stats.mtime.getTime();
this.config = this.parseMarkdownConfig(content);
return this.config;
} catch (error) {
// Only log once to avoid spam
if (!this.errorLogged) {
console.warn('Standards config file not found, using defaults:', this.configPath);
this.errorLogged = true;
}
return this.getDefaultConfig();
}
}
/**
* Parse markdown content to extract YAML blocks
*/
parseMarkdownConfig(content) {
const config = {
title: {},
description: {},
scoring: {},
compliance: {},
validation: {},
dashboard: {}
};
// Extract YAML blocks from markdown
const yamlBlocks = content.match(/```yaml\n([\s\S]*?)```/g) || [];
yamlBlocks.forEach(block => {
// Remove markdown code block markers
const yamlContent = block.replace(/```yaml\n|```/g, '');
try {
const parsed = yaml.load(yamlContent);
// Determine which section this belongs to based on content
if (parsed.min_length !== undefined && parsed.max_length !== undefined) {
// Length requirements
if (!config.title.length) {
config.title.length = parsed;
} else if (!config.description.length) {
config.description.length = parsed;
}
} else if (parsed.strong_actions) {
config.title.strongActions = parsed.strong_actions;
} else if (parsed.weak_words) {
config.title.weakWords = parsed.weak_words;
} else if (parsed.forbidden_patterns) {
config.title.forbiddenPatterns = parsed.forbidden_patterns.map(p => ({
pattern: new RegExp(p.pattern, 'i'),
description: p.description
}));
} else if (parsed.good_examples && parsed.good_examples[0]?.title) {
config.title.goodExamples = parsed.good_examples;
} else if (parsed.bad_examples && parsed.bad_examples[0]?.title) {
config.title.badExamples = parsed.bad_examples;
} else if (parsed.must_have) {
config.description.requirements = parsed.must_have;
} else if (parsed.forbidden_phrases) {
config.description.forbiddenPhrases = parsed.forbidden_phrases;
} else if (parsed.scoring_weights) {
config.scoring = parsed.scoring_weights;
} else if (parsed.thresholds) {
config.compliance = parsed.thresholds;
} else if (parsed.validation) {
config.validation = parsed.validation;
} else if (parsed.dashboard_features) {
config.dashboard = parsed.dashboard_features;
}
} catch (e) {
console.warn('Failed to parse YAML block:', e.message);
}
});
// Extract required elements from markdown
const requiredElements = this.extractRequiredElements(content);
config.title.requiredElements = requiredElements;
return config;
}
/**
* Extract required elements from markdown text
*/
extractRequiredElements(content) {
const required = {
mustHaveAction: true,
mustHaveSubject: true,
mustBeSpecific: true,
properCapitalization: true
};
// Look for required elements section
const requiredSection = content.match(/### Required Elements([\s\S]*?)###/);
if (requiredSection) {
const text = requiredSection[1];
required.mustHaveAction = text.includes('must_have_action**: true');
required.mustHaveSubject = text.includes('must_have_subject**: true');
required.mustBeSpecific = text.includes('must_be_specific**: true');
required.properCapitalization = text.includes('proper_capitalization**: true');
}
return required;
}
/**
* Get title standards from config
*/
getTitleStandards() {
const config = this.loadConfig();
return {
minLength: config.title.length?.min_length || 15,
maxLength: config.title.length?.max_length || 80,
optimalLength: {
min: config.title.length?.optimal_min || 20,
max: config.title.length?.optimal_max || 60
},
mustHave: config.title.requiredElements || {
specificAction: true,
specificSubject: true,
noGenericWords: true,
noTimestamps: true,
noTruncation: true,
properCapitalization: true
},
forbiddenPatterns: config.title.forbiddenPatterns || [],
weakWords: config.title.weakWords || [],
strongActions: config.title.strongActions || [],
goodExamples: config.title.goodExamples || [],
badExamples: config.title.badExamples || []
};
}
/**
* Get description standards from config
*/
getDescriptionStandards() {
const config = this.loadConfig();
return {
minLength: config.description.length?.min_length || 50,
maxLength: config.description.length?.max_length || 300,
optimalLength: {
min: config.description.length?.optimal_min || 80,
max: config.description.length?.optimal_max || 200
},
requirements: config.description.requirements || {
context: true,
actions: true,
technical_details: true,
outcome: false
},
forbiddenPhrases: config.description.forbiddenPhrases || [],
goodExamples: config.description.goodExamples || [],
badExamples: config.description.badExamples || []
};
}
/**
* Get scoring configuration
*/
getScoringConfig() {
const config = this.loadConfig();
return config.scoring || this.getDefaultScoring();
}
/**
* Get compliance thresholds
*/
getComplianceThresholds() {
const config = this.loadConfig();
return config.compliance || {
excellent: 90,
good: 70,
fair: 60,
poor: 40,
critical: 0,
passing_score: 70,
target_compliance: 85
};
}
/**
* Get validation rules
*/
getValidationRules() {
const config = this.loadConfig();
return config.validation || {
strict_mode: true,
auto_fix: {
remove_dates: true,
remove_session_words: true,
add_action_words: true,
fix_capitalization: true,
remove_forbidden_patterns: true
}
};
}
/**
* Get dashboard configuration
*/
getDashboardConfig() {
const config = this.loadConfig();
return config.dashboard || {
show_quality_score: true,
show_compliance_badge: true,
highlight_issues: true,
suggest_improvements: true,
quality_indicators: {
excellent: '🟢',
good: '🟡',
poor: '🔴'
}
};
}
/**
* Watch configuration file for changes
*/
watchConfig(callback) {
if (!fs.existsSync(this.configPath)) {
console.warn('Standards config file not found');
return;
}
fs.watchFile(this.configPath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
console.log('Standards configuration updated');
this.loadConfig(); // Reload config
if (callback) callback(this.config);
}
});
}
/**
* Get default configuration if file not found
*/
getDefaultConfig() {
return {
title: {
length: { min_length: 15, max_length: 80, optimal_min: 20, optimal_max: 60 },
strongActions: ['implement', 'fix', 'add', 'create', 'configure'],
weakWords: ['improvements', 'session', 'update', 'status'],
forbiddenPatterns: [],
requiredElements: {
mustHaveAction: true,
mustHaveSubject: true,
mustBeSpecific: true,
properCapitalization: true
}
},
description: {
length: { min_length: 50, max_length: 300, optimal_min: 80, optimal_max: 200 },
requirements: {
context: true,
actions: true,
technical_details: true
},
forbiddenPhrases: ['various things', 'different stuff']
},
scoring: this.getDefaultScoring(),
compliance: {
excellent: 90,
good: 70,
fair: 60,
poor: 40,
critical: 0,
passing_score: 70,
target_compliance: 85
}
};
}
/**
* Get default scoring weights
*/
getDefaultScoring() {
return {
title: {
weight: 0.4,
components: {
specificity: 0.35,
action_word: 0.25,
length: 0.20,
clarity: 0.20
}
},
description: {
weight: 0.4,
components: {
technical_detail: 0.30,
completeness: 0.25,
structure: 0.25,
length: 0.20
}
},
metadata: {
weight: 0.2,
components: {
required_fields: 0.50,
field_validity: 0.30,
completeness: 0.20
}
}
};
}
/**
* Export configuration for dashboard
*/
exportForDashboard() {
const config = this.loadConfig();
const dashboardConfig = this.getDashboardConfig();
const thresholds = this.getComplianceThresholds();
return {
qualityThresholds: {
excellent: thresholds.excellent,
good: thresholds.good,
fair: thresholds.fair,
poor: thresholds.poor,
critical: thresholds.critical,
passing: thresholds.passing_score
},
indicators: dashboardConfig.quality_indicators,
features: {
showScore: dashboardConfig.show_quality_score,
showBadge: dashboardConfig.show_compliance_badge,
highlightIssues: dashboardConfig.highlight_issues,
showSuggestions: dashboardConfig.suggest_improvements
},
validation: {
titleMinLength: config.title.length?.min_length || 15,
titleMaxLength: config.title.length?.max_length || 80,
descriptionMinLength: config.description.length?.min_length || 50,
descriptionMaxLength: config.description.length?.max_length || 300
}
};
}
}
module.exports = { StandardsConfigParser };
// Also export a singleton instance
module.exports.standardsConfig = new StandardsConfigParser();