wogiflow 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.workflow/specs/architecture.md.template +24 -0
- package/.workflow/specs/stack.md.template +33 -0
- package/.workflow/specs/testing.md.template +36 -0
- package/README.md +90 -1
- package/package.json +1 -1
- package/scripts/MEMORY-ARCHITECTURE.md +150 -0
- package/scripts/flow +20 -19
- package/scripts/flow-auto-context.js +97 -3
- package/scripts/flow-conflict-resolver.js +735 -0
- package/scripts/flow-context-gatherer.js +520 -0
- package/scripts/flow-context-monitor.js +148 -19
- package/scripts/flow-damage-control.js +5 -1
- package/scripts/flow-export-profile +168 -1
- package/scripts/flow-import-profile +257 -6
- package/scripts/flow-instruction-richness.js +182 -18
- package/scripts/flow-knowledge-router.js +2 -0
- package/scripts/flow-knowledge-sync.js +2 -0
- package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
- package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
- package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
- package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
- package/scripts/flow-memory-db.js +386 -1
- package/scripts/flow-memory-sync.js +2 -0
- package/scripts/flow-model-adapter.js +53 -29
- package/scripts/flow-model-router.js +246 -1
- package/scripts/flow-morning.js +94 -0
- package/scripts/flow-onboard +223 -10
- package/scripts/flow-orchestrate-validation.js +539 -0
- package/scripts/flow-orchestrate.js +16 -507
- package/scripts/flow-pattern-extractor.js +1265 -0
- package/scripts/flow-prompt-composer.js +222 -2
- package/scripts/flow-quality-guard.js +594 -0
- package/scripts/flow-section-index.js +713 -0
- package/scripts/flow-section-resolver.js +484 -0
- package/scripts/flow-session-end.js +188 -2
- package/scripts/flow-skill-create.js +19 -3
- package/scripts/flow-skill-matcher.js +122 -7
- package/scripts/flow-statusline-setup.js +218 -0
- package/scripts/flow-step-review.js +19 -0
- package/scripts/flow-tech-debt.js +734 -0
- package/scripts/flow-utils.js +2 -0
- package/scripts/hooks/core/long-input-gate.js +293 -0
- package/scripts/flow-parallel-detector.js +0 -399
- package/scripts/flow-parallel-dispatch.js +0 -987
- /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Quality Guard
|
|
5
|
+
*
|
|
6
|
+
* Ensures context optimization doesn't compromise quality.
|
|
7
|
+
* Part of Smart Context System (Phase 5)
|
|
8
|
+
*
|
|
9
|
+
* Core Principle: Quality is non-negotiable. If dynamic context
|
|
10
|
+
* fails quality checks, automatically escalate to full context.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Verify context sufficiency before execution
|
|
14
|
+
* - Identify required patterns for task types
|
|
15
|
+
* - Auto-escalate to full context on quality failures
|
|
16
|
+
* - Log context gaps for learning and improvement
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const { verifyContextSufficiency } = require('./flow-quality-guard');
|
|
20
|
+
*
|
|
21
|
+
* const check = verifyContextSufficiency(task, context, model);
|
|
22
|
+
* if (!check.sufficient) {
|
|
23
|
+
* // Auto-escalate to full context
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const {
|
|
30
|
+
PATHS,
|
|
31
|
+
PROJECT_ROOT,
|
|
32
|
+
fileExists,
|
|
33
|
+
readFile,
|
|
34
|
+
writeFile,
|
|
35
|
+
info,
|
|
36
|
+
warn,
|
|
37
|
+
success,
|
|
38
|
+
estimateTokens
|
|
39
|
+
} = require('./flow-utils');
|
|
40
|
+
|
|
41
|
+
// ============================================================
|
|
42
|
+
// Configuration
|
|
43
|
+
// ============================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Patterns required for different task categories
|
|
47
|
+
*/
|
|
48
|
+
const REQUIRED_PATTERNS = {
|
|
49
|
+
security: {
|
|
50
|
+
keywords: ['security', 'auth', 'password', 'token', 'encrypt', 'validate', 'sanitize'],
|
|
51
|
+
requiredSections: ['security', 'try-catch', 'error-handling'],
|
|
52
|
+
requiredPins: ['security', 'input-validation', 'try-catch']
|
|
53
|
+
},
|
|
54
|
+
component: {
|
|
55
|
+
keywords: ['component', 'button', 'input', 'form', 'modal', 'ui', 'widget'],
|
|
56
|
+
requiredSections: ['component', 'naming', 'variant'],
|
|
57
|
+
requiredPins: ['component', 'naming-convention', 'component-reuse']
|
|
58
|
+
},
|
|
59
|
+
api: {
|
|
60
|
+
keywords: ['api', 'endpoint', 'route', 'controller', 'service', 'request'],
|
|
61
|
+
requiredSections: ['api', 'error-handling', 'validation'],
|
|
62
|
+
requiredPins: ['api', 'error-handling', 'try-catch']
|
|
63
|
+
},
|
|
64
|
+
file: {
|
|
65
|
+
keywords: ['file', 'fs', 'read', 'write', 'path', 'directory'],
|
|
66
|
+
requiredSections: ['file-safety', 'try-catch', 'path-validation'],
|
|
67
|
+
requiredPins: ['try-catch', 'file-safety', 'path-traversal']
|
|
68
|
+
},
|
|
69
|
+
database: {
|
|
70
|
+
keywords: ['database', 'db', 'sql', 'query', 'model', 'migration'],
|
|
71
|
+
requiredSections: ['database', 'query-safety', 'transaction'],
|
|
72
|
+
requiredPins: ['database', 'sql-injection', 'transaction']
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Context gap log path
|
|
78
|
+
*/
|
|
79
|
+
const CONTEXT_GAP_LOG = path.join(PROJECT_ROOT, '.workflow', 'state', 'context-gaps.json');
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Coverage thresholds for context sufficiency
|
|
83
|
+
* Different models need different coverage levels based on their capabilities
|
|
84
|
+
*/
|
|
85
|
+
const COVERAGE_THRESHOLDS = {
|
|
86
|
+
default: 0.7, // Default: 70% coverage required
|
|
87
|
+
comprehensive: 0.85, // Haiku/Local LLMs need higher coverage
|
|
88
|
+
concise: 0.5 // Opus can infer more from less context
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// ============================================================
|
|
92
|
+
// Pattern Analysis
|
|
93
|
+
// ============================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Identify task category from description
|
|
97
|
+
* @param {string} taskDescription - Task description
|
|
98
|
+
* @returns {string[]} - Categories that apply
|
|
99
|
+
*/
|
|
100
|
+
function identifyTaskCategories(taskDescription) {
|
|
101
|
+
const descLower = taskDescription.toLowerCase();
|
|
102
|
+
const categories = [];
|
|
103
|
+
|
|
104
|
+
for (const [category, config] of Object.entries(REQUIRED_PATTERNS)) {
|
|
105
|
+
const matchCount = config.keywords.filter(kw => descLower.includes(kw)).length;
|
|
106
|
+
if (matchCount > 0) {
|
|
107
|
+
categories.push(category);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return categories;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get required patterns for a task
|
|
116
|
+
* @param {string} taskDescription - Task description
|
|
117
|
+
* @returns {Object} - Required patterns
|
|
118
|
+
*/
|
|
119
|
+
function identifyRequiredPatterns(taskDescription) {
|
|
120
|
+
const categories = identifyTaskCategories(taskDescription);
|
|
121
|
+
|
|
122
|
+
const requiredSections = new Set();
|
|
123
|
+
const requiredPins = new Set();
|
|
124
|
+
|
|
125
|
+
for (const category of categories) {
|
|
126
|
+
const config = REQUIRED_PATTERNS[category];
|
|
127
|
+
if (config) {
|
|
128
|
+
config.requiredSections.forEach(s => requiredSections.add(s));
|
|
129
|
+
config.requiredPins.forEach(p => requiredPins.add(p));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
categories,
|
|
135
|
+
sections: Array.from(requiredSections),
|
|
136
|
+
pins: Array.from(requiredPins)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Extract patterns from gathered context
|
|
142
|
+
* @param {Object} context - Context result from gatherContext
|
|
143
|
+
* @returns {Object} - Extracted patterns
|
|
144
|
+
*/
|
|
145
|
+
function extractPatternsFromContext(context) {
|
|
146
|
+
const sections = context.sections || [];
|
|
147
|
+
const includedSections = new Set();
|
|
148
|
+
const includedPins = new Set();
|
|
149
|
+
|
|
150
|
+
for (const section of sections) {
|
|
151
|
+
// Add section category
|
|
152
|
+
if (section.category) {
|
|
153
|
+
includedSections.add(section.category.toLowerCase());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Add section ID parts
|
|
157
|
+
if (section.id) {
|
|
158
|
+
section.id.split(/[-:_]/).forEach(part => {
|
|
159
|
+
if (part.length > 2) {
|
|
160
|
+
includedSections.add(part.toLowerCase());
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add pins
|
|
166
|
+
if (section.pins && Array.isArray(section.pins)) {
|
|
167
|
+
section.pins.forEach(p => includedPins.add(p.toLowerCase()));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Extract pins from content if available
|
|
171
|
+
if (section.content) {
|
|
172
|
+
// Look for common pattern keywords in content
|
|
173
|
+
const contentLower = section.content.toLowerCase();
|
|
174
|
+
if (contentLower.includes('try-catch') || contentLower.includes('try {')) {
|
|
175
|
+
includedPins.add('try-catch');
|
|
176
|
+
}
|
|
177
|
+
if (contentLower.includes('security') || contentLower.includes('secure')) {
|
|
178
|
+
includedPins.add('security');
|
|
179
|
+
}
|
|
180
|
+
if (contentLower.includes('validate') || contentLower.includes('validation')) {
|
|
181
|
+
includedPins.add('validation');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
sections: Array.from(includedSections),
|
|
188
|
+
pins: Array.from(includedPins)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ============================================================
|
|
193
|
+
// Quality Verification
|
|
194
|
+
// ============================================================
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Verify context sufficiency for a task
|
|
198
|
+
* @param {string} taskDescription - Task description
|
|
199
|
+
* @param {Object} context - Gathered context result
|
|
200
|
+
* @param {string} model - Model being used
|
|
201
|
+
* @returns {Object} - { sufficient, missing, coverage, recommendation }
|
|
202
|
+
*/
|
|
203
|
+
function verifyContextSufficiency(taskDescription, context, model) {
|
|
204
|
+
const required = identifyRequiredPatterns(taskDescription);
|
|
205
|
+
const included = extractPatternsFromContext(context);
|
|
206
|
+
|
|
207
|
+
// Check if no specific patterns required
|
|
208
|
+
if (required.sections.length === 0 && required.pins.length === 0) {
|
|
209
|
+
return {
|
|
210
|
+
sufficient: true,
|
|
211
|
+
reason: 'No specific patterns required for this task',
|
|
212
|
+
categories: [],
|
|
213
|
+
coverage: 1.0
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Calculate coverage
|
|
218
|
+
const missingSections = required.sections.filter(s =>
|
|
219
|
+
!included.sections.some(is => is.includes(s) || s.includes(is))
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const missingPins = required.pins.filter(p =>
|
|
223
|
+
!included.pins.some(ip => ip.includes(p) || p.includes(ip))
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const totalRequired = required.sections.length + required.pins.length;
|
|
227
|
+
const totalMissing = missingSections.length + missingPins.length;
|
|
228
|
+
const coverage = totalRequired > 0 ? (totalRequired - totalMissing) / totalRequired : 1.0;
|
|
229
|
+
|
|
230
|
+
// Determine sufficiency threshold based on model
|
|
231
|
+
let minCoverage = COVERAGE_THRESHOLDS.default;
|
|
232
|
+
try {
|
|
233
|
+
const instructionRichness = require('./flow-instruction-richness');
|
|
234
|
+
const modelPrefs = instructionRichness.getModelContextPreferences(model);
|
|
235
|
+
// Models with comprehensive density need higher coverage
|
|
236
|
+
if (modelPrefs.density === 'comprehensive') {
|
|
237
|
+
minCoverage = COVERAGE_THRESHOLDS.comprehensive;
|
|
238
|
+
} else if (modelPrefs.density === 'concise') {
|
|
239
|
+
minCoverage = COVERAGE_THRESHOLDS.concise;
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
// Use default
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const sufficient = coverage >= minCoverage;
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
sufficient,
|
|
249
|
+
coverage,
|
|
250
|
+
minCoverage,
|
|
251
|
+
categories: required.categories,
|
|
252
|
+
required: {
|
|
253
|
+
sections: required.sections,
|
|
254
|
+
pins: required.pins
|
|
255
|
+
},
|
|
256
|
+
included: {
|
|
257
|
+
sections: included.sections,
|
|
258
|
+
pins: included.pins
|
|
259
|
+
},
|
|
260
|
+
missing: {
|
|
261
|
+
sections: missingSections,
|
|
262
|
+
pins: missingPins
|
|
263
|
+
},
|
|
264
|
+
recommendation: sufficient
|
|
265
|
+
? 'Context is sufficient for quality execution'
|
|
266
|
+
: 'Recommend escalating to full context for quality guarantee'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Quick check if context is likely sufficient
|
|
272
|
+
* @param {string} taskDescription - Task description
|
|
273
|
+
* @param {Object} context - Gathered context
|
|
274
|
+
* @returns {boolean} - Quick sufficiency check
|
|
275
|
+
*/
|
|
276
|
+
function isContextLikelySufficient(taskDescription, context) {
|
|
277
|
+
const result = verifyContextSufficiency(taskDescription, context, 'default');
|
|
278
|
+
return result.sufficient;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ============================================================
|
|
282
|
+
// Context Gap Logging
|
|
283
|
+
// ============================================================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Log a context gap for learning
|
|
287
|
+
* @param {Object} params - Gap information
|
|
288
|
+
*/
|
|
289
|
+
function logContextGap(params) {
|
|
290
|
+
const {
|
|
291
|
+
task,
|
|
292
|
+
model,
|
|
293
|
+
context,
|
|
294
|
+
missing,
|
|
295
|
+
timestamp = new Date().toISOString()
|
|
296
|
+
} = params;
|
|
297
|
+
|
|
298
|
+
// Load existing gaps
|
|
299
|
+
let gaps = [];
|
|
300
|
+
try {
|
|
301
|
+
if (fileExists(CONTEXT_GAP_LOG)) {
|
|
302
|
+
const content = readFile(CONTEXT_GAP_LOG);
|
|
303
|
+
gaps = JSON.parse(content);
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
gaps = [];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Add new gap
|
|
310
|
+
gaps.push({
|
|
311
|
+
timestamp,
|
|
312
|
+
task: task.slice(0, 200), // Truncate long tasks
|
|
313
|
+
model,
|
|
314
|
+
sectionsIncluded: context.sections?.length || 0,
|
|
315
|
+
missing,
|
|
316
|
+
escalated: true
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Keep last 100 gaps
|
|
320
|
+
if (gaps.length > 100) {
|
|
321
|
+
gaps = gaps.slice(-100);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Write back
|
|
325
|
+
try {
|
|
326
|
+
const dir = path.dirname(CONTEXT_GAP_LOG);
|
|
327
|
+
if (!fs.existsSync(dir)) {
|
|
328
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
329
|
+
}
|
|
330
|
+
writeFile(CONTEXT_GAP_LOG, JSON.stringify(gaps, null, 2));
|
|
331
|
+
} catch (err) {
|
|
332
|
+
warn(`Could not log context gap: ${err.message}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Get context gap statistics
|
|
338
|
+
* @returns {Object} - Gap statistics
|
|
339
|
+
*/
|
|
340
|
+
function getContextGapStats() {
|
|
341
|
+
try {
|
|
342
|
+
if (!fileExists(CONTEXT_GAP_LOG)) {
|
|
343
|
+
return { totalGaps: 0, recentGaps: 0, commonMissing: [] };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const content = readFile(CONTEXT_GAP_LOG);
|
|
347
|
+
const gaps = JSON.parse(content);
|
|
348
|
+
|
|
349
|
+
// Count common missing patterns
|
|
350
|
+
const missingCount = {};
|
|
351
|
+
for (const gap of gaps) {
|
|
352
|
+
const allMissing = [
|
|
353
|
+
...(gap.missing?.sections || []),
|
|
354
|
+
...(gap.missing?.pins || [])
|
|
355
|
+
];
|
|
356
|
+
for (const m of allMissing) {
|
|
357
|
+
missingCount[m] = (missingCount[m] || 0) + 1;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Sort by frequency
|
|
362
|
+
const commonMissing = Object.entries(missingCount)
|
|
363
|
+
.sort((a, b) => b[1] - a[1])
|
|
364
|
+
.slice(0, 10)
|
|
365
|
+
.map(([pattern, count]) => ({ pattern, count }));
|
|
366
|
+
|
|
367
|
+
// Recent gaps (last 24 hours)
|
|
368
|
+
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
369
|
+
const recentGaps = gaps.filter(g => g.timestamp > oneDayAgo).length;
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
totalGaps: gaps.length,
|
|
373
|
+
recentGaps,
|
|
374
|
+
commonMissing
|
|
375
|
+
};
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return { totalGaps: 0, recentGaps: 0, commonMissing: [], error: err.message };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ============================================================
|
|
382
|
+
// Full Context Fallback
|
|
383
|
+
// ============================================================
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Load full context (fallback when dynamic context insufficient)
|
|
387
|
+
* @param {string} taskDescription - Task description
|
|
388
|
+
* @returns {Promise<Object>} - Full context result
|
|
389
|
+
*/
|
|
390
|
+
async function loadFullContext(taskDescription) {
|
|
391
|
+
// Try to use the context gatherer with maximum tokens
|
|
392
|
+
try {
|
|
393
|
+
const contextGatherer = require('./flow-context-gatherer');
|
|
394
|
+
|
|
395
|
+
return await contextGatherer.gatherContext({
|
|
396
|
+
task: taskDescription,
|
|
397
|
+
model: 'claude-sonnet-4', // Use standard model preferences
|
|
398
|
+
maxTokens: 150000, // Maximum context
|
|
399
|
+
format: 'full'
|
|
400
|
+
});
|
|
401
|
+
} catch (err) {
|
|
402
|
+
// Fallback: return empty context with warning
|
|
403
|
+
warn(`Could not load full context: ${err.message}`);
|
|
404
|
+
return {
|
|
405
|
+
context: '',
|
|
406
|
+
sections: [],
|
|
407
|
+
stats: { fallback: true, error: err.message }
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Gather context with quality guarantee
|
|
414
|
+
* Auto-escalates to full context if dynamic context is insufficient
|
|
415
|
+
*
|
|
416
|
+
* @param {Object} params - { task, model, maxTokens }
|
|
417
|
+
* @returns {Promise<Object>} - Context with quality guarantee
|
|
418
|
+
*/
|
|
419
|
+
async function gatherContextWithQualityGuard(params) {
|
|
420
|
+
const { task, model, maxTokens = null } = params;
|
|
421
|
+
|
|
422
|
+
// First, try dynamic context
|
|
423
|
+
let contextGatherer;
|
|
424
|
+
try {
|
|
425
|
+
contextGatherer = require('./flow-context-gatherer');
|
|
426
|
+
} catch {
|
|
427
|
+
// No context gatherer, return empty
|
|
428
|
+
return { context: '', sections: [], stats: { error: 'Context gatherer not available' } };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const dynamicResult = await contextGatherer.gatherContext({
|
|
432
|
+
task,
|
|
433
|
+
model,
|
|
434
|
+
maxTokens,
|
|
435
|
+
format: 'full'
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Verify sufficiency
|
|
439
|
+
const verification = verifyContextSufficiency(task, dynamicResult, model);
|
|
440
|
+
|
|
441
|
+
if (verification.sufficient) {
|
|
442
|
+
return {
|
|
443
|
+
...dynamicResult,
|
|
444
|
+
qualityGuard: {
|
|
445
|
+
verified: true,
|
|
446
|
+
coverage: verification.coverage,
|
|
447
|
+
escalated: false
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Log the gap
|
|
453
|
+
logContextGap({
|
|
454
|
+
task,
|
|
455
|
+
model,
|
|
456
|
+
context: dynamicResult,
|
|
457
|
+
missing: verification.missing
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Escalate to full context
|
|
461
|
+
info('Quality guard: Escalating to full context for quality guarantee');
|
|
462
|
+
const fullResult = await loadFullContext(task);
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
...fullResult,
|
|
466
|
+
qualityGuard: {
|
|
467
|
+
verified: true,
|
|
468
|
+
coverage: verification.coverage,
|
|
469
|
+
escalated: true,
|
|
470
|
+
reason: verification.recommendation,
|
|
471
|
+
missing: verification.missing
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ============================================================
|
|
477
|
+
// Exports
|
|
478
|
+
// ============================================================
|
|
479
|
+
|
|
480
|
+
module.exports = {
|
|
481
|
+
// Pattern analysis
|
|
482
|
+
identifyTaskCategories,
|
|
483
|
+
identifyRequiredPatterns,
|
|
484
|
+
extractPatternsFromContext,
|
|
485
|
+
|
|
486
|
+
// Quality verification
|
|
487
|
+
verifyContextSufficiency,
|
|
488
|
+
isContextLikelySufficient,
|
|
489
|
+
|
|
490
|
+
// Gap logging
|
|
491
|
+
logContextGap,
|
|
492
|
+
getContextGapStats,
|
|
493
|
+
|
|
494
|
+
// Context with quality guarantee
|
|
495
|
+
loadFullContext,
|
|
496
|
+
gatherContextWithQualityGuard,
|
|
497
|
+
|
|
498
|
+
// Constants
|
|
499
|
+
REQUIRED_PATTERNS,
|
|
500
|
+
CONTEXT_GAP_LOG,
|
|
501
|
+
COVERAGE_THRESHOLDS
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// ============================================================
|
|
505
|
+
// CLI Interface
|
|
506
|
+
// ============================================================
|
|
507
|
+
|
|
508
|
+
async function main() {
|
|
509
|
+
const args = process.argv.slice(2);
|
|
510
|
+
const command = args[0];
|
|
511
|
+
|
|
512
|
+
switch (command) {
|
|
513
|
+
case 'verify': {
|
|
514
|
+
const taskDesc = args.slice(1).join(' ');
|
|
515
|
+
if (!taskDesc) {
|
|
516
|
+
console.error('Usage: flow-quality-guard verify "<task description>"');
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Get context and verify
|
|
521
|
+
try {
|
|
522
|
+
const contextGatherer = require('./flow-context-gatherer');
|
|
523
|
+
const context = await contextGatherer.gatherContext({
|
|
524
|
+
task: taskDesc,
|
|
525
|
+
model: 'claude-sonnet-4',
|
|
526
|
+
format: 'full'
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const result = verifyContextSufficiency(taskDesc, context, 'claude-sonnet-4');
|
|
530
|
+
console.log(JSON.stringify(result, null, 2));
|
|
531
|
+
} catch (err) {
|
|
532
|
+
console.error('Error:', err.message);
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
case 'gaps': {
|
|
539
|
+
const stats = getContextGapStats();
|
|
540
|
+
console.log('\n=== Context Gap Statistics ===\n');
|
|
541
|
+
console.log(`Total gaps logged: ${stats.totalGaps}`);
|
|
542
|
+
console.log(`Recent gaps (24h): ${stats.recentGaps}`);
|
|
543
|
+
if (stats.commonMissing.length > 0) {
|
|
544
|
+
console.log('\nMost commonly missing patterns:');
|
|
545
|
+
for (const { pattern, count } of stats.commonMissing) {
|
|
546
|
+
console.log(` ${pattern}: ${count} occurrences`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
case 'gather': {
|
|
553
|
+
const taskDesc = args.slice(1).join(' ');
|
|
554
|
+
if (!taskDesc) {
|
|
555
|
+
console.error('Usage: flow-quality-guard gather "<task description>"');
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const result = await gatherContextWithQualityGuard({
|
|
560
|
+
task: taskDesc,
|
|
561
|
+
model: 'claude-sonnet-4'
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
console.log('\n=== Context with Quality Guard ===\n');
|
|
565
|
+
console.log(`Sections: ${result.sections?.length || 0}`);
|
|
566
|
+
console.log(`Tokens: ${result.stats?.totalTokens || 'unknown'}`);
|
|
567
|
+
console.log(`Quality verified: ${result.qualityGuard?.verified}`);
|
|
568
|
+
console.log(`Escalated: ${result.qualityGuard?.escalated}`);
|
|
569
|
+
if (result.qualityGuard?.missing) {
|
|
570
|
+
console.log(`Missing: ${JSON.stringify(result.qualityGuard.missing)}`);
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
default:
|
|
576
|
+
console.log(`
|
|
577
|
+
Usage: node scripts/flow-quality-guard.js <command> [args]
|
|
578
|
+
|
|
579
|
+
Commands:
|
|
580
|
+
verify "<task>" Verify if context is sufficient for a task
|
|
581
|
+
gaps Show context gap statistics
|
|
582
|
+
gather "<task>" Gather context with quality guarantee
|
|
583
|
+
|
|
584
|
+
Examples:
|
|
585
|
+
node scripts/flow-quality-guard.js verify "Add user authentication"
|
|
586
|
+
node scripts/flow-quality-guard.js gaps
|
|
587
|
+
node scripts/flow-quality-guard.js gather "Create secure file upload"
|
|
588
|
+
`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (require.main === module) {
|
|
593
|
+
main().catch(console.error);
|
|
594
|
+
}
|