Commit all workspace changes from current session
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Format Locker - Enforce Structured Output Templates
|
||||
*
|
||||
* Ensures responses follow prescribed formats based on workflow type
|
||||
*/
|
||||
|
||||
class FormatLocker {
|
||||
constructor(workflow) {
|
||||
this.workflow = workflow;
|
||||
this.templates = this.loadTemplates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load format templates for each workflow type
|
||||
*/
|
||||
loadTemplates() {
|
||||
return {
|
||||
coding: {
|
||||
requiredSections: ['Summary', 'Files Modified', 'Verification', 'Decisions Made'],
|
||||
optionalSections: ['Still Open', 'Testing Notes'],
|
||||
format: `
|
||||
## Summary
|
||||
[What changed - 1-2 sentences]
|
||||
|
||||
## Files Modified
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| file1.py | [description] |
|
||||
| file2.js | [description] |
|
||||
|
||||
## Verification
|
||||
- [ ] Tests pass: \`command\`
|
||||
- [ ] Lint passes: \`command\`
|
||||
- [ ] Manual verification: [how]
|
||||
|
||||
## Decisions Made
|
||||
- [Decision 1]
|
||||
- [Decision 2]
|
||||
|
||||
## Still Open
|
||||
- [ ] [if any]
|
||||
`.trim(),
|
||||
validators: {
|
||||
'Files Modified': (content) => content.includes('|') && content.includes('File'),
|
||||
'Verification': (content) => content.includes('- [ ]') || content.includes('- [x]'),
|
||||
'Decisions Made': (content) => content.startsWith('- ')
|
||||
}
|
||||
},
|
||||
|
||||
debug: {
|
||||
requiredSections: ['Problem', 'Cause', 'Fix', 'Validation'],
|
||||
optionalSections: ['Prevention', 'Impact'],
|
||||
format: `
|
||||
## Problem
|
||||
[Symptom - what was observed]
|
||||
|
||||
## Cause
|
||||
[Root cause - why it happened]
|
||||
|
||||
## Fix
|
||||
[What was changed to resolve it]
|
||||
|
||||
## Validation
|
||||
[How it was verified fixed]
|
||||
`.trim(),
|
||||
validators: {
|
||||
'Problem': (content) => content.length > 10,
|
||||
'Cause': (content) => content.length > 10,
|
||||
'Fix': (content) => content.length > 10,
|
||||
'Validation': (content) => content.length > 10
|
||||
}
|
||||
},
|
||||
|
||||
deploy: {
|
||||
requiredSections: ['Deployment Summary', 'Pre-Deployment', 'Changes Applied', 'Verification'],
|
||||
optionalSections: ['Rollback', 'Post-Deployment Notes'],
|
||||
format: `
|
||||
## Deployment Summary
|
||||
[What and where]
|
||||
|
||||
## Pre-Deployment
|
||||
- [x] Backup created
|
||||
- [x] Tests pass
|
||||
- [x] Rollback plan documented
|
||||
|
||||
## Changes Applied
|
||||
| Step | Command | Status |
|
||||
|------|---------|--------|
|
||||
| 1 | [command] | ✅ |
|
||||
| 2 | [command] | ✅ |
|
||||
|
||||
## Verification
|
||||
- [x] Service responding: [url]
|
||||
- [x] Logs normal: [check]
|
||||
- [x] Smoke test: [result]
|
||||
`.trim(),
|
||||
validators: {
|
||||
'Pre-Deployment': (content) => content.includes('- [x]') || content.includes('- [ ]'),
|
||||
'Changes Applied': (content) => content.includes('|'),
|
||||
'Verification': (content) => content.includes('- [x]') || content.includes('- [ ]')
|
||||
}
|
||||
},
|
||||
|
||||
audit: {
|
||||
requiredSections: ['Scope', 'Findings', 'Recommendations'],
|
||||
optionalSections: ['Risk Assessment', 'Priority Matrix', 'Action Items'],
|
||||
format: `
|
||||
## Scope
|
||||
[What was audited]
|
||||
|
||||
## Findings
|
||||
| Item | Status | Severity |
|
||||
|------|--------|----------|
|
||||
| Finding 1 | ✅/⚠️/❌ | Low/Med/High |
|
||||
| Finding 2 | ✅/⚠️/❌ | Low/Med/High |
|
||||
|
||||
## Recommendations
|
||||
1. **[Priority]** [Action to take]
|
||||
2. **[Priority]** [Action to take]
|
||||
`.trim(),
|
||||
validators: {
|
||||
'Findings': (content) => content.includes('|'),
|
||||
'Recommendations': (content) => /^\d+\./.test(content)
|
||||
}
|
||||
},
|
||||
|
||||
planning: {
|
||||
requiredSections: ['Goal', 'Approach', 'Breakdown', 'Timeline'],
|
||||
optionalSections: ['Dependencies', 'Risks', 'Acceptance Criteria'],
|
||||
format: `
|
||||
## Goal
|
||||
[What we're trying to achieve]
|
||||
|
||||
## Approach
|
||||
[High-level strategy]
|
||||
|
||||
## Breakdown
|
||||
| Task | Owner | Estimate |
|
||||
|------|-------|----------|
|
||||
| Task 1 | [agent] | [time] |
|
||||
| Task 2 | [agent] | [time] |
|
||||
|
||||
## Timeline
|
||||
- Phase 1: [duration] - [deliverable]
|
||||
- Phase 2: [duration] - [deliverable]
|
||||
`.trim(),
|
||||
validators: {
|
||||
'Breakdown': (content) => content.includes('|'),
|
||||
'Timeline': (content) => content.includes('- Phase')
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template for current workflow
|
||||
*/
|
||||
getTemplate() {
|
||||
return this.templates[this.workflow] || this.templates['coding'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that response follows required format
|
||||
*/
|
||||
validateFormat(response) {
|
||||
const template = this.getTemplate();
|
||||
const results = {
|
||||
passed: true,
|
||||
missingSections: [],
|
||||
malformedSections: [],
|
||||
suggestions: []
|
||||
};
|
||||
|
||||
// Check for required sections
|
||||
for (const section of template.requiredSections) {
|
||||
const sectionPattern = new RegExp(`##\\s*${section.replace(/\s+/g, '\\s+')}`, 'i');
|
||||
if (!sectionPattern.test(response)) {
|
||||
results.missingSections.push(section);
|
||||
results.passed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and validate section contents
|
||||
const sections = this.extractSections(response);
|
||||
for (const [name, content] of Object.entries(sections)) {
|
||||
if (template.validators[name]) {
|
||||
const isValid = template.validators[name](content);
|
||||
if (!isValid) {
|
||||
results.malformedSections.push(name);
|
||||
results.passed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate suggestions
|
||||
if (results.missingSections.length > 0) {
|
||||
results.suggestions.push(`Add missing sections: ${results.missingSections.join(', ')}`);
|
||||
}
|
||||
if (results.malformedSections.length > 0) {
|
||||
results.suggestions.push(`Fix format in sections: ${results.malformedSections.join(', ')}`);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract sections from markdown response
|
||||
*/
|
||||
extractSections(response) {
|
||||
const sections = {};
|
||||
const sectionRegex = /^##\s+(.+)$/gm;
|
||||
let match;
|
||||
|
||||
const sectionNames = [];
|
||||
const sectionPositions = [];
|
||||
|
||||
while ((match = sectionRegex.exec(response)) !== null) {
|
||||
sectionNames.push(match[1].trim());
|
||||
sectionPositions.push(match.index);
|
||||
}
|
||||
|
||||
// Extract content between sections
|
||||
for (let i = 0; i < sectionNames.length; i++) {
|
||||
const startPos = sectionPositions[i] + sectionNames[i].length + 3; // +3 for "## "
|
||||
const endPos = i < sectionNames.length - 1 ? sectionPositions[i + 1] : response.length;
|
||||
sections[sectionNames[i]] = response.substring(startPos, endPos).trim();
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply format template to content
|
||||
*/
|
||||
applyTemplate(content, customData = {}) {
|
||||
const template = this.getTemplate();
|
||||
let formatted = template.format;
|
||||
|
||||
// Replace placeholders with actual content
|
||||
for (const [key, value] of Object.entries(content)) {
|
||||
const placeholder = `[${key}]`;
|
||||
if (formatted.includes(placeholder)) {
|
||||
formatted = formatted.replace(placeholder, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace custom data
|
||||
for (const [key, value] of Object.entries(customData)) {
|
||||
const placeholder = `{${key}}`;
|
||||
if (formatted.includes(placeholder)) {
|
||||
formatted = formatted.replace(new RegExp(`\\{${key}\\}`, 'g'), value);
|
||||
}
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a response to ensure format compliance
|
||||
*/
|
||||
enforceFormat(response, autoFix = true) {
|
||||
const validation = this.validateFormat(response);
|
||||
|
||||
if (validation.passed) {
|
||||
return {
|
||||
response: response,
|
||||
status: 'passed',
|
||||
fixes: []
|
||||
};
|
||||
}
|
||||
|
||||
if (!autoFix) {
|
||||
return {
|
||||
response: response,
|
||||
status: 'failed',
|
||||
errors: validation,
|
||||
suggestion: this.getTemplate().format
|
||||
};
|
||||
}
|
||||
|
||||
// Auto-fix: add missing sections
|
||||
let fixed = response;
|
||||
const sections = this.extractSections(response);
|
||||
const template = this.getTemplate();
|
||||
|
||||
for (const section of validation.missingSections) {
|
||||
// Add missing section at end
|
||||
fixed += `\n\n## ${section}\n[To be completed]`;
|
||||
}
|
||||
|
||||
return {
|
||||
response: fixed,
|
||||
status: 'fixed',
|
||||
fixes: validation.suggestions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get format requirements as system prompt addition
|
||||
*/
|
||||
getFormatPrompt() {
|
||||
const template = this.getTemplate();
|
||||
|
||||
return `
|
||||
## Output Format Requirements
|
||||
|
||||
You MUST follow this structure exactly:
|
||||
|
||||
${template.format}
|
||||
|
||||
### Required Sections
|
||||
${template.requiredSections.map(s => `- ${s}`).join('\n')}
|
||||
|
||||
### Rules
|
||||
- All required sections must be present
|
||||
- Use proper markdown tables where shown
|
||||
- Include checkboxes for verification items
|
||||
- Be specific, not vague
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use
|
||||
module.exports = FormatLocker;
|
||||
|
||||
// CLI usage
|
||||
if (require.main === module) {
|
||||
const workflow = process.argv[2] || 'coding';
|
||||
const responseFile = process.argv[3];
|
||||
|
||||
const locker = new FormatLocker(workflow);
|
||||
|
||||
console.log('=== Format Template ===');
|
||||
console.log(locker.getTemplate().format);
|
||||
|
||||
if (responseFile) {
|
||||
const fs = require('fs');
|
||||
const response = fs.readFileSync(responseFile, 'utf8');
|
||||
|
||||
console.log('\n=== Validation ===');
|
||||
const result = locker.enforceFormat(response, true);
|
||||
console.log('Status:', result.status);
|
||||
if (result.fixes) {
|
||||
console.log('Fixes:', result.fixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user