@timmeck/brain 1.0.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.
- package/BRAIN_PLAN.md +3324 -0
- package/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/brain.d.ts +11 -0
- package/dist/brain.js +166 -0
- package/dist/brain.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +2 -0
- package/dist/cli/commands/dashboard.js +457 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.js +25 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +173 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/insights.d.ts +2 -0
- package/dist/cli/commands/insights.js +32 -0
- package/dist/cli/commands/insights.js.map +1 -0
- package/dist/cli/commands/modules.d.ts +2 -0
- package/dist/cli/commands/modules.js +29 -0
- package/dist/cli/commands/modules.js.map +1 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +56 -0
- package/dist/cli/commands/network.js.map +1 -0
- package/dist/cli/commands/query.d.ts +2 -0
- package/dist/cli/commands/query.js +40 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +56 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +60 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.js +34 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/ipc-helper.d.ts +2 -0
- package/dist/cli/ipc-helper.js +25 -0
- package/dist/cli/ipc-helper.js.map +1 -0
- package/dist/code/analyzer.d.ts +12 -0
- package/dist/code/analyzer.js +58 -0
- package/dist/code/analyzer.js.map +1 -0
- package/dist/code/fingerprint.d.ts +2 -0
- package/dist/code/fingerprint.js +84 -0
- package/dist/code/fingerprint.js.map +1 -0
- package/dist/code/matcher.d.ts +9 -0
- package/dist/code/matcher.js +37 -0
- package/dist/code/matcher.js.map +1 -0
- package/dist/code/parsers/generic.d.ts +7 -0
- package/dist/code/parsers/generic.js +22 -0
- package/dist/code/parsers/generic.js.map +1 -0
- package/dist/code/parsers/python.d.ts +7 -0
- package/dist/code/parsers/python.js +45 -0
- package/dist/code/parsers/python.js.map +1 -0
- package/dist/code/parsers/typescript.d.ts +7 -0
- package/dist/code/parsers/typescript.js +58 -0
- package/dist/code/parsers/typescript.js.map +1 -0
- package/dist/code/registry.d.ts +22 -0
- package/dist/code/registry.js +31 -0
- package/dist/code/registry.js.map +1 -0
- package/dist/code/scorer.d.ts +15 -0
- package/dist/code/scorer.js +103 -0
- package/dist/code/scorer.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +110 -0
- package/dist/config.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +19 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrations/001_core_schema.d.ts +2 -0
- package/dist/db/migrations/001_core_schema.js +119 -0
- package/dist/db/migrations/001_core_schema.js.map +1 -0
- package/dist/db/migrations/002_learning_schema.d.ts +2 -0
- package/dist/db/migrations/002_learning_schema.js +37 -0
- package/dist/db/migrations/002_learning_schema.js.map +1 -0
- package/dist/db/migrations/003_code_schema.d.ts +2 -0
- package/dist/db/migrations/003_code_schema.js +52 -0
- package/dist/db/migrations/003_code_schema.js.map +1 -0
- package/dist/db/migrations/004_synapses_schema.d.ts +2 -0
- package/dist/db/migrations/004_synapses_schema.js +56 -0
- package/dist/db/migrations/004_synapses_schema.js.map +1 -0
- package/dist/db/migrations/005_fts_indexes.d.ts +2 -0
- package/dist/db/migrations/005_fts_indexes.js +77 -0
- package/dist/db/migrations/005_fts_indexes.js.map +1 -0
- package/dist/db/migrations/006_synapses_phase3.d.ts +2 -0
- package/dist/db/migrations/006_synapses_phase3.js +14 -0
- package/dist/db/migrations/006_synapses_phase3.js.map +1 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/migrations/index.js +49 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/repositories/antipattern.repository.d.ts +26 -0
- package/dist/db/repositories/antipattern.repository.js +44 -0
- package/dist/db/repositories/antipattern.repository.js.map +1 -0
- package/dist/db/repositories/code-module.repository.d.ts +19 -0
- package/dist/db/repositories/code-module.repository.js +64 -0
- package/dist/db/repositories/code-module.repository.js.map +1 -0
- package/dist/db/repositories/error.repository.d.ts +20 -0
- package/dist/db/repositories/error.repository.js +134 -0
- package/dist/db/repositories/error.repository.js.map +1 -0
- package/dist/db/repositories/insight.repository.d.ts +18 -0
- package/dist/db/repositories/insight.repository.js +57 -0
- package/dist/db/repositories/insight.repository.js.map +1 -0
- package/dist/db/repositories/notification.repository.d.ts +24 -0
- package/dist/db/repositories/notification.repository.js +40 -0
- package/dist/db/repositories/notification.repository.js.map +1 -0
- package/dist/db/repositories/project.repository.d.ts +25 -0
- package/dist/db/repositories/project.repository.js +72 -0
- package/dist/db/repositories/project.repository.js.map +1 -0
- package/dist/db/repositories/rule.repository.d.ts +31 -0
- package/dist/db/repositories/rule.repository.js +81 -0
- package/dist/db/repositories/rule.repository.js.map +1 -0
- package/dist/db/repositories/solution.repository.d.ts +27 -0
- package/dist/db/repositories/solution.repository.js +132 -0
- package/dist/db/repositories/solution.repository.js.map +1 -0
- package/dist/db/repositories/synapse.repository.d.ts +25 -0
- package/dist/db/repositories/synapse.repository.js +115 -0
- package/dist/db/repositories/synapse.repository.js.map +1 -0
- package/dist/db/repositories/terminal.repository.d.ts +27 -0
- package/dist/db/repositories/terminal.repository.js +78 -0
- package/dist/db/repositories/terminal.repository.js.map +1 -0
- package/dist/hooks/post-tool-use.d.ts +2 -0
- package/dist/hooks/post-tool-use.js +77 -0
- package/dist/hooks/post-tool-use.js.map +1 -0
- package/dist/hooks/post-write.d.ts +2 -0
- package/dist/hooks/post-write.js +102 -0
- package/dist/hooks/post-write.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +16 -0
- package/dist/ipc/client.js +101 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/protocol.d.ts +8 -0
- package/dist/ipc/protocol.js +29 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/router.d.ts +28 -0
- package/dist/ipc/router.js +70 -0
- package/dist/ipc/router.js.map +1 -0
- package/dist/ipc/server.d.ts +14 -0
- package/dist/ipc/server.js +98 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/learning/confidence-scorer.d.ts +13 -0
- package/dist/learning/confidence-scorer.js +35 -0
- package/dist/learning/confidence-scorer.js.map +1 -0
- package/dist/learning/decay.d.ts +13 -0
- package/dist/learning/decay.js +37 -0
- package/dist/learning/decay.js.map +1 -0
- package/dist/learning/learning-engine.d.ts +30 -0
- package/dist/learning/learning-engine.js +121 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/learning/pattern-extractor.d.ts +16 -0
- package/dist/learning/pattern-extractor.js +61 -0
- package/dist/learning/pattern-extractor.js.map +1 -0
- package/dist/learning/rule-generator.d.ts +18 -0
- package/dist/learning/rule-generator.js +50 -0
- package/dist/learning/rule-generator.js.map +1 -0
- package/dist/matching/error-matcher.d.ts +13 -0
- package/dist/matching/error-matcher.js +84 -0
- package/dist/matching/error-matcher.js.map +1 -0
- package/dist/matching/fingerprint.d.ts +3 -0
- package/dist/matching/fingerprint.js +23 -0
- package/dist/matching/fingerprint.js.map +1 -0
- package/dist/matching/similarity.d.ts +3 -0
- package/dist/matching/similarity.js +53 -0
- package/dist/matching/similarity.js.map +1 -0
- package/dist/matching/tfidf.d.ts +15 -0
- package/dist/matching/tfidf.js +68 -0
- package/dist/matching/tfidf.js.map +1 -0
- package/dist/matching/tokenizer.d.ts +4 -0
- package/dist/matching/tokenizer.js +36 -0
- package/dist/matching/tokenizer.js.map +1 -0
- package/dist/mcp/auto-detect.d.ts +1 -0
- package/dist/mcp/auto-detect.js +81 -0
- package/dist/mcp/auto-detect.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +68 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.js +201 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/parsing/error-parser.d.ts +3 -0
- package/dist/parsing/error-parser.js +26 -0
- package/dist/parsing/error-parser.js.map +1 -0
- package/dist/parsing/parsers/compiler.d.ts +2 -0
- package/dist/parsing/parsers/compiler.js +83 -0
- package/dist/parsing/parsers/compiler.js.map +1 -0
- package/dist/parsing/parsers/generic.d.ts +2 -0
- package/dist/parsing/parsers/generic.js +23 -0
- package/dist/parsing/parsers/generic.js.map +1 -0
- package/dist/parsing/parsers/go.d.ts +2 -0
- package/dist/parsing/parsers/go.js +85 -0
- package/dist/parsing/parsers/go.js.map +1 -0
- package/dist/parsing/parsers/node.d.ts +2 -0
- package/dist/parsing/parsers/node.js +61 -0
- package/dist/parsing/parsers/node.js.map +1 -0
- package/dist/parsing/parsers/python.d.ts +2 -0
- package/dist/parsing/parsers/python.js +50 -0
- package/dist/parsing/parsers/python.js.map +1 -0
- package/dist/parsing/parsers/rust.d.ts +2 -0
- package/dist/parsing/parsers/rust.js +43 -0
- package/dist/parsing/parsers/rust.js.map +1 -0
- package/dist/parsing/parsers/shell.d.ts +2 -0
- package/dist/parsing/parsers/shell.js +36 -0
- package/dist/parsing/parsers/shell.js.map +1 -0
- package/dist/parsing/types.d.ts +28 -0
- package/dist/parsing/types.js +21 -0
- package/dist/parsing/types.js.map +1 -0
- package/dist/research/gap-analyzer.d.ts +23 -0
- package/dist/research/gap-analyzer.js +119 -0
- package/dist/research/gap-analyzer.js.map +1 -0
- package/dist/research/insight-generator.d.ts +23 -0
- package/dist/research/insight-generator.js +107 -0
- package/dist/research/insight-generator.js.map +1 -0
- package/dist/research/research-engine.d.ts +31 -0
- package/dist/research/research-engine.js +97 -0
- package/dist/research/research-engine.js.map +1 -0
- package/dist/research/synergy-detector.d.ts +24 -0
- package/dist/research/synergy-detector.js +109 -0
- package/dist/research/synergy-detector.js.map +1 -0
- package/dist/research/template-extractor.d.ts +18 -0
- package/dist/research/template-extractor.js +116 -0
- package/dist/research/template-extractor.js.map +1 -0
- package/dist/research/trend-analyzer.d.ts +20 -0
- package/dist/research/trend-analyzer.js +111 -0
- package/dist/research/trend-analyzer.js.map +1 -0
- package/dist/services/analytics.service.d.ts +52 -0
- package/dist/services/analytics.service.js +59 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/code.service.d.ts +39 -0
- package/dist/services/code.service.js +98 -0
- package/dist/services/code.service.js.map +1 -0
- package/dist/services/error.service.d.ts +35 -0
- package/dist/services/error.service.js +118 -0
- package/dist/services/error.service.js.map +1 -0
- package/dist/services/notification.service.d.ts +17 -0
- package/dist/services/notification.service.js +29 -0
- package/dist/services/notification.service.js.map +1 -0
- package/dist/services/prevention.service.d.ts +35 -0
- package/dist/services/prevention.service.js +82 -0
- package/dist/services/prevention.service.js.map +1 -0
- package/dist/services/research.service.d.ts +35 -0
- package/dist/services/research.service.js +60 -0
- package/dist/services/research.service.js.map +1 -0
- package/dist/services/solution.service.d.ts +30 -0
- package/dist/services/solution.service.js +73 -0
- package/dist/services/solution.service.js.map +1 -0
- package/dist/services/synapse.service.d.ts +30 -0
- package/dist/services/synapse.service.js +25 -0
- package/dist/services/synapse.service.js.map +1 -0
- package/dist/services/terminal.service.d.ts +20 -0
- package/dist/services/terminal.service.js +66 -0
- package/dist/services/terminal.service.js.map +1 -0
- package/dist/synapses/activation.d.ts +13 -0
- package/dist/synapses/activation.js +50 -0
- package/dist/synapses/activation.js.map +1 -0
- package/dist/synapses/decay.d.ts +11 -0
- package/dist/synapses/decay.js +27 -0
- package/dist/synapses/decay.js.map +1 -0
- package/dist/synapses/hebbian.d.ts +13 -0
- package/dist/synapses/hebbian.js +36 -0
- package/dist/synapses/hebbian.js.map +1 -0
- package/dist/synapses/pathfinder.d.ts +14 -0
- package/dist/synapses/pathfinder.js +50 -0
- package/dist/synapses/pathfinder.js.map +1 -0
- package/dist/synapses/synapse-manager.d.ts +30 -0
- package/dist/synapses/synapse-manager.js +72 -0
- package/dist/synapses/synapse-manager.js.map +1 -0
- package/dist/types/code.types.d.ts +47 -0
- package/dist/types/code.types.js +2 -0
- package/dist/types/code.types.js.map +1 -0
- package/dist/types/config.types.d.ts +70 -0
- package/dist/types/config.types.js +2 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/error.types.d.ts +60 -0
- package/dist/types/error.types.js +2 -0
- package/dist/types/error.types.js.map +1 -0
- package/dist/types/ipc.types.d.ts +11 -0
- package/dist/types/ipc.types.js +2 -0
- package/dist/types/ipc.types.js.map +1 -0
- package/dist/types/mcp.types.d.ts +46 -0
- package/dist/types/mcp.types.js +2 -0
- package/dist/types/mcp.types.js.map +1 -0
- package/dist/types/research.types.d.ts +25 -0
- package/dist/types/research.types.js +2 -0
- package/dist/types/research.types.js.map +1 -0
- package/dist/types/solution.types.d.ts +28 -0
- package/dist/types/solution.types.js +2 -0
- package/dist/types/solution.types.js.map +1 -0
- package/dist/types/synapse.types.d.ts +37 -0
- package/dist/types/synapse.types.js +2 -0
- package/dist/types/synapse.types.js.map +1 -0
- package/dist/utils/events.d.ts +59 -0
- package/dist/utils/events.js +23 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +5 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +43 -0
- package/src/brain.ts +220 -0
- package/src/cli/commands/dashboard.ts +495 -0
- package/src/cli/commands/export.ts +27 -0
- package/src/cli/commands/import.ts +190 -0
- package/src/cli/commands/insights.ts +33 -0
- package/src/cli/commands/modules.ts +30 -0
- package/src/cli/commands/network.ts +61 -0
- package/src/cli/commands/query.ts +43 -0
- package/src/cli/commands/start.ts +59 -0
- package/src/cli/commands/status.ts +69 -0
- package/src/cli/commands/stop.ts +33 -0
- package/src/cli/ipc-helper.ts +21 -0
- package/src/code/analyzer.ts +77 -0
- package/src/code/fingerprint.ts +87 -0
- package/src/code/matcher.ts +64 -0
- package/src/code/parsers/generic.ts +29 -0
- package/src/code/parsers/python.ts +54 -0
- package/src/code/parsers/typescript.ts +65 -0
- package/src/code/registry.ts +60 -0
- package/src/code/scorer.ts +108 -0
- package/src/config.ts +111 -0
- package/src/db/connection.ts +22 -0
- package/src/db/migrations/001_core_schema.ts +120 -0
- package/src/db/migrations/002_learning_schema.ts +38 -0
- package/src/db/migrations/003_code_schema.ts +53 -0
- package/src/db/migrations/004_synapses_schema.ts +57 -0
- package/src/db/migrations/005_fts_indexes.ts +78 -0
- package/src/db/migrations/006_synapses_phase3.ts +17 -0
- package/src/db/migrations/index.ts +64 -0
- package/src/db/repositories/antipattern.repository.ts +66 -0
- package/src/db/repositories/code-module.repository.ts +80 -0
- package/src/db/repositories/error.repository.ts +149 -0
- package/src/db/repositories/insight.repository.ts +78 -0
- package/src/db/repositories/notification.repository.ts +66 -0
- package/src/db/repositories/project.repository.ts +93 -0
- package/src/db/repositories/rule.repository.ts +108 -0
- package/src/db/repositories/solution.repository.ts +154 -0
- package/src/db/repositories/synapse.repository.ts +153 -0
- package/src/db/repositories/terminal.repository.ts +101 -0
- package/src/hooks/post-tool-use.ts +90 -0
- package/src/hooks/post-write.ts +117 -0
- package/src/index.ts +53 -0
- package/src/ipc/client.ts +118 -0
- package/src/ipc/protocol.ts +35 -0
- package/src/ipc/router.ts +106 -0
- package/src/ipc/server.ts +110 -0
- package/src/learning/confidence-scorer.ts +47 -0
- package/src/learning/decay.ts +46 -0
- package/src/learning/learning-engine.ts +162 -0
- package/src/learning/pattern-extractor.ts +90 -0
- package/src/learning/rule-generator.ts +74 -0
- package/src/main.rs:10:5 +0 -0
- package/src/matching/error-matcher.ts +115 -0
- package/src/matching/fingerprint.ts +29 -0
- package/src/matching/similarity.ts +61 -0
- package/src/matching/tfidf.ts +74 -0
- package/src/matching/tokenizer.ts +41 -0
- package/src/mcp/auto-detect.ts +93 -0
- package/src/mcp/server.ts +73 -0
- package/src/mcp/tools.ts +290 -0
- package/src/parsing/error-parser.ts +28 -0
- package/src/parsing/parsers/compiler.ts +93 -0
- package/src/parsing/parsers/generic.ts +28 -0
- package/src/parsing/parsers/go.ts +97 -0
- package/src/parsing/parsers/node.ts +69 -0
- package/src/parsing/parsers/python.ts +62 -0
- package/src/parsing/parsers/rust.ts +50 -0
- package/src/parsing/parsers/shell.ts +42 -0
- package/src/parsing/types.ts +47 -0
- package/src/research/gap-analyzer.ts +135 -0
- package/src/research/insight-generator.ts +123 -0
- package/src/research/research-engine.ts +116 -0
- package/src/research/synergy-detector.ts +126 -0
- package/src/research/template-extractor.ts +130 -0
- package/src/research/trend-analyzer.ts +127 -0
- package/src/services/analytics.service.ts +87 -0
- package/src/services/code.service.ts +140 -0
- package/src/services/error.service.ts +164 -0
- package/src/services/notification.service.ts +41 -0
- package/src/services/prevention.service.ts +119 -0
- package/src/services/research.service.ts +93 -0
- package/src/services/solution.service.ts +116 -0
- package/src/services/synapse.service.ts +59 -0
- package/src/services/terminal.service.ts +81 -0
- package/src/synapses/activation.ts +80 -0
- package/src/synapses/decay.ts +38 -0
- package/src/synapses/hebbian.ts +69 -0
- package/src/synapses/pathfinder.ts +81 -0
- package/src/synapses/synapse-manager.ts +109 -0
- package/src/types/code.types.ts +52 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/error.types.ts +67 -0
- package/src/types/ipc.types.ts +8 -0
- package/src/types/mcp.types.ts +53 -0
- package/src/types/research.types.ts +28 -0
- package/src/types/solution.types.ts +30 -0
- package/src/types/synapse.types.ts +49 -0
- package/src/utils/events.ts +45 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/logger.ts +48 -0
- package/src/utils/paths.ts +19 -0
- package/tests/fixtures/code-modules/modules.ts +83 -0
- package/tests/fixtures/errors/go.ts +9 -0
- package/tests/fixtures/errors/node.ts +24 -0
- package/tests/fixtures/errors/python.ts +21 -0
- package/tests/fixtures/errors/rust.ts +25 -0
- package/tests/fixtures/errors/shell.ts +15 -0
- package/tests/fixtures/solutions/solutions.ts +27 -0
- package/tests/helpers/setup-db.ts +52 -0
- package/tests/integration/code-flow.test.ts +86 -0
- package/tests/integration/error-flow.test.ts +83 -0
- package/tests/integration/ipc-flow.test.ts +166 -0
- package/tests/integration/learning-cycle.test.ts +82 -0
- package/tests/integration/synapse-flow.test.ts +117 -0
- package/tests/unit/code/analyzer.test.ts +58 -0
- package/tests/unit/code/fingerprint.test.ts +51 -0
- package/tests/unit/code/scorer.test.ts +55 -0
- package/tests/unit/learning/confidence-scorer.test.ts +60 -0
- package/tests/unit/learning/decay.test.ts +45 -0
- package/tests/unit/learning/pattern-extractor.test.ts +50 -0
- package/tests/unit/matching/error-matcher.test.ts +69 -0
- package/tests/unit/matching/fingerprint.test.ts +47 -0
- package/tests/unit/matching/similarity.test.ts +65 -0
- package/tests/unit/matching/tfidf.test.ts +71 -0
- package/tests/unit/matching/tokenizer.test.ts +83 -0
- package/tests/unit/parsing/parsers.test.ts +113 -0
- package/tests/unit/research/gap-analyzer.test.ts +45 -0
- package/tests/unit/research/trend-analyzer.test.ts +45 -0
- package/tests/unit/synapses/activation.test.ts +80 -0
- package/tests/unit/synapses/decay.test.ts +27 -0
- package/tests/unit/synapses/hebbian.test.ts +96 -0
- package/tests/unit/synapses/pathfinder.test.ts +72 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { createTestDb, type TestDb } from '../helpers/setup-db.js';
|
|
3
|
+
import { SynapseManager } from '../../src/synapses/synapse-manager.js';
|
|
4
|
+
import { SynapseService } from '../../src/services/synapse.service.js';
|
|
5
|
+
|
|
6
|
+
const synapsesConfig = {
|
|
7
|
+
initialWeight: 0.1,
|
|
8
|
+
learningRate: 0.15,
|
|
9
|
+
decayHalfLifeDays: 45,
|
|
10
|
+
pruneThreshold: 0.05,
|
|
11
|
+
decayAfterDays: 14,
|
|
12
|
+
maxDepth: 3,
|
|
13
|
+
minActivationWeight: 0.2,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe('Synapse Flow Integration', () => {
|
|
17
|
+
let testDb: TestDb;
|
|
18
|
+
let synapseManager: SynapseManager;
|
|
19
|
+
let synapseService: SynapseService;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
testDb = createTestDb();
|
|
23
|
+
synapseManager = new SynapseManager(testDb.repos.synapse, synapsesConfig);
|
|
24
|
+
synapseService = new SynapseService(synapseManager);
|
|
25
|
+
|
|
26
|
+
// Create test data
|
|
27
|
+
testDb.repos.project.create({ name: 'test', path: '/test' } as any);
|
|
28
|
+
testDb.repos.error.create({
|
|
29
|
+
project_id: 1, terminal_id: null, fingerprint: 'fp1', type: 'TypeError',
|
|
30
|
+
message: 'error 1', raw_output: 'TypeError: 1', context: null,
|
|
31
|
+
file_path: null, line_number: null, column_number: null,
|
|
32
|
+
} as any);
|
|
33
|
+
testDb.repos.error.create({
|
|
34
|
+
project_id: 1, terminal_id: null, fingerprint: 'fp2', type: 'TypeError',
|
|
35
|
+
message: 'error 2', raw_output: 'TypeError: 2', context: null,
|
|
36
|
+
file_path: null, line_number: null, column_number: null,
|
|
37
|
+
} as any);
|
|
38
|
+
testDb.repos.error.create({
|
|
39
|
+
project_id: 1, terminal_id: null, fingerprint: 'fp3', type: 'TypeError',
|
|
40
|
+
message: 'error 3', raw_output: 'TypeError: 3', context: null,
|
|
41
|
+
file_path: null, line_number: null, column_number: null,
|
|
42
|
+
} as any);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('builds and queries a synapse network', () => {
|
|
46
|
+
synapseManager.strengthen(
|
|
47
|
+
{ type: 'error', id: 1 },
|
|
48
|
+
{ type: 'error', id: 2 },
|
|
49
|
+
'similar_to',
|
|
50
|
+
);
|
|
51
|
+
synapseManager.strengthen(
|
|
52
|
+
{ type: 'error', id: 1 },
|
|
53
|
+
{ type: 'project', id: 1 },
|
|
54
|
+
'co_occurs',
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const stats = synapseService.getNetworkStats();
|
|
58
|
+
expect(stats.totalSynapses).toBe(2);
|
|
59
|
+
|
|
60
|
+
const strongest = synapseService.getStrongestSynapses(10);
|
|
61
|
+
expect(strongest.length).toBe(2);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('strengthening same synapse increases weight', () => {
|
|
65
|
+
const first = synapseManager.strengthen(
|
|
66
|
+
{ type: 'error', id: 1 },
|
|
67
|
+
{ type: 'error', id: 2 },
|
|
68
|
+
'similar_to',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const second = synapseManager.strengthen(
|
|
72
|
+
{ type: 'error', id: 1 },
|
|
73
|
+
{ type: 'error', id: 2 },
|
|
74
|
+
'similar_to',
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(second.weight).toBeGreaterThan(first.weight);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('finds path between connected nodes', () => {
|
|
81
|
+
synapseManager.strengthen(
|
|
82
|
+
{ type: 'error', id: 1 },
|
|
83
|
+
{ type: 'error', id: 2 },
|
|
84
|
+
'similar_to',
|
|
85
|
+
);
|
|
86
|
+
synapseManager.strengthen(
|
|
87
|
+
{ type: 'error', id: 2 },
|
|
88
|
+
{ type: 'error', id: 3 },
|
|
89
|
+
'similar_to',
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const path = synapseService.findPath('error', 1, 'error', 3);
|
|
93
|
+
expect(path).not.toBeNull();
|
|
94
|
+
expect(path!.hops).toBe(2);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('returns null for unconnected nodes', () => {
|
|
98
|
+
const path = synapseService.findPath('error', 1, 'error', 3);
|
|
99
|
+
expect(path).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('decay reduces old synapse weights', () => {
|
|
103
|
+
synapseManager.strengthen(
|
|
104
|
+
{ type: 'error', id: 1 },
|
|
105
|
+
{ type: 'error', id: 2 },
|
|
106
|
+
'similar_to',
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Make synapse old
|
|
110
|
+
testDb.db.prepare(
|
|
111
|
+
`UPDATE synapses SET last_activated_at = datetime('now', '-100 days')`
|
|
112
|
+
).run();
|
|
113
|
+
|
|
114
|
+
const { decayed, pruned } = synapseManager.runDecay();
|
|
115
|
+
expect(decayed + pruned).toBeGreaterThanOrEqual(0);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { analyzeCode, checkPurity, measureCohesion } from '../../../src/code/analyzer.js';
|
|
3
|
+
import { retryModule, loggerModule, hashModule } from '../../fixtures/code-modules/modules.js';
|
|
4
|
+
|
|
5
|
+
describe('analyzeCode', () => {
|
|
6
|
+
it('analyzes TypeScript module', () => {
|
|
7
|
+
const result = analyzeCode(retryModule.source, 'typescript');
|
|
8
|
+
expect(result).toBeTruthy();
|
|
9
|
+
expect(result.exports).toBeDefined();
|
|
10
|
+
expect(result.linesOfCode).toBeGreaterThan(0);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('analyzes Python module', () => {
|
|
14
|
+
const result = analyzeCode(hashModule.source, 'python');
|
|
15
|
+
expect(result).toBeTruthy();
|
|
16
|
+
expect(result.exports.length).toBeGreaterThan(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('detects exports in TypeScript', () => {
|
|
20
|
+
const result = analyzeCode(retryModule.source, 'typescript');
|
|
21
|
+
const exportNames = result.exports.map((e: any) => e.name);
|
|
22
|
+
expect(exportNames).toContain('retry');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('detects external dependencies', () => {
|
|
26
|
+
const result = analyzeCode(loggerModule.source, 'typescript');
|
|
27
|
+
expect(result.externalDeps.length).toBeGreaterThan(0);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('checkPurity', () => {
|
|
32
|
+
it('pure function returns true', () => {
|
|
33
|
+
const pureCode = `export function add(a: number, b: number): number { return a + b; }`;
|
|
34
|
+
expect(checkPurity(pureCode)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('function with side effects returns false', () => {
|
|
38
|
+
const impureCode = `export function save(data: any) { fs.writeFileSync('file', data); console.log('saved'); }`;
|
|
39
|
+
expect(checkPurity(impureCode)).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('measureCohesion', () => {
|
|
44
|
+
it('returns value between 0 and 1', () => {
|
|
45
|
+
const exports = [
|
|
46
|
+
{ name: 'sha256', type: 'function' },
|
|
47
|
+
{ name: 'md5', type: 'function' },
|
|
48
|
+
] as any[];
|
|
49
|
+
const score = measureCohesion(exports);
|
|
50
|
+
expect(score).toBeGreaterThanOrEqual(0);
|
|
51
|
+
expect(score).toBeLessThanOrEqual(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('handles empty exports without crashing', () => {
|
|
55
|
+
const score = measureCohesion([]);
|
|
56
|
+
expect(typeof score).toBe('number');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { fingerprintCode, stripComments } from '../../../src/code/fingerprint.js';
|
|
3
|
+
|
|
4
|
+
describe('fingerprintCode', () => {
|
|
5
|
+
it('produces consistent fingerprint', () => {
|
|
6
|
+
const code = 'function add(a, b) { return a + b; }';
|
|
7
|
+
const fp1 = fingerprintCode(code, 'typescript');
|
|
8
|
+
const fp2 = fingerprintCode(code, 'typescript');
|
|
9
|
+
expect(fp1).toBe(fp2);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('produces different fingerprint for different code', () => {
|
|
13
|
+
const fp1 = fingerprintCode('function add(a, b) { return a + b; }', 'typescript');
|
|
14
|
+
const fp2 = fingerprintCode('function mul(a, b) { return a * b; }', 'typescript');
|
|
15
|
+
expect(fp1).not.toBe(fp2);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('ignores whitespace differences', () => {
|
|
19
|
+
const fp1 = fingerprintCode('function add(a, b) { return a + b; }', 'typescript');
|
|
20
|
+
const fp2 = fingerprintCode('function add(a, b) {\n return a + b;\n}', 'typescript');
|
|
21
|
+
expect(fp1).toBe(fp2);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns hex string', () => {
|
|
25
|
+
const fp = fingerprintCode('const x = 1;', 'typescript');
|
|
26
|
+
expect(fp).toMatch(/^[a-f0-9]+$/);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('stripComments', () => {
|
|
31
|
+
it('removes single-line comments', () => {
|
|
32
|
+
const code = 'const x = 1; // this is a comment\nconst y = 2;';
|
|
33
|
+
const stripped = stripComments(code, 'typescript');
|
|
34
|
+
expect(stripped).not.toContain('this is a comment');
|
|
35
|
+
expect(stripped).toContain('const x = 1');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('removes multi-line comments', () => {
|
|
39
|
+
const code = '/* block comment */\nconst x = 1;';
|
|
40
|
+
const stripped = stripComments(code, 'typescript');
|
|
41
|
+
expect(stripped).not.toContain('block comment');
|
|
42
|
+
expect(stripped).toContain('const x = 1');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('removes Python comments', () => {
|
|
46
|
+
const code = '# python comment\nx = 1';
|
|
47
|
+
const stripped = stripComments(code, 'python');
|
|
48
|
+
expect(stripped).not.toContain('python comment');
|
|
49
|
+
expect(stripped).toContain('x = 1');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { computeReusabilityScore, getSignalBreakdown, MODULE_THRESHOLD } from '../../../src/code/scorer.js';
|
|
3
|
+
import { analyzeCode } from '../../../src/code/analyzer.js';
|
|
4
|
+
import { retryModule } from '../../fixtures/code-modules/modules.js';
|
|
5
|
+
|
|
6
|
+
describe('computeReusabilityScore', () => {
|
|
7
|
+
it('scores a real module', () => {
|
|
8
|
+
const analysis = analyzeCode(retryModule.source, 'typescript');
|
|
9
|
+
const score = computeReusabilityScore({
|
|
10
|
+
source: retryModule.source,
|
|
11
|
+
filePath: retryModule.filePath,
|
|
12
|
+
exports: analysis.exports,
|
|
13
|
+
internalDeps: analysis.internalDeps,
|
|
14
|
+
hasTypeAnnotations: analysis.hasTypeAnnotations,
|
|
15
|
+
});
|
|
16
|
+
expect(score).toBeGreaterThan(0);
|
|
17
|
+
expect(score).toBeLessThanOrEqual(1);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('returns value between 0 and 1', () => {
|
|
21
|
+
const score = computeReusabilityScore({
|
|
22
|
+
source: 'export function add(a: number, b: number) { return a + b; }',
|
|
23
|
+
filePath: 'utils/add.ts',
|
|
24
|
+
exports: [{ name: 'add', type: 'function' }] as any[],
|
|
25
|
+
internalDeps: [],
|
|
26
|
+
hasTypeAnnotations: true,
|
|
27
|
+
});
|
|
28
|
+
expect(score).toBeGreaterThanOrEqual(0);
|
|
29
|
+
expect(score).toBeLessThanOrEqual(1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('getSignalBreakdown', () => {
|
|
34
|
+
it('returns named signals with scores', () => {
|
|
35
|
+
const signals = getSignalBreakdown({
|
|
36
|
+
source: 'export function add(a: number, b: number) { return a + b; }',
|
|
37
|
+
filePath: 'utils/add.ts',
|
|
38
|
+
exports: [{ name: 'add', type: 'function' }] as any[],
|
|
39
|
+
internalDeps: [],
|
|
40
|
+
hasTypeAnnotations: true,
|
|
41
|
+
});
|
|
42
|
+
expect(signals.length).toBeGreaterThan(0);
|
|
43
|
+
for (const signal of signals) {
|
|
44
|
+
expect(signal.name).toBeTruthy();
|
|
45
|
+
expect(typeof signal.score).toBe('number');
|
|
46
|
+
expect(typeof signal.weighted).toBe('number');
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('MODULE_THRESHOLD', () => {
|
|
52
|
+
it('is 0.6', () => {
|
|
53
|
+
expect(MODULE_THRESHOLD).toBe(0.6);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { wilsonScore, timeDecayedConfidence, computeConfidence } from '../../../src/learning/confidence-scorer.js';
|
|
3
|
+
|
|
4
|
+
describe('wilsonScore', () => {
|
|
5
|
+
it('returns 0 for 0 trials', () => {
|
|
6
|
+
expect(wilsonScore(0, 0)).toBe(0);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns low score for single success', () => {
|
|
10
|
+
const score = wilsonScore(1, 1);
|
|
11
|
+
// 1/1 = 100% but Wilson should be conservative
|
|
12
|
+
expect(score).toBeLessThan(0.9);
|
|
13
|
+
expect(score).toBeGreaterThan(0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('increases with more samples', () => {
|
|
17
|
+
const score5 = wilsonScore(5, 5);
|
|
18
|
+
const score50 = wilsonScore(50, 50);
|
|
19
|
+
expect(score50).toBeGreaterThan(score5);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('reflects success rate', () => {
|
|
23
|
+
const high = wilsonScore(9, 10);
|
|
24
|
+
const low = wilsonScore(1, 10);
|
|
25
|
+
expect(high).toBeGreaterThan(low);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns value between 0 and 1', () => {
|
|
29
|
+
const score = wilsonScore(7, 10);
|
|
30
|
+
expect(score).toBeGreaterThanOrEqual(0);
|
|
31
|
+
expect(score).toBeLessThanOrEqual(1);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('timeDecayedConfidence', () => {
|
|
36
|
+
it('recent usage has higher confidence', () => {
|
|
37
|
+
const now = new Date().toISOString();
|
|
38
|
+
const recent = timeDecayedConfidence(8, 10, now, 30);
|
|
39
|
+
|
|
40
|
+
const oldDate = new Date(Date.now() - 90 * 86400000).toISOString();
|
|
41
|
+
const old = timeDecayedConfidence(8, 10, oldDate, 30);
|
|
42
|
+
|
|
43
|
+
expect(recent).toBeGreaterThan(old);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('computeConfidence', () => {
|
|
48
|
+
it('combines success rate and time decay', () => {
|
|
49
|
+
const now = new Date().toISOString();
|
|
50
|
+
const conf = computeConfidence(8, 2, now);
|
|
51
|
+
expect(conf).toBeGreaterThan(0);
|
|
52
|
+
expect(conf).toBeLessThanOrEqual(1);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('zero successes gives low confidence', () => {
|
|
56
|
+
const now = new Date().toISOString();
|
|
57
|
+
const conf = computeConfidence(0, 5, now);
|
|
58
|
+
expect(conf).toBeLessThan(0.3);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { relevanceDecay, shouldPruneRule } from '../../../src/learning/decay.js';
|
|
3
|
+
|
|
4
|
+
describe('relevanceDecay', () => {
|
|
5
|
+
it('returns 1.0 for current timestamp', () => {
|
|
6
|
+
const now = new Date().toISOString();
|
|
7
|
+
expect(relevanceDecay(now, 30)).toBeCloseTo(1.0, 1);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('returns ~0.5 after one half-life', () => {
|
|
11
|
+
const halfLifeDays = 30;
|
|
12
|
+
const past = new Date(Date.now() - halfLifeDays * 86400000).toISOString();
|
|
13
|
+
expect(relevanceDecay(past, halfLifeDays)).toBeCloseTo(0.5, 1);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns near 0 for very old timestamps', () => {
|
|
17
|
+
const veryOld = new Date(Date.now() - 365 * 86400000).toISOString();
|
|
18
|
+
expect(relevanceDecay(veryOld, 30)).toBeLessThan(0.01);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns value between 0 and 1', () => {
|
|
22
|
+
const past = new Date(Date.now() - 10 * 86400000).toISOString();
|
|
23
|
+
const val = relevanceDecay(past, 30);
|
|
24
|
+
expect(val).toBeGreaterThanOrEqual(0);
|
|
25
|
+
expect(val).toBeLessThanOrEqual(1);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('shouldPruneRule', () => {
|
|
30
|
+
it('prunes low confidence rules', () => {
|
|
31
|
+
expect(shouldPruneRule(0.1, 0, 10, 0.2, 0.5)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('keeps high confidence rules', () => {
|
|
35
|
+
expect(shouldPruneRule(0.8, 1, 20, 0.2, 0.5)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('prunes rules with high rejection rate', () => {
|
|
39
|
+
expect(shouldPruneRule(0.5, 8, 10, 0.2, 0.5)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('keeps rules with low rejection rate', () => {
|
|
43
|
+
expect(shouldPruneRule(0.5, 1, 10, 0.2, 0.5)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractPatterns } from '../../../src/learning/pattern-extractor.js';
|
|
3
|
+
import type { ErrorRecord } from '../../../src/types/error.types.js';
|
|
4
|
+
|
|
5
|
+
function makeErrorRecord(overrides: Partial<ErrorRecord> = {}): ErrorRecord {
|
|
6
|
+
return {
|
|
7
|
+
id: 1,
|
|
8
|
+
project_id: 1,
|
|
9
|
+
terminal_id: null,
|
|
10
|
+
type: 'TypeError',
|
|
11
|
+
message: "Cannot read properties of undefined (reading 'map')",
|
|
12
|
+
fingerprint: 'fp1',
|
|
13
|
+
raw_output: '',
|
|
14
|
+
context: null,
|
|
15
|
+
file_path: '/app/main.ts',
|
|
16
|
+
line_number: null,
|
|
17
|
+
column_number: null,
|
|
18
|
+
occurrence_count: 1,
|
|
19
|
+
first_seen: new Date().toISOString(),
|
|
20
|
+
last_seen: new Date().toISOString(),
|
|
21
|
+
resolved: 0,
|
|
22
|
+
resolved_at: null,
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('extractPatterns', () => {
|
|
28
|
+
it('finds pattern from similar errors', () => {
|
|
29
|
+
const errors = [
|
|
30
|
+
makeErrorRecord({ id: 1, message: "Cannot read properties of undefined (reading 'map')", fingerprint: 'fp1' }),
|
|
31
|
+
makeErrorRecord({ id: 2, message: "Cannot read properties of undefined (reading 'filter')", fingerprint: 'fp2' }),
|
|
32
|
+
makeErrorRecord({ id: 3, message: "Cannot read properties of undefined (reading 'forEach')", fingerprint: 'fp3' }),
|
|
33
|
+
makeErrorRecord({ id: 4, message: "Cannot read properties of undefined (reading 'reduce')", fingerprint: 'fp4' }),
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const patterns = extractPatterns(errors);
|
|
37
|
+
expect(patterns.length).toBeGreaterThan(0);
|
|
38
|
+
expect(patterns[0].errorIds.length).toBeGreaterThanOrEqual(2);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns empty for single error', () => {
|
|
42
|
+
const errors = [makeErrorRecord({ id: 1 })];
|
|
43
|
+
const patterns = extractPatterns(errors);
|
|
44
|
+
expect(patterns).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns empty for empty input', () => {
|
|
48
|
+
expect(extractPatterns([])).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { matchError } from '../../../src/matching/error-matcher.js';
|
|
3
|
+
import type { ErrorRecord } from '../../../src/types/error.types.js';
|
|
4
|
+
|
|
5
|
+
function makeError(overrides: Partial<ErrorRecord> = {}): ErrorRecord {
|
|
6
|
+
return {
|
|
7
|
+
id: 1,
|
|
8
|
+
project_id: 1,
|
|
9
|
+
terminal_id: null,
|
|
10
|
+
type: 'TypeError',
|
|
11
|
+
message: "Cannot read properties of undefined (reading 'map')",
|
|
12
|
+
fingerprint: 'abc123',
|
|
13
|
+
raw_output: "TypeError: Cannot read properties of undefined (reading 'map')",
|
|
14
|
+
context: null,
|
|
15
|
+
file_path: '/app/src/main.ts',
|
|
16
|
+
line_number: null,
|
|
17
|
+
column_number: null,
|
|
18
|
+
occurrence_count: 1,
|
|
19
|
+
first_seen: new Date().toISOString(),
|
|
20
|
+
last_seen: new Date().toISOString(),
|
|
21
|
+
resolved: 0,
|
|
22
|
+
resolved_at: null,
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('matchError', () => {
|
|
28
|
+
it('returns matches sorted by score', () => {
|
|
29
|
+
const incoming = makeError({ id: 0 });
|
|
30
|
+
const candidates = [
|
|
31
|
+
makeError({ id: 1, fingerprint: 'abc123', type: 'TypeError' }),
|
|
32
|
+
makeError({ id: 2, fingerprint: 'different', type: 'RangeError', message: 'Maximum call stack size exceeded' }),
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const results = matchError(incoming, candidates);
|
|
36
|
+
expect(results.length).toBeGreaterThan(0);
|
|
37
|
+
// Identical fingerprint should score highest
|
|
38
|
+
expect(results[0].errorId).toBe(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns empty for no candidates', () => {
|
|
42
|
+
const incoming = makeError({ id: 0 });
|
|
43
|
+
const results = matchError(incoming, []);
|
|
44
|
+
expect(results).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('fingerprint match boosts score', () => {
|
|
48
|
+
const incoming = makeError({ id: 0, fingerprint: 'same_fp' });
|
|
49
|
+
const exactFp = makeError({ id: 1, fingerprint: 'same_fp' });
|
|
50
|
+
const diffFp = makeError({ id: 2, fingerprint: 'other_fp', type: 'RangeError', message: 'completely different' });
|
|
51
|
+
|
|
52
|
+
const results = matchError(incoming, [exactFp, diffFp]);
|
|
53
|
+
if (results.length >= 2) {
|
|
54
|
+
expect(results[0].errorId).toBe(1);
|
|
55
|
+
expect(results[0].score).toBeGreaterThan(results[1].score);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('results have score and signals', () => {
|
|
60
|
+
const incoming = makeError({ id: 0 });
|
|
61
|
+
const candidate = makeError({ id: 1 });
|
|
62
|
+
const results = matchError(incoming, [candidate]);
|
|
63
|
+
if (results.length > 0) {
|
|
64
|
+
expect(typeof results[0].score).toBe('number');
|
|
65
|
+
expect(results[0].signals.length).toBeGreaterThan(0);
|
|
66
|
+
expect(typeof results[0].isStrong).toBe('boolean');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { templateMessage, generateFingerprint } from '../../../src/matching/fingerprint.js';
|
|
3
|
+
|
|
4
|
+
describe('templateMessage', () => {
|
|
5
|
+
it('replaces file paths with placeholder', () => {
|
|
6
|
+
const result = templateMessage('Error in /app/src/main.ts:42:5');
|
|
7
|
+
expect(result).not.toContain('/app/src/main.ts');
|
|
8
|
+
expect(result).toContain('<PATH>');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('replaces hex addresses with <ADDR>', () => {
|
|
12
|
+
const result = templateMessage('at 0x7fff5fbff8c0');
|
|
13
|
+
expect(result).toContain('<ADDR>');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('produces consistent output for similar messages', () => {
|
|
17
|
+
const a = templateMessage("Cannot read property 'name' of undefined at /foo/bar.js:10:5");
|
|
18
|
+
const b = templateMessage("Cannot read property 'name' of undefined at /baz/qux.js:20:3");
|
|
19
|
+
expect(a).toBe(b);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('replaces URLs', () => {
|
|
23
|
+
// Unix path regex runs before URL regex, so URL domain/path part gets <PATH>
|
|
24
|
+
// Test that the URL string is at least transformed (not left as-is)
|
|
25
|
+
const result = templateMessage('Failed to fetch https://api.example.com/data');
|
|
26
|
+
expect(result).not.toContain('https://api.example.com/data');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('generateFingerprint', () => {
|
|
31
|
+
it('produces consistent hash for same error', () => {
|
|
32
|
+
const fp1 = generateFingerprint('TypeError', 'Cannot read undefined', []);
|
|
33
|
+
const fp2 = generateFingerprint('TypeError', 'Cannot read undefined', []);
|
|
34
|
+
expect(fp1).toBe(fp2);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('produces different hash for different error types', () => {
|
|
38
|
+
const fp1 = generateFingerprint('TypeError', 'msg', []);
|
|
39
|
+
const fp2 = generateFingerprint('RangeError', 'msg', []);
|
|
40
|
+
expect(fp1).not.toBe(fp2);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns a hex string', () => {
|
|
44
|
+
const fp = generateFingerprint('Error', 'test', []);
|
|
45
|
+
expect(fp).toMatch(/^[a-f0-9]+$/);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { levenshteinDistance, cosineSimilarity, jaccardSimilarity } from '../../../src/matching/similarity.js';
|
|
3
|
+
|
|
4
|
+
describe('levenshteinDistance (normalized similarity)', () => {
|
|
5
|
+
it('returns 1 for identical strings', () => {
|
|
6
|
+
expect(levenshteinDistance('hello', 'hello')).toBeCloseTo(1.0);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns 0 for completely different strings', () => {
|
|
10
|
+
expect(levenshteinDistance('', 'abc')).toBeCloseTo(0.0);
|
|
11
|
+
expect(levenshteinDistance('abc', '')).toBeCloseTo(0.0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns high similarity for single edit', () => {
|
|
15
|
+
// cat vs bat: 1 edit / 3 length = 0.33 distance → 0.67 similarity
|
|
16
|
+
expect(levenshteinDistance('cat', 'bat')).toBeCloseTo(1 - 1 / 3, 1);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns value between 0 and 1', () => {
|
|
20
|
+
const sim = levenshteinDistance('kitten', 'sitting');
|
|
21
|
+
expect(sim).toBeGreaterThanOrEqual(0);
|
|
22
|
+
expect(sim).toBeLessThanOrEqual(1);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('cosineSimilarity', () => {
|
|
27
|
+
it('returns 1 for identical token arrays', () => {
|
|
28
|
+
expect(cosineSimilarity(['a', 'b', 'c'], ['a', 'b', 'c'])).toBeCloseTo(1.0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns 0 for disjoint token arrays', () => {
|
|
32
|
+
expect(cosineSimilarity(['a', 'b'], ['c', 'd'])).toBeCloseTo(0.0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns value between 0 and 1 for partial overlap', () => {
|
|
36
|
+
const sim = cosineSimilarity(['a', 'b', 'c'], ['b', 'c', 'd']);
|
|
37
|
+
expect(sim).toBeGreaterThan(0);
|
|
38
|
+
expect(sim).toBeLessThan(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('handles empty arrays', () => {
|
|
42
|
+
expect(cosineSimilarity([], [])).toBe(0);
|
|
43
|
+
expect(cosineSimilarity(['a'], [])).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('jaccardSimilarity', () => {
|
|
48
|
+
it('returns 1 for identical sets', () => {
|
|
49
|
+
expect(jaccardSimilarity(['a', 'b'], ['a', 'b'])).toBeCloseTo(1.0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns 0 for disjoint sets', () => {
|
|
53
|
+
expect(jaccardSimilarity(['a', 'b'], ['c', 'd'])).toBeCloseTo(0.0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('computes correct ratio for partial overlap', () => {
|
|
57
|
+
// intersection {b} = 1, union {a,b,c} = 3
|
|
58
|
+
const sim = jaccardSimilarity(['a', 'b'], ['b', 'c']);
|
|
59
|
+
expect(sim).toBeCloseTo(1 / 3);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('handles empty arrays', () => {
|
|
63
|
+
expect(jaccardSimilarity([], [])).toBe(0);
|
|
64
|
+
});
|
|
65
|
+
});
|