dino-spec 17.4.3 → 17.6.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 (277) hide show
  1. package/README.md +16 -1
  2. package/dist/commands/init.d.ts.map +1 -1
  3. package/dist/commands/init.js +10 -0
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/core/agents/__tests__/confidence-gate.test.d.ts +5 -0
  6. package/dist/core/agents/__tests__/confidence-gate.test.d.ts.map +1 -0
  7. package/dist/core/agents/__tests__/confidence-gate.test.js +329 -0
  8. package/dist/core/agents/__tests__/confidence-gate.test.js.map +1 -0
  9. package/dist/core/agents/__tests__/gap-detector.test.d.ts +5 -0
  10. package/dist/core/agents/__tests__/gap-detector.test.d.ts.map +1 -0
  11. package/dist/core/agents/__tests__/gap-detector.test.js +609 -0
  12. package/dist/core/agents/__tests__/gap-detector.test.js.map +1 -0
  13. package/dist/core/agents/__tests__/learning-loop.test.d.ts +5 -0
  14. package/dist/core/agents/__tests__/learning-loop.test.d.ts.map +1 -0
  15. package/dist/core/agents/__tests__/learning-loop.test.js +663 -0
  16. package/dist/core/agents/__tests__/learning-loop.test.js.map +1 -0
  17. package/dist/core/agents/__tests__/parallel-executor.test.d.ts +5 -0
  18. package/dist/core/agents/__tests__/parallel-executor.test.d.ts.map +1 -0
  19. package/dist/core/agents/__tests__/parallel-executor.test.js +610 -0
  20. package/dist/core/agents/__tests__/parallel-executor.test.js.map +1 -0
  21. package/dist/core/agents/__tests__/research-validator.test.js +5 -5
  22. package/dist/core/agents/__tests__/research-validator.test.js.map +1 -1
  23. package/dist/core/agents/__tests__/skill-creator.test.d.ts +5 -0
  24. package/dist/core/agents/__tests__/skill-creator.test.d.ts.map +1 -0
  25. package/dist/core/agents/__tests__/skill-creator.test.js +443 -0
  26. package/dist/core/agents/__tests__/skill-creator.test.js.map +1 -0
  27. package/dist/core/agents/__tests__/state-persistence.test.d.ts +2 -0
  28. package/dist/core/agents/__tests__/state-persistence.test.d.ts.map +1 -0
  29. package/dist/core/agents/__tests__/state-persistence.test.js +270 -0
  30. package/dist/core/agents/__tests__/state-persistence.test.js.map +1 -0
  31. package/dist/core/agents/__tests__/validation-cache.test.d.ts +2 -0
  32. package/dist/core/agents/__tests__/validation-cache.test.d.ts.map +1 -0
  33. package/dist/core/agents/__tests__/validation-cache.test.js +181 -0
  34. package/dist/core/agents/__tests__/validation-cache.test.js.map +1 -0
  35. package/dist/core/agents/ambiguity-resolver.d.ts +14 -1
  36. package/dist/core/agents/ambiguity-resolver.d.ts.map +1 -1
  37. package/dist/core/agents/ambiguity-resolver.js +24 -2
  38. package/dist/core/agents/ambiguity-resolver.js.map +1 -1
  39. package/dist/core/agents/blackboard.d.ts +22 -3
  40. package/dist/core/agents/blackboard.d.ts.map +1 -1
  41. package/dist/core/agents/blackboard.js +52 -2
  42. package/dist/core/agents/blackboard.js.map +1 -1
  43. package/dist/core/agents/circuit-breaker.d.ts +79 -1
  44. package/dist/core/agents/circuit-breaker.d.ts.map +1 -1
  45. package/dist/core/agents/circuit-breaker.js +175 -1
  46. package/dist/core/agents/circuit-breaker.js.map +1 -1
  47. package/dist/core/agents/clarification-phase.d.ts +128 -0
  48. package/dist/core/agents/clarification-phase.d.ts.map +1 -0
  49. package/dist/core/agents/clarification-phase.js +375 -0
  50. package/dist/core/agents/clarification-phase.js.map +1 -0
  51. package/dist/core/agents/confidence-gate.d.ts +133 -4
  52. package/dist/core/agents/confidence-gate.d.ts.map +1 -1
  53. package/dist/core/agents/confidence-gate.js +376 -9
  54. package/dist/core/agents/confidence-gate.js.map +1 -1
  55. package/dist/core/agents/correction-tracker.d.ts +197 -0
  56. package/dist/core/agents/correction-tracker.d.ts.map +1 -0
  57. package/dist/core/agents/correction-tracker.js +366 -0
  58. package/dist/core/agents/correction-tracker.js.map +1 -0
  59. package/dist/core/agents/dependency-graph.d.ts +193 -0
  60. package/dist/core/agents/dependency-graph.d.ts.map +1 -0
  61. package/dist/core/agents/dependency-graph.js +462 -0
  62. package/dist/core/agents/dependency-graph.js.map +1 -0
  63. package/dist/core/agents/gap-detector.d.ts +277 -0
  64. package/dist/core/agents/gap-detector.d.ts.map +1 -0
  65. package/dist/core/agents/gap-detector.js +540 -0
  66. package/dist/core/agents/gap-detector.js.map +1 -0
  67. package/dist/core/agents/index.d.ts +14 -1
  68. package/dist/core/agents/index.d.ts.map +1 -1
  69. package/dist/core/agents/index.js +26 -1
  70. package/dist/core/agents/index.js.map +1 -1
  71. package/dist/core/agents/message-protocol.d.ts +13 -2
  72. package/dist/core/agents/message-protocol.d.ts.map +1 -1
  73. package/dist/core/agents/message-protocol.js +20 -1
  74. package/dist/core/agents/message-protocol.js.map +1 -1
  75. package/dist/core/agents/parallel-executor.d.ts +182 -0
  76. package/dist/core/agents/parallel-executor.d.ts.map +1 -0
  77. package/dist/core/agents/parallel-executor.js +474 -0
  78. package/dist/core/agents/parallel-executor.js.map +1 -0
  79. package/dist/core/agents/pattern-detector.d.ts +157 -0
  80. package/dist/core/agents/pattern-detector.d.ts.map +1 -0
  81. package/dist/core/agents/pattern-detector.js +370 -0
  82. package/dist/core/agents/pattern-detector.js.map +1 -0
  83. package/dist/core/agents/registry-client.d.ts +6 -0
  84. package/dist/core/agents/registry-client.d.ts.map +1 -1
  85. package/dist/core/agents/registry-client.js +3 -0
  86. package/dist/core/agents/registry-client.js.map +1 -1
  87. package/dist/core/agents/research-phase.d.ts +114 -0
  88. package/dist/core/agents/research-phase.d.ts.map +1 -0
  89. package/dist/core/agents/research-phase.js +355 -0
  90. package/dist/core/agents/research-phase.js.map +1 -0
  91. package/dist/core/agents/research-validator.d.ts +13 -2
  92. package/dist/core/agents/research-validator.d.ts.map +1 -1
  93. package/dist/core/agents/research-validator.js +63 -31
  94. package/dist/core/agents/research-validator.js.map +1 -1
  95. package/dist/core/agents/skill-creator.d.ts +174 -0
  96. package/dist/core/agents/skill-creator.d.ts.map +1 -0
  97. package/dist/core/agents/skill-creator.js +570 -0
  98. package/dist/core/agents/skill-creator.js.map +1 -0
  99. package/dist/core/agents/state-persistence.d.ts +184 -0
  100. package/dist/core/agents/state-persistence.d.ts.map +1 -0
  101. package/dist/core/agents/state-persistence.js +394 -0
  102. package/dist/core/agents/state-persistence.js.map +1 -0
  103. package/dist/core/agents/validation-cache.d.ts +122 -0
  104. package/dist/core/agents/validation-cache.d.ts.map +1 -0
  105. package/dist/core/agents/validation-cache.js +280 -0
  106. package/dist/core/agents/validation-cache.js.map +1 -0
  107. package/dist/core/agents/validators/__tests__/validators.test.d.ts +5 -0
  108. package/dist/core/agents/validators/__tests__/validators.test.d.ts.map +1 -0
  109. package/dist/core/agents/validators/__tests__/validators.test.js +321 -0
  110. package/dist/core/agents/validators/__tests__/validators.test.js.map +1 -0
  111. package/dist/core/agents/validators/base-validator.d.ts +191 -0
  112. package/dist/core/agents/validators/base-validator.d.ts.map +1 -0
  113. package/dist/core/agents/validators/base-validator.js +192 -0
  114. package/dist/core/agents/validators/base-validator.js.map +1 -0
  115. package/dist/core/agents/validators/index.d.ts +7 -0
  116. package/dist/core/agents/validators/index.d.ts.map +1 -0
  117. package/dist/core/agents/validators/index.js +7 -0
  118. package/dist/core/agents/validators/index.js.map +1 -0
  119. package/dist/core/agents/validators/npm-validator.d.ts +50 -0
  120. package/dist/core/agents/validators/npm-validator.d.ts.map +1 -0
  121. package/dist/core/agents/validators/npm-validator.js +211 -0
  122. package/dist/core/agents/validators/npm-validator.js.map +1 -0
  123. package/dist/core/agents/validators/pip-validator.d.ts +49 -0
  124. package/dist/core/agents/validators/pip-validator.d.ts.map +1 -0
  125. package/dist/core/agents/validators/pip-validator.js +191 -0
  126. package/dist/core/agents/validators/pip-validator.js.map +1 -0
  127. package/dist/core/agents/validators/validator-registry.d.ts +122 -0
  128. package/dist/core/agents/validators/validator-registry.d.ts.map +1 -0
  129. package/dist/core/agents/validators/validator-registry.js +321 -0
  130. package/dist/core/agents/validators/validator-registry.js.map +1 -0
  131. package/dist/core/context/auto-injection-engine.d.ts +7 -1
  132. package/dist/core/context/auto-injection-engine.d.ts.map +1 -1
  133. package/dist/core/context/auto-injection-engine.js +46 -1
  134. package/dist/core/context/auto-injection-engine.js.map +1 -1
  135. package/dist/core/context-repl/types.d.ts +4 -4
  136. package/dist/core/memory/index.d.ts +1 -0
  137. package/dist/core/memory/index.d.ts.map +1 -1
  138. package/dist/core/memory/index.js +2 -0
  139. package/dist/core/memory/index.js.map +1 -1
  140. package/dist/core/memory/learning-store.d.ts +222 -0
  141. package/dist/core/memory/learning-store.d.ts.map +1 -0
  142. package/dist/core/memory/learning-store.js +477 -0
  143. package/dist/core/memory/learning-store.js.map +1 -0
  144. package/dist/core/ralph/__tests__/prd-generator-session-context.test.d.ts +7 -0
  145. package/dist/core/ralph/__tests__/prd-generator-session-context.test.d.ts.map +1 -0
  146. package/dist/core/ralph/__tests__/prd-generator-session-context.test.js +143 -0
  147. package/dist/core/ralph/__tests__/prd-generator-session-context.test.js.map +1 -0
  148. package/dist/core/ralph/index.d.ts +3 -3
  149. package/dist/core/ralph/index.d.ts.map +1 -1
  150. package/dist/core/ralph/index.js +2 -2
  151. package/dist/core/ralph/index.js.map +1 -1
  152. package/dist/core/ralph/prd-generator.d.ts +18 -3
  153. package/dist/core/ralph/prd-generator.d.ts.map +1 -1
  154. package/dist/core/ralph/prd-generator.js +33 -11
  155. package/dist/core/ralph/prd-generator.js.map +1 -1
  156. package/dist/core/ralph/progress-tracker.d.ts.map +1 -1
  157. package/dist/core/ralph/progress-tracker.js +2 -1
  158. package/dist/core/ralph/progress-tracker.js.map +1 -1
  159. package/dist/core/ralph/sub-agent-spawner.d.ts +4 -2
  160. package/dist/core/ralph/sub-agent-spawner.d.ts.map +1 -1
  161. package/dist/core/ralph/sub-agent-spawner.js +48 -3
  162. package/dist/core/ralph/sub-agent-spawner.js.map +1 -1
  163. package/dist/core/ralph/task-integration.d.ts.map +1 -1
  164. package/dist/core/ralph/task-integration.js +6 -3
  165. package/dist/core/ralph/task-integration.js.map +1 -1
  166. package/dist/core/ralph/types.d.ts +62 -3
  167. package/dist/core/ralph/types.d.ts.map +1 -1
  168. package/dist/core/ralph/types.js.map +1 -1
  169. package/dist/core/recommendations/recommendation-engine.d.ts +6 -0
  170. package/dist/core/recommendations/recommendation-engine.d.ts.map +1 -1
  171. package/dist/core/recommendations/recommendation-engine.js.map +1 -1
  172. package/dist/core/spec-analyzer/__tests__/ambiguity-detector.test.d.ts +5 -0
  173. package/dist/core/spec-analyzer/__tests__/ambiguity-detector.test.d.ts.map +1 -0
  174. package/dist/core/spec-analyzer/__tests__/ambiguity-detector.test.js +401 -0
  175. package/dist/core/spec-analyzer/__tests__/ambiguity-detector.test.js.map +1 -0
  176. package/dist/core/spec-analyzer/ambiguity-detector.d.ts +99 -0
  177. package/dist/core/spec-analyzer/ambiguity-detector.d.ts.map +1 -0
  178. package/dist/core/spec-analyzer/ambiguity-detector.js +250 -0
  179. package/dist/core/spec-analyzer/ambiguity-detector.js.map +1 -0
  180. package/dist/core/spec-analyzer/clarification-generator.d.ts +76 -0
  181. package/dist/core/spec-analyzer/clarification-generator.d.ts.map +1 -0
  182. package/dist/core/spec-analyzer/clarification-generator.js +257 -0
  183. package/dist/core/spec-analyzer/clarification-generator.js.map +1 -0
  184. package/dist/core/spec-analyzer/index.d.ts +22 -2
  185. package/dist/core/spec-analyzer/index.d.ts.map +1 -1
  186. package/dist/core/spec-analyzer/index.js +21 -3
  187. package/dist/core/spec-analyzer/index.js.map +1 -1
  188. package/dist/core/spec-analyzer/patterns.d.ts +73 -0
  189. package/dist/core/spec-analyzer/patterns.d.ts.map +1 -0
  190. package/dist/core/spec-analyzer/patterns.js +412 -0
  191. package/dist/core/spec-analyzer/patterns.js.map +1 -0
  192. package/dist/hooks/__tests__/dynamic-generator.test.d.ts +5 -0
  193. package/dist/hooks/__tests__/dynamic-generator.test.d.ts.map +1 -0
  194. package/dist/hooks/__tests__/dynamic-generator.test.js +425 -0
  195. package/dist/hooks/__tests__/dynamic-generator.test.js.map +1 -0
  196. package/dist/hooks/__tests__/hook-agent-bridge.test.d.ts +5 -0
  197. package/dist/hooks/__tests__/hook-agent-bridge.test.d.ts.map +1 -0
  198. package/dist/hooks/__tests__/hook-agent-bridge.test.js +315 -0
  199. package/dist/hooks/__tests__/hook-agent-bridge.test.js.map +1 -0
  200. package/dist/hooks/dynamic-generator.d.ts +158 -0
  201. package/dist/hooks/dynamic-generator.d.ts.map +1 -0
  202. package/dist/hooks/dynamic-generator.js +448 -0
  203. package/dist/hooks/dynamic-generator.js.map +1 -0
  204. package/dist/hooks/hook-agent-bridge.d.ts +252 -0
  205. package/dist/hooks/hook-agent-bridge.d.ts.map +1 -0
  206. package/dist/hooks/hook-agent-bridge.js +489 -0
  207. package/dist/hooks/hook-agent-bridge.js.map +1 -0
  208. package/dist/hooks/index.d.ts +3 -1
  209. package/dist/hooks/index.d.ts.map +1 -1
  210. package/dist/hooks/index.js +5 -1
  211. package/dist/hooks/index.js.map +1 -1
  212. package/dist/mcp/tool-catalog.d.ts.map +1 -1
  213. package/dist/mcp/tool-catalog.js +47 -0
  214. package/dist/mcp/tool-catalog.js.map +1 -1
  215. package/dist/mcp/tool-tiers.d.ts.map +1 -1
  216. package/dist/mcp/tool-tiers.js +4 -0
  217. package/dist/mcp/tool-tiers.js.map +1 -1
  218. package/dist/mcp/tools/auto-inject.d.ts +4 -0
  219. package/dist/mcp/tools/auto-inject.d.ts.map +1 -1
  220. package/dist/mcp/tools/auto-inject.js +13 -4
  221. package/dist/mcp/tools/auto-inject.js.map +1 -1
  222. package/dist/mcp/tools/chunking.d.ts +3 -0
  223. package/dist/mcp/tools/chunking.d.ts.map +1 -1
  224. package/dist/mcp/tools/chunking.js +10 -4
  225. package/dist/mcp/tools/chunking.js.map +1 -1
  226. package/dist/mcp/tools/embeddings.d.ts +4 -0
  227. package/dist/mcp/tools/embeddings.d.ts.map +1 -1
  228. package/dist/mcp/tools/embeddings.js +16 -6
  229. package/dist/mcp/tools/embeddings.js.map +1 -1
  230. package/dist/mcp/tools/index.d.ts +2 -1
  231. package/dist/mcp/tools/index.d.ts.map +1 -1
  232. package/dist/mcp/tools/index.js +15 -1
  233. package/dist/mcp/tools/index.js.map +1 -1
  234. package/dist/mcp/tools/knowledge-graph.d.ts +3 -0
  235. package/dist/mcp/tools/knowledge-graph.d.ts.map +1 -1
  236. package/dist/mcp/tools/knowledge-graph.js +19 -8
  237. package/dist/mcp/tools/knowledge-graph.js.map +1 -1
  238. package/dist/mcp/tools/lazy-context.d.ts +2 -0
  239. package/dist/mcp/tools/lazy-context.d.ts.map +1 -1
  240. package/dist/mcp/tools/lazy-context.js +10 -4
  241. package/dist/mcp/tools/lazy-context.js.map +1 -1
  242. package/dist/mcp/tools/learning-store.d.ts +30 -0
  243. package/dist/mcp/tools/learning-store.d.ts.map +1 -0
  244. package/dist/mcp/tools/learning-store.js +286 -0
  245. package/dist/mcp/tools/learning-store.js.map +1 -0
  246. package/dist/mcp/tools/memory-decay.d.ts.map +1 -1
  247. package/dist/mcp/tools/memory-decay.js +4 -2
  248. package/dist/mcp/tools/memory-decay.js.map +1 -1
  249. package/dist/mcp/tools/prefetch.d.ts +4 -1
  250. package/dist/mcp/tools/prefetch.d.ts.map +1 -1
  251. package/dist/mcp/tools/prefetch.js +18 -7
  252. package/dist/mcp/tools/prefetch.js.map +1 -1
  253. package/dist/mcp/tools/ralph.d.ts +2 -0
  254. package/dist/mcp/tools/ralph.d.ts.map +1 -1
  255. package/dist/mcp/tools/ralph.js +96 -6
  256. package/dist/mcp/tools/ralph.js.map +1 -1
  257. package/dist/mcp/tools/recommendations.d.ts +2 -0
  258. package/dist/mcp/tools/recommendations.d.ts.map +1 -1
  259. package/dist/mcp/tools/recommendations.js +13 -4
  260. package/dist/mcp/tools/recommendations.js.map +1 -1
  261. package/dist/mcp/tools/retrieval.d.ts +4 -0
  262. package/dist/mcp/tools/retrieval.d.ts.map +1 -1
  263. package/dist/mcp/tools/retrieval.js +25 -10
  264. package/dist/mcp/tools/retrieval.js.map +1 -1
  265. package/dist/mcp/tools/spec-feedback.d.ts +2 -0
  266. package/dist/mcp/tools/spec-feedback.d.ts.map +1 -1
  267. package/dist/mcp/tools/spec-feedback.js +33 -16
  268. package/dist/mcp/tools/spec-feedback.js.map +1 -1
  269. package/dist/mcp/utils/index.d.ts +1 -0
  270. package/dist/mcp/utils/index.d.ts.map +1 -1
  271. package/dist/mcp/utils/index.js +2 -0
  272. package/dist/mcp/utils/index.js.map +1 -1
  273. package/dist/mcp/utils/session-context.d.ts +108 -0
  274. package/dist/mcp/utils/session-context.d.ts.map +1 -0
  275. package/dist/mcp/utils/session-context.js +157 -0
  276. package/dist/mcp/utils/session-context.js.map +1 -0
  277. package/package.json +1 -1
@@ -0,0 +1,663 @@
1
+ /**
2
+ * Tests for Learning Loop from Corrections (MAC-9)
3
+ */
4
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
7
+ import { formatLearnedPattern, formatLearningStats, formatPatternsForSession, LearningStore, } from '../../memory/learning-store.js';
8
+ import { CorrectionTracker, detectCorrection, formatCluster, formatCorrection, } from '../correction-tracker.js';
9
+ import { calculateConfidenceAdjustment, formatDetectedPattern, formatSuggestionsForSession, PatternDetector, } from '../pattern-detector.js';
10
+ // =============================================================================
11
+ // Test Fixtures
12
+ // =============================================================================
13
+ const testDir = join(process.cwd(), '.test-learning-loop');
14
+ function createTestContext(original, corrected) {
15
+ return {
16
+ original,
17
+ corrected,
18
+ filePath: 'src/test.ts',
19
+ focusArea: 'src/',
20
+ };
21
+ }
22
+ // =============================================================================
23
+ // CorrectionTracker Tests
24
+ // =============================================================================
25
+ describe('CorrectionTracker', () => {
26
+ let tracker;
27
+ beforeEach(() => {
28
+ tracker = new CorrectionTracker();
29
+ });
30
+ describe('basic tracking', () => {
31
+ it('should track a correction', () => {
32
+ const context = createTestContext('const x = 1;', 'const x = 1');
33
+ const correction = tracker.track(context, 'explicit');
34
+ expect(correction.id).toBeDefined();
35
+ expect(correction.type).toBe('explicit');
36
+ expect(correction.context.original).toBe('const x = 1;');
37
+ expect(correction.context.corrected).toBe('const x = 1');
38
+ expect(correction.keywords.length).toBeGreaterThan(0);
39
+ });
40
+ it('should get all corrections', () => {
41
+ tracker.track(createTestContext('a', 'b'), 'explicit');
42
+ tracker.track(createTestContext('c', 'd'), 'implicit');
43
+ const corrections = tracker.getCorrections();
44
+ expect(corrections).toHaveLength(2);
45
+ });
46
+ it('should get correction by ID', () => {
47
+ const correction = tracker.track(createTestContext('x', 'y'), 'explicit');
48
+ const retrieved = tracker.getCorrection(correction.id);
49
+ expect(retrieved).toBeDefined();
50
+ expect(retrieved?.id).toBe(correction.id);
51
+ });
52
+ });
53
+ describe('correction detection', () => {
54
+ it('should detect "no, use X" pattern', () => {
55
+ const result = detectCorrection('no, use single quotes');
56
+ expect(result.detected).toBe(true);
57
+ expect(result.pattern).toBe('use single quotes');
58
+ });
59
+ it('should detect "actually, X" pattern', () => {
60
+ const result = detectCorrection('actually, it should be camelCase');
61
+ expect(result.detected).toBe(true);
62
+ });
63
+ it('should detect "change X to Y" pattern', () => {
64
+ const result = detectCorrection('change tabs to spaces');
65
+ expect(result.detected).toBe(true);
66
+ expect(result.pattern).toBe('tabs');
67
+ expect(result.replacement).toBe('spaces');
68
+ });
69
+ it('should return false for non-corrections', () => {
70
+ const result = detectCorrection('looks good to me');
71
+ expect(result.detected).toBe(false);
72
+ });
73
+ });
74
+ describe('clustering', () => {
75
+ it('should cluster similar corrections', () => {
76
+ // Track multiple similar corrections
77
+ tracker.track(createTestContext('var x', 'const x'), 'explicit');
78
+ tracker.track(createTestContext('var y', 'const y'), 'explicit');
79
+ tracker.track(createTestContext('var z', 'const z'), 'explicit');
80
+ const clusters = tracker.getClusters();
81
+ expect(clusters.length).toBeGreaterThan(0);
82
+ });
83
+ it('should identify promotable clusters', () => {
84
+ // Create tracker with low threshold for testing
85
+ const testTracker = new CorrectionTracker({ automationThreshold: 2 });
86
+ testTracker.track(createTestContext('semicolon;', 'no semicolon'), 'explicit');
87
+ testTracker.track(createTestContext('add semicolon;', 'remove semicolon'), 'explicit');
88
+ const promotable = testTracker.getPromotableClusters();
89
+ // May or may not be promotable depending on similarity
90
+ expect(Array.isArray(promotable)).toBe(true);
91
+ });
92
+ it('should find similar corrections', () => {
93
+ tracker.track(createTestContext('use npm', 'use bun'), 'explicit');
94
+ tracker.track(createTestContext('npm install', 'bun install'), 'explicit');
95
+ const similar = tracker.findSimilar(createTestContext('npm run', 'bun run'), 0.3);
96
+ expect(similar.length).toBeGreaterThanOrEqual(0);
97
+ });
98
+ });
99
+ describe('statistics', () => {
100
+ it('should return correct stats', () => {
101
+ tracker.track(createTestContext('a', 'b'), 'explicit');
102
+ tracker.track(createTestContext('c', 'd'), 'implicit');
103
+ tracker.track(createTestContext('e', 'f'), 'style');
104
+ const stats = tracker.getStats();
105
+ expect(stats.totalCorrections).toBe(3);
106
+ expect(stats.byType.explicit).toBe(1);
107
+ expect(stats.byType.implicit).toBe(1);
108
+ expect(stats.byType.style).toBe(1);
109
+ });
110
+ });
111
+ describe('export/import', () => {
112
+ it('should export and import state', () => {
113
+ tracker.track(createTestContext('x', 'y'), 'explicit');
114
+ tracker.track(createTestContext('a', 'b'), 'implicit');
115
+ const exported = tracker.export();
116
+ expect(exported.corrections).toHaveLength(2);
117
+ const newTracker = new CorrectionTracker();
118
+ newTracker.import(exported);
119
+ expect(newTracker.getCorrections()).toHaveLength(2);
120
+ });
121
+ });
122
+ describe('formatting', () => {
123
+ it('should format correction', () => {
124
+ const correction = tracker.track(createTestContext('old', 'new'), 'explicit');
125
+ const formatted = formatCorrection(correction);
126
+ expect(formatted).toContain('Correction');
127
+ expect(formatted).toContain('old');
128
+ expect(formatted).toContain('new');
129
+ });
130
+ it('should format cluster', () => {
131
+ tracker.track(createTestContext('x', 'y'), 'explicit');
132
+ const clusters = tracker.getClusters();
133
+ if (clusters.length > 0) {
134
+ const formatted = formatCluster(clusters[0]);
135
+ expect(formatted).toContain('Cluster');
136
+ expect(formatted).toContain('occurrences');
137
+ }
138
+ });
139
+ });
140
+ });
141
+ // =============================================================================
142
+ // PatternDetector Tests
143
+ // =============================================================================
144
+ describe('PatternDetector', () => {
145
+ let detector;
146
+ let tracker;
147
+ beforeEach(() => {
148
+ detector = new PatternDetector();
149
+ tracker = new CorrectionTracker({ automationThreshold: 2 });
150
+ });
151
+ describe('pattern detection', () => {
152
+ it('should detect pattern from cluster', () => {
153
+ // Create enough similar corrections
154
+ tracker.track(createTestContext('use semicolons;', 'no semicolons'), 'explicit');
155
+ tracker.track(createTestContext('add semicolon;', 'remove semicolon'), 'explicit');
156
+ tracker.track(createTestContext('with semicolon;', 'without semicolon'), 'explicit');
157
+ const clusters = tracker.getClusters().filter((c) => c.count >= 2);
158
+ if (clusters.length > 0) {
159
+ const pattern = detector.detectFromCluster(clusters[0]);
160
+ if (pattern) {
161
+ expect(pattern.trigger).toBeDefined();
162
+ expect(pattern.preference).toBeDefined();
163
+ expect(pattern.type).toBeDefined();
164
+ }
165
+ }
166
+ });
167
+ it('should detect patterns from multiple clusters', () => {
168
+ tracker.track(createTestContext('double quotes', 'single quotes'), 'explicit');
169
+ tracker.track(createTestContext('"hello"', "'hello'"), 'explicit');
170
+ const clusters = tracker.getClusters();
171
+ const patterns = detector.detectFromClusters(clusters);
172
+ expect(Array.isArray(patterns)).toBe(true);
173
+ });
174
+ it('should classify pattern types correctly', () => {
175
+ // Create style-related corrections
176
+ const styleTracker = new CorrectionTracker({ automationThreshold: 1 });
177
+ styleTracker.track(createTestContext('2 space indent', '4 space indent'), 'style');
178
+ styleTracker.track(createTestContext('tabs', 'spaces'), 'style');
179
+ styleTracker.track(createTestContext('tabs indent', 'space indent'), 'style');
180
+ const clusters = styleTracker.getClusters();
181
+ if (clusters.length > 0) {
182
+ const pattern = detector.detectFromCluster(clusters[0]);
183
+ if (pattern) {
184
+ expect(['style', 'preference']).toContain(pattern.type);
185
+ }
186
+ }
187
+ });
188
+ });
189
+ describe('suggestions', () => {
190
+ it('should provide context-aware suggestions', () => {
191
+ // Add a pattern manually
192
+ const mockPattern = {
193
+ id: 'test-1',
194
+ type: 'style',
195
+ trigger: 'semicolons',
196
+ preference: 'no semicolons',
197
+ confidence: 'high',
198
+ occurrences: 5,
199
+ examples: [],
200
+ keywords: ['semicolon', 'style'],
201
+ sourceClusterId: 'cluster-1',
202
+ detectedAt: new Date(),
203
+ };
204
+ detector.import([mockPattern]);
205
+ const suggestions = detector.getSuggestions({
206
+ focusArea: 'src/',
207
+ currentTask: 'fix semicolon issues',
208
+ });
209
+ expect(suggestions.length).toBeGreaterThan(0);
210
+ expect(suggestions[0].pattern.id).toBe('test-1');
211
+ });
212
+ it('should return empty for no relevant patterns', () => {
213
+ const suggestions = detector.getSuggestions({
214
+ focusArea: 'test/',
215
+ currentTask: 'write tests',
216
+ });
217
+ expect(suggestions).toHaveLength(0);
218
+ });
219
+ });
220
+ describe('confidence adjustment', () => {
221
+ it('should calculate positive adjustment for patterns', () => {
222
+ const patterns = [
223
+ {
224
+ id: 'p1',
225
+ type: 'style',
226
+ trigger: 't',
227
+ preference: 'p',
228
+ confidence: 'high',
229
+ occurrences: 5,
230
+ examples: [],
231
+ keywords: [],
232
+ sourceClusterId: 'c1',
233
+ detectedAt: new Date(),
234
+ },
235
+ {
236
+ id: 'p2',
237
+ type: 'naming',
238
+ trigger: 't2',
239
+ preference: 'p2',
240
+ confidence: 'medium',
241
+ occurrences: 3,
242
+ examples: [],
243
+ keywords: [],
244
+ sourceClusterId: 'c2',
245
+ detectedAt: new Date(),
246
+ },
247
+ ];
248
+ const adjustment = calculateConfidenceAdjustment(patterns);
249
+ expect(adjustment).toBeGreaterThan(0);
250
+ expect(adjustment).toBeLessThanOrEqual(0.1);
251
+ });
252
+ it('should return 0 for no patterns', () => {
253
+ const adjustment = calculateConfidenceAdjustment([]);
254
+ expect(adjustment).toBe(0);
255
+ });
256
+ });
257
+ describe('formatting', () => {
258
+ it('should format detected pattern', () => {
259
+ const pattern = {
260
+ id: 'test',
261
+ type: 'style',
262
+ trigger: 'semicolons',
263
+ preference: 'no semicolons',
264
+ confidence: 'high',
265
+ occurrences: 5,
266
+ examples: [],
267
+ keywords: ['semi', 'style'],
268
+ sourceClusterId: 'c1',
269
+ detectedAt: new Date(),
270
+ };
271
+ const formatted = formatDetectedPattern(pattern);
272
+ expect(formatted).toContain('style');
273
+ expect(formatted).toContain('5 occurrences');
274
+ expect(formatted).toContain('semicolons');
275
+ });
276
+ it('should format suggestions for session', () => {
277
+ const suggestions = [
278
+ {
279
+ pattern: {
280
+ id: 'test',
281
+ type: 'style',
282
+ trigger: 'tabs',
283
+ preference: 'spaces',
284
+ confidence: 'high',
285
+ occurrences: 3,
286
+ examples: [],
287
+ keywords: [],
288
+ sourceClusterId: 'c1',
289
+ detectedAt: new Date(),
290
+ },
291
+ relevance: 0.8,
292
+ reason: 'frequently corrected',
293
+ },
294
+ ];
295
+ const formatted = formatSuggestionsForSession(suggestions);
296
+ expect(formatted).toContain('Learned Patterns');
297
+ expect(formatted).toContain('tabs');
298
+ expect(formatted).toContain('spaces');
299
+ });
300
+ });
301
+ });
302
+ // =============================================================================
303
+ // LearningStore Tests
304
+ // =============================================================================
305
+ describe('LearningStore', () => {
306
+ let store;
307
+ beforeEach(() => {
308
+ // Clean up test directory
309
+ if (existsSync(testDir)) {
310
+ rmSync(testDir, { recursive: true });
311
+ }
312
+ mkdirSync(testDir, { recursive: true });
313
+ store = new LearningStore(testDir, { autoSave: false });
314
+ });
315
+ afterEach(() => {
316
+ if (existsSync(testDir)) {
317
+ rmSync(testDir, { recursive: true });
318
+ }
319
+ });
320
+ describe('pattern storage', () => {
321
+ it('should add and retrieve patterns', () => {
322
+ const pattern = {
323
+ id: 'test-pattern',
324
+ type: 'style',
325
+ trigger: 'semicolons',
326
+ preference: 'no semicolons',
327
+ confidence: 'high',
328
+ occurrences: 5,
329
+ examples: [{ original: 'x;', corrected: 'x' }],
330
+ keywords: ['semicolon'],
331
+ sourceClusterId: 'cluster-1',
332
+ detectedAt: new Date(),
333
+ };
334
+ const learned = store.addPattern(pattern);
335
+ expect(learned.id).toBe('test-pattern');
336
+ expect(learned.active).toBe(true);
337
+ expect(learned.applyCount).toBe(0);
338
+ const retrieved = store.getPattern('test-pattern');
339
+ expect(retrieved).toBeDefined();
340
+ expect(retrieved?.trigger).toBe('semicolons');
341
+ });
342
+ it('should get active patterns only', () => {
343
+ const pattern1 = {
344
+ id: 'p1',
345
+ type: 'style',
346
+ trigger: 't1',
347
+ preference: 'p1',
348
+ confidence: 'high',
349
+ occurrences: 3,
350
+ examples: [],
351
+ keywords: [],
352
+ sourceClusterId: 'c1',
353
+ detectedAt: new Date(),
354
+ };
355
+ const pattern2 = {
356
+ id: 'p2',
357
+ type: 'naming',
358
+ trigger: 't2',
359
+ preference: 'p2',
360
+ confidence: 'medium',
361
+ occurrences: 2,
362
+ examples: [],
363
+ keywords: [],
364
+ sourceClusterId: 'c2',
365
+ detectedAt: new Date(),
366
+ };
367
+ store.addPattern(pattern1);
368
+ store.addPattern(pattern2);
369
+ store.deactivatePattern('p2');
370
+ const active = store.getActivePatterns();
371
+ expect(active).toHaveLength(1);
372
+ expect(active[0].id).toBe('p1');
373
+ });
374
+ it('should record pattern application', () => {
375
+ const pattern = {
376
+ id: 'apply-test',
377
+ type: 'tool',
378
+ trigger: 'npm',
379
+ preference: 'bun',
380
+ confidence: 'high',
381
+ occurrences: 5,
382
+ examples: [],
383
+ keywords: [],
384
+ sourceClusterId: 'c1',
385
+ detectedAt: new Date(),
386
+ };
387
+ store.addPattern(pattern);
388
+ store.recordApplication('apply-test');
389
+ store.recordApplication('apply-test');
390
+ const retrieved = store.getPattern('apply-test');
391
+ expect(retrieved?.applyCount).toBe(2);
392
+ expect(retrieved?.lastApplied).toBeDefined();
393
+ });
394
+ });
395
+ describe('persistence', () => {
396
+ it('should save and load state', () => {
397
+ const pattern = {
398
+ id: 'persist-test',
399
+ type: 'workflow',
400
+ trigger: 'before commit',
401
+ preference: 'run tests',
402
+ confidence: 'high',
403
+ occurrences: 4,
404
+ examples: [],
405
+ keywords: ['test', 'commit'],
406
+ sourceClusterId: 'c1',
407
+ detectedAt: new Date(),
408
+ };
409
+ store.addPattern(pattern);
410
+ store.save();
411
+ // Create new store and load
412
+ const newStore = new LearningStore(testDir);
413
+ newStore.load();
414
+ const retrieved = newStore.getPattern('persist-test');
415
+ expect(retrieved).toBeDefined();
416
+ expect(retrieved?.trigger).toBe('before commit');
417
+ });
418
+ });
419
+ describe('suggestions', () => {
420
+ it('should provide context-relevant suggestions', () => {
421
+ const pattern = {
422
+ id: 'suggest-test',
423
+ type: 'style',
424
+ trigger: 'formatting',
425
+ preference: 'use prettier',
426
+ confidence: 'high',
427
+ occurrences: 5,
428
+ examples: [],
429
+ keywords: ['format', 'prettier', 'style'],
430
+ sourceClusterId: 'c1',
431
+ detectedAt: new Date(),
432
+ };
433
+ store.addPattern(pattern);
434
+ const suggestions = store.getSuggestions({
435
+ focusArea: 'src/',
436
+ currentTask: 'fix formatting issues',
437
+ });
438
+ expect(suggestions.length).toBeGreaterThan(0);
439
+ });
440
+ });
441
+ describe('confidence boost', () => {
442
+ it('should calculate confidence boost from patterns', () => {
443
+ const patterns = [
444
+ {
445
+ id: 'boost1',
446
+ type: 'style',
447
+ trigger: 't1',
448
+ preference: 'p1',
449
+ confidence: 'high',
450
+ occurrences: 5,
451
+ examples: [],
452
+ keywords: ['test', 'style'],
453
+ sourceClusterId: 'c1',
454
+ detectedAt: new Date(),
455
+ },
456
+ {
457
+ id: 'boost2',
458
+ type: 'naming',
459
+ trigger: 't2',
460
+ preference: 'p2',
461
+ confidence: 'medium',
462
+ occurrences: 3,
463
+ examples: [],
464
+ keywords: ['name', 'convention'],
465
+ sourceClusterId: 'c2',
466
+ detectedAt: new Date(),
467
+ },
468
+ ];
469
+ for (const p of patterns) {
470
+ store.addPattern(p);
471
+ }
472
+ const boost = store.calculateConfidenceBoost({
473
+ focusArea: 'src/',
474
+ currentTask: 'test style improvements',
475
+ });
476
+ expect(boost).toBeGreaterThanOrEqual(0);
477
+ expect(boost).toBeLessThanOrEqual(0.1);
478
+ });
479
+ });
480
+ describe('statistics', () => {
481
+ it('should return correct stats', () => {
482
+ const patterns = [
483
+ {
484
+ id: 's1',
485
+ type: 'style',
486
+ trigger: 't1',
487
+ preference: 'p1',
488
+ confidence: 'high',
489
+ occurrences: 5,
490
+ examples: [],
491
+ keywords: [],
492
+ sourceClusterId: 'c1',
493
+ detectedAt: new Date(),
494
+ },
495
+ {
496
+ id: 's2',
497
+ type: 'style',
498
+ trigger: 't2',
499
+ preference: 'p2',
500
+ confidence: 'medium',
501
+ occurrences: 3,
502
+ examples: [],
503
+ keywords: [],
504
+ sourceClusterId: 'c2',
505
+ detectedAt: new Date(),
506
+ },
507
+ {
508
+ id: 'n1',
509
+ type: 'naming',
510
+ trigger: 't3',
511
+ preference: 'p3',
512
+ confidence: 'low',
513
+ occurrences: 2,
514
+ examples: [],
515
+ keywords: [],
516
+ sourceClusterId: 'c3',
517
+ detectedAt: new Date(),
518
+ },
519
+ ];
520
+ for (const p of patterns) {
521
+ store.addPattern(p);
522
+ }
523
+ const stats = store.getStats();
524
+ expect(stats.totalPatterns).toBe(3);
525
+ expect(stats.activePatterns).toBe(3);
526
+ expect(stats.patternsByType.style).toBe(2);
527
+ expect(stats.patternsByType.naming).toBe(1);
528
+ });
529
+ });
530
+ describe('formatting', () => {
531
+ it('should format learned pattern', () => {
532
+ const pattern = {
533
+ id: 'fmt-test',
534
+ type: 'tool',
535
+ trigger: 'npm',
536
+ preference: 'bun',
537
+ confidence: 'high',
538
+ occurrences: 5,
539
+ examples: [],
540
+ keywords: [],
541
+ sourceClusterId: 'c1',
542
+ detectedAt: new Date(),
543
+ };
544
+ const learned = store.addPattern(pattern);
545
+ const formatted = formatLearnedPattern(learned);
546
+ expect(formatted).toContain('tool');
547
+ expect(formatted).toContain('npm');
548
+ expect(formatted).toContain('bun');
549
+ });
550
+ it('should format patterns for session', () => {
551
+ const patterns = [
552
+ {
553
+ id: 'sess1',
554
+ type: 'style',
555
+ trigger: 'tabs',
556
+ preference: 'spaces',
557
+ confidence: 'high',
558
+ occurrences: 5,
559
+ examples: [],
560
+ keywords: [],
561
+ sourceClusterId: 'c1',
562
+ detectedAt: new Date(),
563
+ },
564
+ ];
565
+ for (const p of patterns) {
566
+ store.addPattern(p);
567
+ }
568
+ const formatted = formatPatternsForSession(store.getActivePatterns());
569
+ expect(formatted).toContain('Learned Preferences');
570
+ expect(formatted).toContain('tabs');
571
+ expect(formatted).toContain('spaces');
572
+ });
573
+ it('should format stats', () => {
574
+ const pattern = {
575
+ id: 'stat-fmt',
576
+ type: 'workflow',
577
+ trigger: 't',
578
+ preference: 'p',
579
+ confidence: 'high',
580
+ occurrences: 3,
581
+ examples: [],
582
+ keywords: [],
583
+ sourceClusterId: 'c1',
584
+ detectedAt: new Date(),
585
+ };
586
+ store.addPattern(pattern);
587
+ const formatted = formatLearningStats(store.getStats());
588
+ expect(formatted).toContain('Learning Store Stats');
589
+ expect(formatted).toContain('Patterns');
590
+ });
591
+ });
592
+ });
593
+ // =============================================================================
594
+ // Integration Tests
595
+ // =============================================================================
596
+ describe('Learning Loop Integration', () => {
597
+ let tracker;
598
+ let detector;
599
+ let store;
600
+ beforeEach(() => {
601
+ if (existsSync(testDir)) {
602
+ rmSync(testDir, { recursive: true });
603
+ }
604
+ mkdirSync(testDir, { recursive: true });
605
+ tracker = new CorrectionTracker({ automationThreshold: 2 });
606
+ detector = new PatternDetector({ minClusterSize: 2 });
607
+ store = new LearningStore(testDir, { autoSave: false });
608
+ });
609
+ afterEach(() => {
610
+ if (existsSync(testDir)) {
611
+ rmSync(testDir, { recursive: true });
612
+ }
613
+ });
614
+ it('should complete full learning loop', () => {
615
+ // Step 1: Track corrections
616
+ tracker.track(createTestContext('use var', 'use const'), 'explicit');
617
+ tracker.track(createTestContext('var x', 'const x'), 'explicit');
618
+ tracker.track(createTestContext('declare var', 'declare const'), 'explicit');
619
+ // Step 2: Get promotable clusters
620
+ const clusters = tracker.getClusters();
621
+ expect(clusters.length).toBeGreaterThan(0);
622
+ // Step 3: Detect patterns from clusters
623
+ const patterns = detector.detectFromClusters(clusters);
624
+ // Step 4: Store patterns
625
+ for (const pattern of patterns) {
626
+ store.addPattern(pattern);
627
+ }
628
+ // Step 5: Verify patterns are stored
629
+ const storedPatterns = store.getAllPatterns();
630
+ expect(storedPatterns.length).toBeGreaterThanOrEqual(0);
631
+ // Step 6: Get suggestions for context
632
+ const suggestions = store.getSuggestions({
633
+ focusArea: 'src/',
634
+ currentTask: 'refactor variables',
635
+ });
636
+ // Suggestions may or may not be returned depending on keyword matching
637
+ expect(Array.isArray(suggestions)).toBe(true);
638
+ });
639
+ it('should integrate with confidence scoring', () => {
640
+ // Add some patterns
641
+ const pattern = {
642
+ id: 'conf-int',
643
+ type: 'style',
644
+ trigger: 'formatting',
645
+ preference: 'prettier',
646
+ confidence: 'high',
647
+ occurrences: 5,
648
+ examples: [],
649
+ keywords: ['format', 'style'],
650
+ sourceClusterId: 'c1',
651
+ detectedAt: new Date(),
652
+ };
653
+ store.addPattern(pattern);
654
+ // Calculate confidence boost
655
+ const boost = store.calculateConfidenceBoost({
656
+ focusArea: 'src/',
657
+ currentTask: 'format code',
658
+ });
659
+ expect(boost).toBeGreaterThanOrEqual(0);
660
+ expect(boost).toBeLessThanOrEqual(0.1);
661
+ });
662
+ });
663
+ //# sourceMappingURL=learning-loop.test.js.map