Commit all workspace changes from current session

This commit is contained in:
JC Beasley
2026-07-05 12:50:33 -07:00
parent 93b21a8164
commit 3d855cfd11
74 changed files with 2204 additions and 2491 deletions
+348
View File
@@ -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);
}
}
}
+206
View File
@@ -0,0 +1,206 @@
#!/usr/bin/env node
/**
* Orchestrator - Main Integration Point
*
* Ties together: Pipeline → Router → LLM → Validator → Format Locker
*/
const ContextPipeline = require('./pipeline');
const WorkflowRouter = require('./workflow-router');
const ResponseValidator = require('./validator');
const FormatLocker = require('./format-locker');
class AgentOrchestrator {
constructor(options = {}) {
this.workspace = options.workspace || '/home/jcbeasley/.openclaw/workspace';
this.verbose = options.verbose || false;
this.autoFix = options.autoFix !== false;
this.pipeline = new ContextPipeline();
this.router = new WorkflowRouter();
}
/**
* Main entry point - process user request through full pipeline
*/
async process(userInput, context = {}) {
const startTime = Date.now();
this.log('=== Starting Orchestration ===');
this.log(`Input: ${userInput.substring(0, 100)}...`);
try {
// Step 1: Build context
this.log('Step 1: Building context...');
const contextPacket = await this.buildContext(userInput, context);
// Step 2: Classify intent and route
this.log('Step 2: Classifying intent...');
const route = this.router.route(userInput, context);
// Step 3: Prepare system prompt
this.log(`Step 3: Workflow = ${route.workflow} (${(route.confidence * 100).toFixed(1)}% confidence)`);
const systemPrompt = this.buildSystemPrompt(route, contextPacket);
// Step 4: Get response (this would call LLM)
// For now, return the prompt for testing
const response = {
systemPrompt: systemPrompt,
workflow: route.workflow,
confidence: route.confidence,
context: contextPacket
};
// Step 5: Validate response
this.log('Step 4: Validating...');
const validation = this.validateResponse(response.systemPrompt, route.workflow);
// Step 6: Enforce format
this.log('Step 5: Enforcing format...');
const formatted = this.enforceFormat(response.systemPrompt, route.workflow);
const duration = Date.now() - startTime;
this.log(`=== Complete in ${duration}ms ===`);
return {
...response,
validation: validation,
format: formatted,
metadata: {
duration: duration,
timestamp: new Date().toISOString()
}
};
} catch (error) {
console.error('Orchestration error:', error);
return {
error: error.message,
input: userInput,
metadata: {
duration: Date.now() - startTime,
timestamp: new Date().toISOString()
}
};
}
}
/**
* Build context packet from all sources
*/
async buildContext(userInput, context) {
// Load rules
await this.pipeline.loadRules();
// Load preferences
await this.pipeline.loadPreferences();
// Load relevant memory
await this.pipeline.loadRelevantMemory(userInput, 5);
// Build final packet
const packet = this.pipeline.buildPacket(userInput);
// Add session context
packet.project = context.project || process.env.CURRENT_PROJECT;
packet.user = context.user || 'JC';
return packet;
}
/**
* Build combined system prompt
*/
buildSystemPrompt(route, contextPacket) {
const parts = [
'# System Instructions',
'',
contextPacket.system,
'',
'---',
'',
'# Workflow Mode',
route.systemPrompt,
'',
'---',
'',
'# Current Context',
`Project: ${contextPacket.project || 'None'}`,
`User: ${contextPacket.user || 'Unknown'}`,
'',
'---',
'',
'# Memory',
contextPacket.memory.substring(0, 1000) // Truncate for brevity
];
return parts.join('\n');
}
/**
* Validate response against rules
*/
validateResponse(response, workflow) {
const validator = new ResponseValidator(workflow);
return validator.validate(response);
}
/**
* Enforce format compliance
*/
enforceFormat(response, workflow) {
const locker = new FormatLocker(workflow);
return locker.enforceFormat(response, this.autoFix);
}
/**
* Get format prompt for LLM
*/
getFormatPrompt(workflow) {
const locker = new FormatLocker(workflow);
return locker.getFormatPrompt();
}
/**
* Log with verbosity control
*/
log(message) {
if (this.verbose) {
console.log(message);
}
}
/**
* Quick classification without full processing
*/
classify(userInput) {
return this.router.classifyIntent(userInput);
}
/**
* Get workflow config
*/
getWorkflow(name) {
return this.router.getWorkflow(name);
}
}
// Export for use
module.exports = AgentOrchestrator;
// CLI usage
if (require.main === module) {
const userInput = process.argv[2] || 'implement a new feature';
const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
const orchestrator = new AgentOrchestrator({ verbose: verbose });
orchestrator.process(userInput, { project: process.env.CURRENT_PROJECT })
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error('Error:', err);
process.exit(1);
});
}
+288
View File
@@ -0,0 +1,288 @@
#!/usr/bin/env node
/**
* Workflow Router - Intent Classification and Routing
*
* Before responding, classify intent and route to fixed workflow template
*/
const fs = require('fs');
const path = require('path');
class WorkflowRouter {
constructor() {
this.workflows = new Map();
this.loadWorkflows();
}
/**
* Load all workflow definitions from workflows/ directory
*/
loadWorkflows() {
const workflowsDir = path.join(__dirname, '..', 'workflows');
const workflowFiles = [
'coding.md',
'debug.md',
'deploy.md',
'audit.md',
'planning.md'
];
for (const file of workflowFiles) {
const workflowName = path.basename(file, '.md');
const filePath = path.join(workflowsDir, file);
try {
const content = fs.readFileSync(filePath, 'utf8');
this.workflows.set(workflowName, this.parseWorkflow(content));
} catch (err) {
console.warn(`Workflow ${file} not found, using defaults`);
this.workflows.set(workflowName, this.getDefaultWorkflow(workflowName));
}
}
}
/**
* Parse workflow markdown into structured object
*/
parseWorkflow(content) {
const workflow = {
name: '',
triggers: [],
outputFormat: '',
toolRules: [],
constraints: [],
requiredSections: []
};
// Extract name from first header
const nameMatch = content.match(/^#\s+Workflow:\s*(.+)$/m);
if (nameMatch) workflow.name = nameMatch[1].trim();
// Extract triggers
const triggersMatch = content.match(/\*\*Triggers:\*\*\s*(.+)/);
if (triggersMatch) {
workflow.triggers = triggersMatch[1].split(',').map(t => t.trim().toLowerCase());
}
// Extract output format (code block after "Fixed Output Format")
const formatMatch = content.match(/## Fixed Output Format\s*```markdown\s*([\s\S]*?)```/);
if (formatMatch) workflow.outputFormat = formatMatch[1].trim();
// Extract tool rules
const rulesSection = content.match(/## Tool Access Rules\s*([\s\S]*?)(?=##|$)/);
if (rulesSection) {
workflow.toolRules = rulesSection[1]
.split('\n')
.filter(line => /^\d+\./.test(line))
.map(line => line.replace(/^\d+\.\s*/, '').trim());
}
// Extract constraints
const constraintsSection = content.match(/## Constraints\s*([\s\S]*?)(?=##|$)/);
if (constraintsSection) {
workflow.constraints = constraintsSection[1]
.split('\n')
.filter(line => /^(NO|YES):/.test(line))
.map(line => line.trim());
}
// Extract required sections from format template
const sectionMatches = workflow.outputFormat.match(/^##\s+(.+)$/gm);
if (sectionMatches) {
workflow.requiredSections = sectionMatches.map(s => s.replace(/^##\s*/, '').trim());
}
return workflow;
}
/**
* Classify user intent and return matching workflow
*/
classifyIntent(userInput) {
const input = userInput.toLowerCase();
// Intent patterns with confidence scoring
const intentPatterns = {
'coding': {
patterns: [
/\b(code|implement|build|create|write|develop|feature|fix bug|add\s+\w+\s+to)\b/,
/\b(refactor|optimize|improve|clean up)\b/,
/\b(pull request|pr|commit|merge|branch)\b/,
/\b(api|endpoint|route|handler|controller|model)\b/
],
weight: 1.0
},
'debug': {
patterns: [
/\b(debug|fix|broken|error|bug|issue|problem|crash|fails?|not working)\b/,
/\b(troubleshoot|diagnose|investigate|trace|root cause)\b/,
/\b(exception|stack trace|log|error message)\b/
],
weight: 1.0
},
'deploy': {
patterns: [
/\b(deploy|release|push to|go live|production|staging)\b/,
/\b(docker|container|build|image|registry)\b/,
/\b(rollback|revert|restore|emergency)\b/,
/\b(install|upgrade|update|migrate)\b/
],
weight: 1.0
},
'audit': {
patterns: [
/\b(audit|review|assess|inventory|check|scan|inspect)\b/,
/\b(security|vulnerability|compliance|policy)\b/,
/\b(status|health|state|report|summary)\b/,
/\b(workspace|files|structure|organization)\b/
],
weight: 1.0
},
'planning': {
patterns: [
/\b(plan|design|architecture|strategy|roadmap|timeline)\b/,
/\b(requirement|spec|proposal|approach)\b/,
/\b(breakdown|estimate|scope|prioritize)\b/
],
weight: 0.8
}
};
let bestMatch = null;
let bestScore = 0;
for (const [workflowName, config] of Object.entries(intentPatterns)) {
let score = 0;
let matches = 0;
for (const pattern of config.patterns) {
if (pattern.test(input)) {
matches++;
score += config.weight;
}
}
// Boost score for multiple matches
if (matches > 1) score *= 1.2;
if (score > bestScore) {
bestScore = score;
bestMatch = workflowName;
}
}
// Default to coding if no strong match
if (bestScore < 0.5 || !bestMatch) {
bestMatch = 'coding';
bestScore = 0.3;
}
return {
workflow: bestMatch,
confidence: Math.min(bestScore / 2, 1.0), // Normalize
allScores: this.getAllScores(intentPatterns, input)
};
}
/**
* Get confidence scores for all workflows
*/
getAllScores(patterns, input) {
const scores = {};
for (const [name, config] of Object.entries(patterns)) {
let score = 0;
for (const pattern of config.patterns) {
if (pattern.test(input)) score += config.weight;
}
scores[name] = score;
}
return scores;
}
/**
* Get workflow by name
*/
getWorkflow(name) {
return this.workflows.get(name) || this.getDefaultWorkflow(name);
}
/**
* Get default workflow template
*/
getDefaultWorkflow(name) {
return {
name: name || 'default',
triggers: ['general'],
outputFormat: '## Summary\n[Response]\n\n## Details\n[Details]',
toolRules: ['Delegate when possible', 'Verify before reporting'],
constraints: ['NO: Assumptions without verification', 'YES: Clear status updates'],
requiredSections: ['Summary', 'Details']
};
}
/**
* Build context-aware system prompt for the classified workflow
*/
buildSystemPrompt(workflowName, context = {}) {
const workflow = this.getWorkflow(workflowName);
const parts = [
`You are in ${workflow.name} workflow mode.`,
'',
'## Required Output Sections',
...workflow.requiredSections.map(s => `- ${s}`),
'',
'## Tool Access Rules',
...workflow.toolRules.map((rule, i) => `${i + 1}. ${rule}`),
'',
'## Constraints',
...workflow.constraints,
'',
'## Format Template',
'```markdown',
workflow.outputFormat,
'```'
];
if (context.project) {
parts.push('', `## Current Project: ${context.project}`);
}
return parts.join('\n');
}
/**
* Main entry point: classify and route
*/
route(userInput, context = {}) {
const classification = this.classifyIntent(userInput);
const workflow = this.getWorkflow(classification.workflow);
const systemPrompt = this.buildSystemPrompt(classification.workflow, context);
return {
workflow: classification.workflow,
confidence: classification.confidence,
workflowConfig: workflow,
systemPrompt: systemPrompt,
context: context
};
}
}
// Export for use
module.exports = WorkflowRouter;
// CLI usage
if (require.main === module) {
const router = new WorkflowRouter();
const userInput = process.argv[2] || 'implement a new feature';
const result = router.route(userInput, { project: process.env.CURRENT_PROJECT });
console.log('=== Workflow Classification ===');
console.log(`Workflow: ${result.workflow}`);
console.log(`Confidence: ${(result.confidence * 100).toFixed(1)}%`);
console.log('\n=== System Prompt ===');
console.log(result.systemPrompt);
}