aios-core 3.6.0 → 3.8.0

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 (62) hide show
  1. package/.aios-core/core/session/context-detector.js +3 -0
  2. package/.aios-core/core/session/context-loader.js +154 -0
  3. package/.aios-core/data/learned-patterns.yaml +3 -0
  4. package/.aios-core/data/workflow-patterns.yaml +347 -3
  5. package/.aios-core/development/agents/dev.md +13 -0
  6. package/.aios-core/development/agents/squad-creator.md +30 -0
  7. package/.aios-core/development/scripts/squad/squad-analyzer.js +638 -0
  8. package/.aios-core/development/scripts/squad/squad-extender.js +871 -0
  9. package/.aios-core/development/scripts/squad/squad-generator.js +107 -19
  10. package/.aios-core/development/scripts/squad/squad-migrator.js +3 -5
  11. package/.aios-core/development/scripts/squad/squad-validator.js +98 -0
  12. package/.aios-core/development/tasks/create-service.md +391 -0
  13. package/.aios-core/development/tasks/next.md +294 -0
  14. package/.aios-core/development/tasks/patterns.md +334 -0
  15. package/.aios-core/development/tasks/squad-creator-analyze.md +315 -0
  16. package/.aios-core/development/tasks/squad-creator-create.md +26 -3
  17. package/.aios-core/development/tasks/squad-creator-extend.md +411 -0
  18. package/.aios-core/development/tasks/squad-creator-validate.md +9 -1
  19. package/.aios-core/development/tasks/waves.md +205 -0
  20. package/.aios-core/development/templates/service-template/README.md.hbs +158 -0
  21. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -0
  22. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -0
  23. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -0
  24. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -0
  25. package/.aios-core/development/templates/service-template/jest.config.js +89 -0
  26. package/.aios-core/development/templates/service-template/package.json.hbs +87 -0
  27. package/.aios-core/development/templates/service-template/tsconfig.json +45 -0
  28. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -0
  29. package/.aios-core/development/templates/squad/agent-template.md +69 -0
  30. package/.aios-core/development/templates/squad/checklist-template.md +82 -0
  31. package/.aios-core/development/templates/squad/data-template.yaml +105 -0
  32. package/.aios-core/development/templates/squad/script-template.js +179 -0
  33. package/.aios-core/development/templates/squad/task-template.md +125 -0
  34. package/.aios-core/development/templates/squad/template-template.md +97 -0
  35. package/.aios-core/development/templates/squad/tool-template.js +103 -0
  36. package/.aios-core/development/templates/squad/workflow-template.yaml +108 -0
  37. package/.aios-core/infrastructure/scripts/ide-sync/agent-parser.js +45 -1
  38. package/.aios-core/infrastructure/scripts/ide-sync/transformers/antigravity.js +6 -6
  39. package/.aios-core/infrastructure/scripts/ide-sync/transformers/cursor.js +5 -4
  40. package/.aios-core/infrastructure/scripts/ide-sync/transformers/trae.js +3 -3
  41. package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +3 -3
  42. package/.aios-core/install-manifest.yaml +139 -35
  43. package/.aios-core/quality/metrics-collector.js +27 -0
  44. package/.aios-core/scripts/session-context-loader.js +13 -254
  45. package/.aios-core/utils/aios-validator.js +25 -0
  46. package/.aios-core/workflow-intelligence/__tests__/confidence-scorer.test.js +334 -0
  47. package/.aios-core/workflow-intelligence/__tests__/integration.test.js +337 -0
  48. package/.aios-core/workflow-intelligence/__tests__/suggestion-engine.test.js +431 -0
  49. package/.aios-core/workflow-intelligence/__tests__/wave-analyzer.test.js +458 -0
  50. package/.aios-core/workflow-intelligence/__tests__/workflow-registry.test.js +302 -0
  51. package/.aios-core/workflow-intelligence/engine/confidence-scorer.js +305 -0
  52. package/.aios-core/workflow-intelligence/engine/output-formatter.js +285 -0
  53. package/.aios-core/workflow-intelligence/engine/suggestion-engine.js +603 -0
  54. package/.aios-core/workflow-intelligence/engine/wave-analyzer.js +676 -0
  55. package/.aios-core/workflow-intelligence/index.js +327 -0
  56. package/.aios-core/workflow-intelligence/learning/capture-hook.js +147 -0
  57. package/.aios-core/workflow-intelligence/learning/index.js +230 -0
  58. package/.aios-core/workflow-intelligence/learning/pattern-capture.js +340 -0
  59. package/.aios-core/workflow-intelligence/learning/pattern-store.js +498 -0
  60. package/.aios-core/workflow-intelligence/learning/pattern-validator.js +309 -0
  61. package/.aios-core/workflow-intelligence/registry/workflow-registry.js +358 -0
  62. package/package.json +1 -1
@@ -0,0 +1,498 @@
1
+ /**
2
+ * @module PatternStore
3
+ * @description Stores and manages learned workflow patterns
4
+ * @story WIS-5 - Pattern Capture (Internal)
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const crypto = require('crypto');
13
+ const yaml = require('yaml');
14
+
15
+ /**
16
+ * Default storage path for learned patterns
17
+ * @type {string}
18
+ */
19
+ const DEFAULT_STORAGE_PATH = path.join(__dirname, '../../..', 'data', 'learned-patterns.yaml');
20
+
21
+ /**
22
+ * Default maximum number of patterns
23
+ * @type {number}
24
+ */
25
+ const DEFAULT_MAX_PATTERNS = 100;
26
+
27
+ /**
28
+ * Default auto-prune threshold (percentage of max)
29
+ * @type {number}
30
+ */
31
+ const DEFAULT_PRUNE_THRESHOLD = 0.9;
32
+
33
+ /**
34
+ * Pattern status values
35
+ * @type {Object}
36
+ */
37
+ const PATTERN_STATUS = {
38
+ PENDING: 'pending',
39
+ ACTIVE: 'active',
40
+ PROMOTED: 'promoted',
41
+ DEPRECATED: 'deprecated'
42
+ };
43
+
44
+ /**
45
+ * PatternStore class for persisting workflow patterns
46
+ */
47
+ class PatternStore {
48
+ /**
49
+ * Create a PatternStore instance
50
+ * @param {Object} options - Configuration options
51
+ * @param {string} options.storagePath - Path to storage file
52
+ * @param {number} options.maxPatterns - Maximum patterns to store
53
+ * @param {number} options.pruneThreshold - Auto-prune threshold (0-1)
54
+ * @param {string} options.pruneStrategy - Prune strategy ('oldest_low_occurrence' | 'lowest_success_rate')
55
+ */
56
+ constructor(options = {}) {
57
+ this.storagePath = options.storagePath || DEFAULT_STORAGE_PATH;
58
+ this.maxPatterns = options.maxPatterns || DEFAULT_MAX_PATTERNS;
59
+ this.pruneThreshold = options.pruneThreshold || DEFAULT_PRUNE_THRESHOLD;
60
+ this.pruneStrategy = options.pruneStrategy || 'oldest_low_occurrence';
61
+ this._cache = null;
62
+ this._cacheTime = null;
63
+ }
64
+
65
+ /**
66
+ * Save a pattern to storage
67
+ * @param {Object} pattern - Pattern to save
68
+ * @returns {Object} Save result
69
+ */
70
+ save(pattern) {
71
+ const data = this._load();
72
+
73
+ // Ensure pattern has required fields
74
+ const normalizedPattern = this._normalizePattern(pattern);
75
+
76
+ // Check for existing pattern with same sequence
77
+ const existingIndex = data.patterns.findIndex(
78
+ p => this._sequenceEquals(p.sequence, normalizedPattern.sequence)
79
+ );
80
+
81
+ if (existingIndex >= 0) {
82
+ // Update existing pattern
83
+ const existing = data.patterns[existingIndex];
84
+ existing.occurrences = (existing.occurrences || 1) + 1;
85
+ existing.lastSeen = new Date().toISOString();
86
+
87
+ // Update success rate (weighted average)
88
+ const totalOccurrences = existing.occurrences;
89
+ const newSuccessRate = normalizedPattern.successRate || 1.0;
90
+ existing.successRate = (
91
+ (existing.successRate * (totalOccurrences - 1) + newSuccessRate) / totalOccurrences
92
+ );
93
+
94
+ data.patterns[existingIndex] = existing;
95
+
96
+ this._save(data);
97
+ return { action: 'updated', pattern: existing };
98
+ }
99
+
100
+ // Add new pattern
101
+ data.patterns.push(normalizedPattern);
102
+
103
+ // Check if auto-prune needed
104
+ if (data.patterns.length > this.maxPatterns * this.pruneThreshold) {
105
+ this._autoPrune(data);
106
+ }
107
+
108
+ this._save(data);
109
+ return { action: 'created', pattern: normalizedPattern };
110
+ }
111
+
112
+ /**
113
+ * Load all patterns from storage
114
+ * @returns {Object} Loaded data with patterns array
115
+ */
116
+ load() {
117
+ return this._load();
118
+ }
119
+
120
+ /**
121
+ * Find patterns similar to a given sequence
122
+ * @param {string[]} sequence - Sequence to match
123
+ * @returns {Object[]} Matching patterns sorted by relevance
124
+ */
125
+ findSimilar(sequence) {
126
+ const data = this._load();
127
+
128
+ if (!sequence || sequence.length === 0) {
129
+ return [];
130
+ }
131
+
132
+ const matches = [];
133
+
134
+ for (const pattern of data.patterns) {
135
+ const similarity = this._calculateSimilarity(sequence, pattern.sequence);
136
+
137
+ if (similarity > 0.3) { // Minimum threshold for consideration
138
+ matches.push({
139
+ ...pattern,
140
+ similarity: similarity
141
+ });
142
+ }
143
+ }
144
+
145
+ // Sort by similarity (descending), then by occurrences
146
+ return matches.sort((a, b) => {
147
+ if (Math.abs(a.similarity - b.similarity) > 0.1) {
148
+ return b.similarity - a.similarity;
149
+ }
150
+ return (b.occurrences || 1) - (a.occurrences || 1);
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Get storage statistics
156
+ * @returns {Object} Statistics object
157
+ */
158
+ getStats() {
159
+ const data = this._load();
160
+ const patterns = data.patterns;
161
+
162
+ const statusCounts = {
163
+ pending: 0,
164
+ active: 0,
165
+ promoted: 0,
166
+ deprecated: 0
167
+ };
168
+
169
+ let totalSuccessRate = 0;
170
+ let totalOccurrences = 0;
171
+
172
+ for (const pattern of patterns) {
173
+ statusCounts[pattern.status || 'pending']++;
174
+ totalSuccessRate += pattern.successRate || 0;
175
+ totalOccurrences += pattern.occurrences || 1;
176
+ }
177
+
178
+ const avgSuccessRate = patterns.length > 0
179
+ ? totalSuccessRate / patterns.length
180
+ : 0;
181
+
182
+ return {
183
+ totalPatterns: patterns.length,
184
+ maxPatterns: this.maxPatterns,
185
+ utilizationPercent: Math.round((patterns.length / this.maxPatterns) * 100),
186
+ statusCounts: statusCounts,
187
+ avgSuccessRate: Math.round(avgSuccessRate * 100) / 100,
188
+ totalOccurrences: totalOccurrences,
189
+ storageFile: this.storagePath,
190
+ lastUpdated: data.lastUpdated || null
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Prune patterns based on configured strategy
196
+ * @param {Object} options - Prune options
197
+ * @param {number} options.keepCount - Number of patterns to keep
198
+ * @param {string} options.strategy - Prune strategy override
199
+ * @returns {Object} Prune result
200
+ */
201
+ prune(options = {}) {
202
+ const data = this._load();
203
+ const originalCount = data.patterns.length;
204
+
205
+ const keepCount = options.keepCount || Math.floor(this.maxPatterns * 0.7);
206
+ const strategy = options.strategy || this.pruneStrategy;
207
+
208
+ if (data.patterns.length <= keepCount) {
209
+ return { pruned: 0, remaining: data.patterns.length };
210
+ }
211
+
212
+ // Sort patterns for pruning
213
+ let sorted;
214
+ if (strategy === 'lowest_success_rate') {
215
+ sorted = [...data.patterns].sort((a, b) => {
216
+ // Keep promoted/active patterns
217
+ if (a.status === 'promoted' || a.status === 'active') return -1;
218
+ if (b.status === 'promoted' || b.status === 'active') return 1;
219
+ // Then by success rate
220
+ return (b.successRate || 0) - (a.successRate || 0);
221
+ });
222
+ } else { // oldest_low_occurrence (default)
223
+ sorted = [...data.patterns].sort((a, b) => {
224
+ // Keep promoted patterns
225
+ if (a.status === 'promoted') return -1;
226
+ if (b.status === 'promoted') return 1;
227
+ // Keep active patterns
228
+ if (a.status === 'active') return -1;
229
+ if (b.status === 'active') return 1;
230
+ // Then by occurrences (higher = keep)
231
+ if ((a.occurrences || 1) !== (b.occurrences || 1)) {
232
+ return (b.occurrences || 1) - (a.occurrences || 1);
233
+ }
234
+ // Then by last seen (newer = keep)
235
+ return new Date(b.lastSeen || 0) - new Date(a.lastSeen || 0);
236
+ });
237
+ }
238
+
239
+ // Keep top patterns
240
+ data.patterns = sorted.slice(0, keepCount);
241
+
242
+ this._save(data);
243
+
244
+ return {
245
+ pruned: originalCount - data.patterns.length,
246
+ remaining: data.patterns.length
247
+ };
248
+ }
249
+
250
+ /**
251
+ * Update pattern status
252
+ * @param {string} patternId - Pattern ID
253
+ * @param {string} newStatus - New status value
254
+ * @returns {Object} Update result
255
+ */
256
+ updateStatus(patternId, newStatus) {
257
+ const data = this._load();
258
+ const pattern = data.patterns.find(p => p.id === patternId);
259
+
260
+ if (!pattern) {
261
+ return { success: false, error: 'Pattern not found' };
262
+ }
263
+
264
+ const validStatuses = Object.values(PATTERN_STATUS);
265
+ if (!validStatuses.includes(newStatus)) {
266
+ return { success: false, error: `Invalid status: ${newStatus}` };
267
+ }
268
+
269
+ pattern.status = newStatus;
270
+ pattern.lastUpdated = new Date().toISOString();
271
+
272
+ this._save(data);
273
+
274
+ return { success: true, pattern: pattern };
275
+ }
276
+
277
+ /**
278
+ * Get patterns by status
279
+ * @param {string} status - Status to filter by
280
+ * @returns {Object[]} Patterns with given status
281
+ */
282
+ getByStatus(status) {
283
+ const data = this._load();
284
+ return data.patterns.filter(p => p.status === status);
285
+ }
286
+
287
+ /**
288
+ * Get active and promoted patterns (for SuggestionEngine)
289
+ * @returns {Object[]} Active patterns
290
+ */
291
+ getActivePatterns() {
292
+ const data = this._load();
293
+ return data.patterns.filter(
294
+ p => p.status === PATTERN_STATUS.ACTIVE || p.status === PATTERN_STATUS.PROMOTED
295
+ );
296
+ }
297
+
298
+ /**
299
+ * Delete a pattern by ID
300
+ * @param {string} patternId - Pattern ID
301
+ * @returns {Object} Delete result
302
+ */
303
+ delete(patternId) {
304
+ const data = this._load();
305
+ const index = data.patterns.findIndex(p => p.id === patternId);
306
+
307
+ if (index < 0) {
308
+ return { success: false, error: 'Pattern not found' };
309
+ }
310
+
311
+ data.patterns.splice(index, 1);
312
+ this._save(data);
313
+
314
+ return { success: true };
315
+ }
316
+
317
+ /**
318
+ * Load data from storage file
319
+ * @returns {Object} Loaded data
320
+ * @private
321
+ */
322
+ _load() {
323
+ // Use cache if valid (within 5 seconds)
324
+ if (this._cache && this._cacheTime && (Date.now() - this._cacheTime) < 5000) {
325
+ return this._cache;
326
+ }
327
+
328
+ try {
329
+ if (fs.existsSync(this.storagePath)) {
330
+ const content = fs.readFileSync(this.storagePath, 'utf8');
331
+ const data = yaml.parse(content) || {};
332
+ data.patterns = data.patterns || [];
333
+ this._cache = data;
334
+ this._cacheTime = Date.now();
335
+ return data;
336
+ }
337
+ } catch (error) {
338
+ console.warn('[PatternStore] Failed to load:', error.message);
339
+ }
340
+
341
+ // Return empty structure
342
+ const empty = { version: '1.0', patterns: [] };
343
+ this._cache = empty;
344
+ this._cacheTime = Date.now();
345
+ return empty;
346
+ }
347
+
348
+ /**
349
+ * Save data to storage file
350
+ * @param {Object} data - Data to save
351
+ * @private
352
+ */
353
+ _save(data) {
354
+ try {
355
+ data.lastUpdated = new Date().toISOString();
356
+
357
+ // Sort patterns for git-friendly diffs
358
+ data.patterns.sort((a, b) => a.id.localeCompare(b.id));
359
+
360
+ // Ensure directory exists
361
+ const dir = path.dirname(this.storagePath);
362
+ if (!fs.existsSync(dir)) {
363
+ fs.mkdirSync(dir, { recursive: true });
364
+ }
365
+
366
+ const content = yaml.stringify(data, { indent: 2, lineWidth: 120 });
367
+ fs.writeFileSync(this.storagePath, content, 'utf8');
368
+
369
+ // Invalidate cache
370
+ this._cache = data;
371
+ this._cacheTime = Date.now();
372
+ } catch (error) {
373
+ console.error('[PatternStore] Failed to save:', error.message);
374
+ throw error;
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Normalize pattern object
380
+ * @param {Object} pattern - Pattern to normalize
381
+ * @returns {Object} Normalized pattern
382
+ * @private
383
+ */
384
+ _normalizePattern(pattern) {
385
+ return {
386
+ id: pattern.id || crypto.randomUUID(),
387
+ sequence: pattern.sequence || [],
388
+ agents: pattern.agents || [],
389
+ occurrences: pattern.occurrences || 1,
390
+ successRate: pattern.successRate || 1.0,
391
+ firstSeen: pattern.firstSeen || new Date().toISOString(),
392
+ lastSeen: pattern.lastSeen || new Date().toISOString(),
393
+ workflow: pattern.workflow || null,
394
+ status: pattern.status || PATTERN_STATUS.PENDING
395
+ };
396
+ }
397
+
398
+ /**
399
+ * Check if two sequences are equal
400
+ * @param {string[]} seq1 - First sequence
401
+ * @param {string[]} seq2 - Second sequence
402
+ * @returns {boolean} True if equal
403
+ * @private
404
+ */
405
+ _sequenceEquals(seq1, seq2) {
406
+ if (!seq1 || !seq2 || seq1.length !== seq2.length) {
407
+ return false;
408
+ }
409
+ return seq1.every((cmd, i) => cmd === seq2[i]);
410
+ }
411
+
412
+ /**
413
+ * Calculate similarity between sequences
414
+ * @param {string[]} seq1 - First sequence
415
+ * @param {string[]} seq2 - Second sequence
416
+ * @returns {number} Similarity score (0-1)
417
+ * @private
418
+ */
419
+ _calculateSimilarity(seq1, seq2) {
420
+ if (!seq1 || !seq2) return 0;
421
+
422
+ // Check for subsequence match
423
+ const joined1 = seq1.join('|');
424
+ const joined2 = seq2.join('|');
425
+
426
+ if (joined2.includes(joined1) || joined1.includes(joined2)) {
427
+ return 0.9;
428
+ }
429
+
430
+ // Check for partial overlap
431
+ let matches = 0;
432
+ for (const cmd of seq1) {
433
+ if (seq2.includes(cmd)) {
434
+ matches++;
435
+ }
436
+ }
437
+
438
+ return matches / Math.max(seq1.length, seq2.length);
439
+ }
440
+
441
+ /**
442
+ * Auto-prune when approaching limit
443
+ * @param {Object} data - Data object
444
+ * @private
445
+ */
446
+ _autoPrune(data) {
447
+ const keepCount = Math.floor(this.maxPatterns * 0.8);
448
+
449
+ // Sort by priority (keep promoted > active > pending)
450
+ // Then by occurrences, then by last seen
451
+ data.patterns.sort((a, b) => {
452
+ // Status priority
453
+ const statusPriority = { promoted: 3, active: 2, pending: 1, deprecated: 0 };
454
+ const aPriority = statusPriority[a.status] || 0;
455
+ const bPriority = statusPriority[b.status] || 0;
456
+
457
+ if (aPriority !== bPriority) {
458
+ return bPriority - aPriority;
459
+ }
460
+
461
+ // Then by occurrences
462
+ if ((a.occurrences || 1) !== (b.occurrences || 1)) {
463
+ return (b.occurrences || 1) - (a.occurrences || 1);
464
+ }
465
+
466
+ // Then by last seen
467
+ return new Date(b.lastSeen || 0) - new Date(a.lastSeen || 0);
468
+ });
469
+
470
+ data.patterns = data.patterns.slice(0, keepCount);
471
+ }
472
+
473
+ /**
474
+ * Invalidate cache
475
+ */
476
+ invalidateCache() {
477
+ this._cache = null;
478
+ this._cacheTime = null;
479
+ }
480
+ }
481
+
482
+ /**
483
+ * Create a new PatternStore instance
484
+ * @param {Object} options - Configuration options
485
+ * @returns {PatternStore} New instance
486
+ */
487
+ function createPatternStore(options = {}) {
488
+ return new PatternStore(options);
489
+ }
490
+
491
+ module.exports = {
492
+ PatternStore,
493
+ createPatternStore,
494
+ DEFAULT_STORAGE_PATH,
495
+ DEFAULT_MAX_PATTERNS,
496
+ DEFAULT_PRUNE_THRESHOLD,
497
+ PATTERN_STATUS
498
+ };