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.
Files changed (45) hide show
  1. package/.workflow/specs/architecture.md.template +24 -0
  2. package/.workflow/specs/stack.md.template +33 -0
  3. package/.workflow/specs/testing.md.template +36 -0
  4. package/README.md +90 -1
  5. package/package.json +1 -1
  6. package/scripts/MEMORY-ARCHITECTURE.md +150 -0
  7. package/scripts/flow +20 -19
  8. package/scripts/flow-auto-context.js +97 -3
  9. package/scripts/flow-conflict-resolver.js +735 -0
  10. package/scripts/flow-context-gatherer.js +520 -0
  11. package/scripts/flow-context-monitor.js +148 -19
  12. package/scripts/flow-damage-control.js +5 -1
  13. package/scripts/flow-export-profile +168 -1
  14. package/scripts/flow-import-profile +257 -6
  15. package/scripts/flow-instruction-richness.js +182 -18
  16. package/scripts/flow-knowledge-router.js +2 -0
  17. package/scripts/flow-knowledge-sync.js +2 -0
  18. package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
  19. package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
  20. package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
  21. package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
  22. package/scripts/flow-memory-db.js +386 -1
  23. package/scripts/flow-memory-sync.js +2 -0
  24. package/scripts/flow-model-adapter.js +53 -29
  25. package/scripts/flow-model-router.js +246 -1
  26. package/scripts/flow-morning.js +94 -0
  27. package/scripts/flow-onboard +223 -10
  28. package/scripts/flow-orchestrate-validation.js +539 -0
  29. package/scripts/flow-orchestrate.js +16 -507
  30. package/scripts/flow-pattern-extractor.js +1265 -0
  31. package/scripts/flow-prompt-composer.js +222 -2
  32. package/scripts/flow-quality-guard.js +594 -0
  33. package/scripts/flow-section-index.js +713 -0
  34. package/scripts/flow-section-resolver.js +484 -0
  35. package/scripts/flow-session-end.js +188 -2
  36. package/scripts/flow-skill-create.js +19 -3
  37. package/scripts/flow-skill-matcher.js +122 -7
  38. package/scripts/flow-statusline-setup.js +218 -0
  39. package/scripts/flow-step-review.js +19 -0
  40. package/scripts/flow-tech-debt.js +734 -0
  41. package/scripts/flow-utils.js +2 -0
  42. package/scripts/hooks/core/long-input-gate.js +293 -0
  43. package/scripts/flow-parallel-detector.js +0 -399
  44. package/scripts/flow-parallel-dispatch.js +0 -987
  45. /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
+ }