@timmeck/brain 1.9.0 → 2.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/README.md +19 -0
- package/brain.log +1164 -0
- package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
- package/dist/api/server.d.ts +4 -18
- package/dist/api/server.js +4 -173
- package/dist/api/server.js.map +1 -1
- package/dist/brain.d.ts +1 -0
- package/dist/brain.js +6 -1
- package/dist/brain.js.map +1 -1
- package/dist/cli/colors.d.ts +4 -25
- package/dist/cli/colors.js +3 -89
- package/dist/cli/colors.js.map +1 -1
- package/dist/cli/commands/peers.d.ts +2 -0
- package/dist/cli/commands/peers.js +38 -0
- package/dist/cli/commands/peers.js.map +1 -0
- package/dist/db/connection.d.ts +1 -2
- package/dist/db/connection.js +1 -18
- package/dist/db/connection.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
- package/dist/ipc/__tests__/protocol.test.js +117 -0
- package/dist/ipc/__tests__/protocol.test.js.map +1 -0
- package/dist/ipc/client.d.ts +1 -16
- package/dist/ipc/client.js +1 -100
- package/dist/ipc/client.js.map +1 -1
- package/dist/ipc/protocol.d.ts +1 -8
- package/dist/ipc/protocol.js +1 -28
- package/dist/ipc/protocol.js.map +1 -1
- package/dist/ipc/router.js +8 -0
- package/dist/ipc/router.js.map +1 -1
- package/dist/ipc/server.d.ts +1 -22
- package/dist/ipc/server.js +1 -163
- package/dist/ipc/server.js.map +1 -1
- package/dist/mcp/http-server.d.ts +1 -7
- package/dist/mcp/http-server.js +6 -117
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/mcp/server.js +5 -61
- package/dist/mcp/server.js.map +1 -1
- package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
- package/dist/signals/__tests__/fingerprint.test.js +118 -0
- package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
- package/dist/types/ipc.types.d.ts +1 -11
- package/dist/utils/__tests__/hash.test.d.ts +1 -0
- package/dist/utils/__tests__/hash.test.js +32 -0
- package/dist/utils/__tests__/hash.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.d.ts +1 -0
- package/dist/utils/__tests__/paths.test.js +75 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -0
- package/dist/utils/events.d.ts +4 -8
- package/dist/utils/events.js +2 -14
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/hash.d.ts +1 -1
- package/dist/utils/hash.js +1 -4
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -2
- package/dist/utils/logger.js +8 -35
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.js +4 -13
- package/dist/utils/paths.js.map +1 -1
- package/package.json +2 -1
- package/BRAIN_PLAN.md +0 -3324
- package/reddit_post.md +0 -45
- package/src/api/server.ts +0 -395
- package/src/brain.ts +0 -313
- package/src/cli/colors.ts +0 -116
- package/src/cli/commands/config.ts +0 -169
- package/src/cli/commands/doctor.ts +0 -124
- package/src/cli/commands/explain.ts +0 -83
- package/src/cli/commands/export.ts +0 -31
- package/src/cli/commands/import.ts +0 -199
- package/src/cli/commands/insights.ts +0 -65
- package/src/cli/commands/learn.ts +0 -24
- package/src/cli/commands/modules.ts +0 -53
- package/src/cli/commands/network.ts +0 -67
- package/src/cli/commands/projects.ts +0 -42
- package/src/cli/commands/query.ts +0 -120
- package/src/cli/commands/start.ts +0 -105
- package/src/cli/commands/status.ts +0 -75
- package/src/cli/commands/stop.ts +0 -34
- package/src/cli/ipc-helper.ts +0 -22
- package/src/cli/update-check.ts +0 -63
- package/src/code/analyzer.ts +0 -117
- package/src/code/fingerprint.ts +0 -87
- package/src/code/matcher.ts +0 -129
- package/src/code/parsers/generic.ts +0 -29
- package/src/code/parsers/python.ts +0 -54
- package/src/code/parsers/typescript.ts +0 -65
- package/src/code/registry.ts +0 -60
- package/src/code/scorer.ts +0 -120
- package/src/config.ts +0 -135
- package/src/dashboard/server.ts +0 -142
- package/src/db/connection.ts +0 -22
- package/src/db/migrations/001_core_schema.ts +0 -120
- package/src/db/migrations/002_learning_schema.ts +0 -38
- package/src/db/migrations/003_code_schema.ts +0 -53
- package/src/db/migrations/004_synapses_schema.ts +0 -57
- package/src/db/migrations/005_fts_indexes.ts +0 -78
- package/src/db/migrations/006_synapses_phase3.ts +0 -17
- package/src/db/migrations/007_feedback.ts +0 -13
- package/src/db/migrations/008_git_integration.ts +0 -38
- package/src/db/migrations/009_embeddings.ts +0 -8
- package/src/db/migrations/index.ts +0 -70
- package/src/db/repositories/antipattern.repository.ts +0 -66
- package/src/db/repositories/code-module.repository.ts +0 -142
- package/src/db/repositories/error.repository.ts +0 -189
- package/src/db/repositories/insight.repository.ts +0 -99
- package/src/db/repositories/notification.repository.ts +0 -66
- package/src/db/repositories/project.repository.ts +0 -93
- package/src/db/repositories/rule.repository.ts +0 -108
- package/src/db/repositories/solution.repository.ts +0 -154
- package/src/db/repositories/synapse.repository.ts +0 -163
- package/src/db/repositories/terminal.repository.ts +0 -101
- package/src/embeddings/engine.ts +0 -238
- package/src/hooks/post-tool-use.ts +0 -92
- package/src/hooks/post-write.ts +0 -129
- package/src/index.ts +0 -63
- package/src/ipc/client.ts +0 -118
- package/src/ipc/protocol.ts +0 -35
- package/src/ipc/router.ts +0 -133
- package/src/ipc/server.ts +0 -176
- package/src/learning/confidence-scorer.ts +0 -80
- package/src/learning/decay.ts +0 -46
- package/src/learning/learning-engine.ts +0 -170
- package/src/learning/pattern-extractor.ts +0 -90
- package/src/learning/rule-generator.ts +0 -74
- package/src/main.rs:10:5 +0 -0
- package/src/matching/error-matcher.ts +0 -166
- package/src/matching/fingerprint.ts +0 -34
- package/src/matching/similarity.ts +0 -61
- package/src/matching/tfidf.ts +0 -74
- package/src/matching/tokenizer.ts +0 -41
- package/src/mcp/auto-detect.ts +0 -93
- package/src/mcp/http-server.ts +0 -140
- package/src/mcp/server.ts +0 -73
- package/src/mcp/tools.ts +0 -328
- package/src/parsing/error-parser.ts +0 -28
- package/src/parsing/parsers/compiler.ts +0 -93
- package/src/parsing/parsers/generic.ts +0 -28
- package/src/parsing/parsers/go.ts +0 -97
- package/src/parsing/parsers/node.ts +0 -69
- package/src/parsing/parsers/python.ts +0 -62
- package/src/parsing/parsers/rust.ts +0 -50
- package/src/parsing/parsers/shell.ts +0 -42
- package/src/parsing/types.ts +0 -47
- package/src/research/gap-analyzer.ts +0 -135
- package/src/research/insight-generator.ts +0 -123
- package/src/research/research-engine.ts +0 -116
- package/src/research/synergy-detector.ts +0 -126
- package/src/research/template-extractor.ts +0 -130
- package/src/research/trend-analyzer.ts +0 -127
- package/src/services/analytics.service.ts +0 -226
- package/src/services/code.service.ts +0 -271
- package/src/services/error.service.ts +0 -266
- package/src/services/git.service.ts +0 -132
- package/src/services/notification.service.ts +0 -41
- package/src/services/prevention.service.ts +0 -159
- package/src/services/research.service.ts +0 -98
- package/src/services/solution.service.ts +0 -174
- package/src/services/synapse.service.ts +0 -59
- package/src/services/terminal.service.ts +0 -81
- package/src/synapses/activation.ts +0 -80
- package/src/synapses/decay.ts +0 -38
- package/src/synapses/hebbian.ts +0 -69
- package/src/synapses/pathfinder.ts +0 -81
- package/src/synapses/synapse-manager.ts +0 -113
- package/src/types/code.types.ts +0 -52
- package/src/types/config.types.ts +0 -103
- package/src/types/error.types.ts +0 -67
- package/src/types/ipc.types.ts +0 -8
- package/src/types/mcp.types.ts +0 -53
- package/src/types/research.types.ts +0 -28
- package/src/types/solution.types.ts +0 -30
- package/src/types/synapse.types.ts +0 -50
- package/src/utils/events.ts +0 -45
- package/src/utils/hash.ts +0 -5
- package/src/utils/logger.ts +0 -48
- package/src/utils/paths.ts +0 -19
- package/tests/e2e/test_code_intelligence.py +0 -1015
- package/tests/e2e/test_error_memory.py +0 -451
- package/tests/e2e/test_full_integration.py +0 -534
- package/tests/fixtures/code-modules/modules.ts +0 -83
- package/tests/fixtures/errors/go.ts +0 -9
- package/tests/fixtures/errors/node.ts +0 -24
- package/tests/fixtures/errors/python.ts +0 -21
- package/tests/fixtures/errors/rust.ts +0 -25
- package/tests/fixtures/errors/shell.ts +0 -15
- package/tests/fixtures/solutions/solutions.ts +0 -27
- package/tests/helpers/setup-db.ts +0 -52
- package/tests/integration/code-flow.test.ts +0 -86
- package/tests/integration/error-flow.test.ts +0 -83
- package/tests/integration/ipc-flow.test.ts +0 -166
- package/tests/integration/learning-cycle.test.ts +0 -82
- package/tests/integration/synapse-flow.test.ts +0 -117
- package/tests/unit/code/analyzer.test.ts +0 -58
- package/tests/unit/code/fingerprint.test.ts +0 -51
- package/tests/unit/code/scorer.test.ts +0 -55
- package/tests/unit/learning/confidence-scorer.test.ts +0 -60
- package/tests/unit/learning/decay.test.ts +0 -45
- package/tests/unit/learning/pattern-extractor.test.ts +0 -50
- package/tests/unit/matching/error-matcher.test.ts +0 -69
- package/tests/unit/matching/fingerprint.test.ts +0 -47
- package/tests/unit/matching/similarity.test.ts +0 -65
- package/tests/unit/matching/tfidf.test.ts +0 -71
- package/tests/unit/matching/tokenizer.test.ts +0 -83
- package/tests/unit/parsing/parsers.test.ts +0 -113
- package/tests/unit/research/gap-analyzer.test.ts +0 -45
- package/tests/unit/research/trend-analyzer.test.ts +0 -45
- package/tests/unit/synapses/activation.test.ts +0 -80
- package/tests/unit/synapses/decay.test.ts +0 -27
- package/tests/unit/synapses/hebbian.test.ts +0 -96
- package/tests/unit/synapses/pathfinder.test.ts +0 -72
- package/tsconfig.json +0 -18
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { NotificationRepository, NotificationRecord } from '../db/repositories/notification.repository.js';
|
|
2
|
-
import { getLogger } from '../utils/logger.js';
|
|
3
|
-
|
|
4
|
-
export interface CreateNotificationInput {
|
|
5
|
-
type: string;
|
|
6
|
-
title: string;
|
|
7
|
-
message: string;
|
|
8
|
-
priority?: number;
|
|
9
|
-
projectId?: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class NotificationService {
|
|
13
|
-
private logger = getLogger();
|
|
14
|
-
|
|
15
|
-
constructor(private notificationRepo: NotificationRepository) {}
|
|
16
|
-
|
|
17
|
-
create(input: CreateNotificationInput): number {
|
|
18
|
-
const id = this.notificationRepo.create({
|
|
19
|
-
type: input.type,
|
|
20
|
-
title: input.title,
|
|
21
|
-
message: input.message,
|
|
22
|
-
priority: input.priority ?? 0,
|
|
23
|
-
project_id: input.projectId ?? null,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
this.logger.info(`Notification created (id=${id}, type=${input.type})`);
|
|
27
|
-
return id;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
list(projectId?: number): NotificationRecord[] {
|
|
31
|
-
return this.notificationRepo.findUnacknowledged(projectId);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
acknowledge(id: number): void {
|
|
35
|
-
this.notificationRepo.acknowledge(id);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
getById(id: number): NotificationRecord | undefined {
|
|
39
|
-
return this.notificationRepo.getById(id);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import type { RuleRepository } from '../db/repositories/rule.repository.js';
|
|
2
|
-
import type { AntipatternRepository } from '../db/repositories/antipattern.repository.js';
|
|
3
|
-
import type { SynapseManager } from '../synapses/synapse-manager.js';
|
|
4
|
-
import { getLogger } from '../utils/logger.js';
|
|
5
|
-
|
|
6
|
-
export interface RuleCheckResult {
|
|
7
|
-
matched: boolean;
|
|
8
|
-
ruleId: number;
|
|
9
|
-
action: string;
|
|
10
|
-
description: string | null;
|
|
11
|
-
confidence: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface AntipatternCheckResult {
|
|
15
|
-
matched: boolean;
|
|
16
|
-
antipatternId: number;
|
|
17
|
-
pattern: string;
|
|
18
|
-
description: string;
|
|
19
|
-
severity: string;
|
|
20
|
-
suggestion: string | null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class PreventionService {
|
|
24
|
-
private logger = getLogger();
|
|
25
|
-
|
|
26
|
-
constructor(
|
|
27
|
-
private ruleRepo: RuleRepository,
|
|
28
|
-
private antipatternRepo: AntipatternRepository,
|
|
29
|
-
private synapseManager: SynapseManager,
|
|
30
|
-
) {}
|
|
31
|
-
|
|
32
|
-
checkRules(errorType: string, message: string, projectId?: number): RuleCheckResult[] {
|
|
33
|
-
const rules = this.ruleRepo.findActive(projectId);
|
|
34
|
-
const results: RuleCheckResult[] = [];
|
|
35
|
-
|
|
36
|
-
for (const rule of rules) {
|
|
37
|
-
try {
|
|
38
|
-
const pattern = new RegExp(rule.pattern, 'i');
|
|
39
|
-
const input = `${errorType}: ${message}`;
|
|
40
|
-
|
|
41
|
-
if (pattern.test(input)) {
|
|
42
|
-
results.push({
|
|
43
|
-
matched: true,
|
|
44
|
-
ruleId: rule.id,
|
|
45
|
-
action: rule.action,
|
|
46
|
-
description: rule.description,
|
|
47
|
-
confidence: rule.confidence,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
this.logger.debug(`Rule ${rule.id} matched: ${rule.pattern}`);
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
// Invalid regex in rule pattern, skip
|
|
54
|
-
this.logger.warn(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return results.sort((a, b) => b.confidence - a.confidence);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
checkAntipatterns(errorType: string, message: string, projectId?: number): AntipatternCheckResult[] {
|
|
62
|
-
const antipatterns = projectId
|
|
63
|
-
? [...this.antipatternRepo.findByProject(projectId), ...this.antipatternRepo.findGlobal()]
|
|
64
|
-
: this.antipatternRepo.findGlobal();
|
|
65
|
-
|
|
66
|
-
const results: AntipatternCheckResult[] = [];
|
|
67
|
-
const input = `${errorType}: ${message}`;
|
|
68
|
-
|
|
69
|
-
for (const ap of antipatterns) {
|
|
70
|
-
try {
|
|
71
|
-
const pattern = new RegExp(ap.pattern, 'i');
|
|
72
|
-
if (pattern.test(input)) {
|
|
73
|
-
results.push({
|
|
74
|
-
matched: true,
|
|
75
|
-
antipatternId: ap.id,
|
|
76
|
-
pattern: ap.pattern,
|
|
77
|
-
description: ap.description,
|
|
78
|
-
severity: ap.severity,
|
|
79
|
-
suggestion: ap.suggestion,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
} catch {
|
|
83
|
-
this.logger.warn(`Invalid regex in antipattern ${ap.id}: ${ap.pattern}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return results;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
createRule(data: {
|
|
91
|
-
pattern: string;
|
|
92
|
-
action: string;
|
|
93
|
-
description?: string;
|
|
94
|
-
confidence?: number;
|
|
95
|
-
projectId?: number;
|
|
96
|
-
}): number {
|
|
97
|
-
return this.ruleRepo.create({
|
|
98
|
-
pattern: data.pattern,
|
|
99
|
-
action: data.action,
|
|
100
|
-
description: data.description ?? null,
|
|
101
|
-
confidence: data.confidence ?? 0.5,
|
|
102
|
-
occurrences: 0,
|
|
103
|
-
active: 1,
|
|
104
|
-
project_id: data.projectId ?? null,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
checkCodeForPatterns(source: string, filePath?: string): { warnings: Array<{ message: string; severity: string; ruleId?: number }> } {
|
|
109
|
-
const warnings: Array<{ message: string; severity: string; ruleId?: number }> = [];
|
|
110
|
-
|
|
111
|
-
// Check antipatterns against the code itself
|
|
112
|
-
const globalAntipatterns = this.antipatternRepo.findGlobal();
|
|
113
|
-
for (const ap of globalAntipatterns) {
|
|
114
|
-
try {
|
|
115
|
-
const pattern = new RegExp(ap.pattern, 'i');
|
|
116
|
-
if (pattern.test(source)) {
|
|
117
|
-
warnings.push({
|
|
118
|
-
message: `Code matches known error pattern: ${ap.description}${ap.suggestion ? `. Suggestion: ${ap.suggestion}` : ''}`,
|
|
119
|
-
severity: ap.severity,
|
|
120
|
-
ruleId: undefined,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
} catch {
|
|
124
|
-
// Invalid regex, skip
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Check active rules
|
|
129
|
-
const rules = this.ruleRepo.findActive();
|
|
130
|
-
for (const rule of rules) {
|
|
131
|
-
try {
|
|
132
|
-
const pattern = new RegExp(rule.pattern, 'i');
|
|
133
|
-
if (pattern.test(source)) {
|
|
134
|
-
warnings.push({
|
|
135
|
-
message: `Code matches learned rule: ${rule.description ?? rule.pattern}. Action: ${rule.action}`,
|
|
136
|
-
severity: 'warning',
|
|
137
|
-
ruleId: rule.id,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
} catch {
|
|
141
|
-
// Invalid regex, skip
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return { warnings: warnings.slice(0, 5) };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
reportPrevention(ruleId: number, errorId: number): void {
|
|
149
|
-
const rule = this.ruleRepo.getById(ruleId);
|
|
150
|
-
if (rule) {
|
|
151
|
-
this.ruleRepo.update(ruleId, { occurrences: rule.occurrences + 1 });
|
|
152
|
-
this.synapseManager.strengthen(
|
|
153
|
-
{ type: 'rule', id: ruleId },
|
|
154
|
-
{ type: 'error', id: errorId },
|
|
155
|
-
'prevents',
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { InsightRecord } from '../types/research.types.js';
|
|
2
|
-
import type { InsightRepository } from '../db/repositories/insight.repository.js';
|
|
3
|
-
import type { ErrorRepository } from '../db/repositories/error.repository.js';
|
|
4
|
-
import type { SynapseManager } from '../synapses/synapse-manager.js';
|
|
5
|
-
import { getLogger } from '../utils/logger.js';
|
|
6
|
-
|
|
7
|
-
export interface InsightQuery {
|
|
8
|
-
projectId?: number;
|
|
9
|
-
type?: string;
|
|
10
|
-
activeOnly?: boolean;
|
|
11
|
-
limit?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface TrendResult {
|
|
15
|
-
errorType: string;
|
|
16
|
-
count: number;
|
|
17
|
-
direction: 'increasing' | 'decreasing' | 'stable';
|
|
18
|
-
period: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class ResearchService {
|
|
22
|
-
private logger = getLogger();
|
|
23
|
-
|
|
24
|
-
constructor(
|
|
25
|
-
private insightRepo: InsightRepository,
|
|
26
|
-
private errorRepo: ErrorRepository,
|
|
27
|
-
private synapseManager: SynapseManager,
|
|
28
|
-
) {}
|
|
29
|
-
|
|
30
|
-
getInsights(query: InsightQuery): InsightRecord[] {
|
|
31
|
-
if (query.type) {
|
|
32
|
-
return this.insightRepo.findByType(query.type);
|
|
33
|
-
}
|
|
34
|
-
if (query.activeOnly !== false) {
|
|
35
|
-
return this.insightRepo.findActive(query.projectId);
|
|
36
|
-
}
|
|
37
|
-
return this.insightRepo.findActive(query.projectId);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
createInsight(data: {
|
|
41
|
-
type: string;
|
|
42
|
-
title: string;
|
|
43
|
-
description: string;
|
|
44
|
-
evidence?: string;
|
|
45
|
-
priority?: number;
|
|
46
|
-
projectId?: number;
|
|
47
|
-
expiresInDays?: number;
|
|
48
|
-
}): number {
|
|
49
|
-
const expiresAt = data.expiresInDays
|
|
50
|
-
? new Date(Date.now() + data.expiresInDays * 86400000).toISOString()
|
|
51
|
-
: null;
|
|
52
|
-
|
|
53
|
-
const id = this.insightRepo.create({
|
|
54
|
-
type: data.type as InsightRecord['type'],
|
|
55
|
-
title: data.title,
|
|
56
|
-
description: data.description,
|
|
57
|
-
evidence: data.evidence ?? '[]',
|
|
58
|
-
priority: data.priority ?? 0,
|
|
59
|
-
project_id: data.projectId ?? null,
|
|
60
|
-
active: 1,
|
|
61
|
-
expires_at: expiresAt,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
this.logger.info(`Insight created (id=${id}, type=${data.type})`);
|
|
65
|
-
return id;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
getTrends(projectId?: number, windowDays: number = 7): TrendResult[] {
|
|
69
|
-
const now = new Date();
|
|
70
|
-
const currentStart = new Date(now.getTime() - windowDays * 86400000).toISOString();
|
|
71
|
-
const previousStart = new Date(now.getTime() - windowDays * 2 * 86400000).toISOString();
|
|
72
|
-
|
|
73
|
-
const currentCount = this.errorRepo.countSince(currentStart, projectId);
|
|
74
|
-
const previousCount = this.errorRepo.countSince(previousStart, projectId) - currentCount;
|
|
75
|
-
|
|
76
|
-
const direction = currentCount > previousCount * 1.2
|
|
77
|
-
? 'increasing' as const
|
|
78
|
-
: currentCount < previousCount * 0.8
|
|
79
|
-
? 'decreasing' as const
|
|
80
|
-
: 'stable' as const;
|
|
81
|
-
|
|
82
|
-
return [{
|
|
83
|
-
errorType: 'all',
|
|
84
|
-
count: currentCount,
|
|
85
|
-
direction,
|
|
86
|
-
period: `${windowDays}d`,
|
|
87
|
-
}];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
expireOldInsights(): number {
|
|
91
|
-
return this.insightRepo.expire();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
rateInsight(id: number, rating: number, comment?: string): boolean {
|
|
95
|
-
const clamped = Math.max(-1, Math.min(1, rating)); // -1 (bad), 0 (neutral), 1 (useful)
|
|
96
|
-
return this.insightRepo.rate(id, clamped, comment);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import type { SolutionRecord } from '../types/solution.types.js';
|
|
2
|
-
import type { SolutionRepository } from '../db/repositories/solution.repository.js';
|
|
3
|
-
import type { SynapseManager } from '../synapses/synapse-manager.js';
|
|
4
|
-
import { getEventBus } from '../utils/events.js';
|
|
5
|
-
import { getLogger } from '../utils/logger.js';
|
|
6
|
-
|
|
7
|
-
export interface ReportSolutionInput {
|
|
8
|
-
errorId: number;
|
|
9
|
-
description: string;
|
|
10
|
-
commands?: string;
|
|
11
|
-
codeChange?: string;
|
|
12
|
-
source?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface RateOutcomeInput {
|
|
16
|
-
errorId: number;
|
|
17
|
-
solutionId: number;
|
|
18
|
-
success: boolean;
|
|
19
|
-
terminalId?: number;
|
|
20
|
-
output?: string;
|
|
21
|
-
durationMs?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class SolutionService {
|
|
25
|
-
private logger = getLogger();
|
|
26
|
-
private eventBus = getEventBus();
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
29
|
-
private solutionRepo: SolutionRepository,
|
|
30
|
-
private synapseManager: SynapseManager,
|
|
31
|
-
) {}
|
|
32
|
-
|
|
33
|
-
report(input: ReportSolutionInput): number {
|
|
34
|
-
const solutionId = this.solutionRepo.create({
|
|
35
|
-
description: input.description,
|
|
36
|
-
commands: input.commands ?? null,
|
|
37
|
-
code_change: input.codeChange ?? null,
|
|
38
|
-
source: input.source ?? 'manual',
|
|
39
|
-
confidence: 0.5,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
this.solutionRepo.linkToError(input.errorId, solutionId);
|
|
43
|
-
|
|
44
|
-
// Create synapse: solution solves error
|
|
45
|
-
this.synapseManager.strengthen(
|
|
46
|
-
{ type: 'solution', id: solutionId },
|
|
47
|
-
{ type: 'error', id: input.errorId },
|
|
48
|
-
'solves',
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
this.eventBus.emit('solution:created', { solutionId });
|
|
52
|
-
this.logger.info(`Solution reported (id=${solutionId}) for error ${input.errorId}`);
|
|
53
|
-
|
|
54
|
-
return solutionId;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
rateOutcome(input: RateOutcomeInput): void {
|
|
58
|
-
// Record the attempt
|
|
59
|
-
const errorSolutions = this.solutionRepo.findForError(input.errorId);
|
|
60
|
-
const link = errorSolutions.find(s => s.id === input.solutionId);
|
|
61
|
-
if (!link) {
|
|
62
|
-
this.solutionRepo.linkToError(input.errorId, input.solutionId);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Find the error_solution link id via DB
|
|
66
|
-
this.solutionRepo.recordAttempt({
|
|
67
|
-
errorSolutionId: input.errorId, // will be resolved via link
|
|
68
|
-
terminalId: input.terminalId,
|
|
69
|
-
success: input.success ? 1 : 0,
|
|
70
|
-
output: input.output,
|
|
71
|
-
durationMs: input.durationMs,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Update synapse
|
|
75
|
-
if (input.success) {
|
|
76
|
-
this.synapseManager.strengthen(
|
|
77
|
-
{ type: 'solution', id: input.solutionId },
|
|
78
|
-
{ type: 'error', id: input.errorId },
|
|
79
|
-
'solves',
|
|
80
|
-
{ outcome: 'success' },
|
|
81
|
-
);
|
|
82
|
-
// Update solution confidence based on success rate
|
|
83
|
-
const rate = this.solutionRepo.successRate(input.solutionId);
|
|
84
|
-
this.solutionRepo.update(input.solutionId, { confidence: rate });
|
|
85
|
-
} else {
|
|
86
|
-
const synapse = this.synapseManager.find(
|
|
87
|
-
{ type: 'solution', id: input.solutionId },
|
|
88
|
-
{ type: 'error', id: input.errorId },
|
|
89
|
-
'solves',
|
|
90
|
-
);
|
|
91
|
-
if (synapse) {
|
|
92
|
-
this.synapseManager.weaken(synapse.id, 0.7);
|
|
93
|
-
}
|
|
94
|
-
const rate = this.solutionRepo.successRate(input.solutionId);
|
|
95
|
-
this.solutionRepo.update(input.solutionId, { confidence: rate });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
this.eventBus.emit('solution:applied', {
|
|
99
|
-
errorId: input.errorId,
|
|
100
|
-
solutionId: input.solutionId,
|
|
101
|
-
success: input.success,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
findForError(errorId: number): SolutionRecord[] {
|
|
106
|
-
return this.solutionRepo.findForError(errorId);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getById(id: number): SolutionRecord | undefined {
|
|
110
|
-
return this.solutionRepo.getById(id);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
successRate(solutionId: number): number {
|
|
114
|
-
return this.solutionRepo.successRate(solutionId);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
analyzeEfficiency(): {
|
|
118
|
-
avgDurationMs: number;
|
|
119
|
-
slowSolutions: Array<{ solutionId: number; avgDuration: number; description: string }>;
|
|
120
|
-
successRateOverall: number;
|
|
121
|
-
totalAttempts: number;
|
|
122
|
-
} {
|
|
123
|
-
const allSolutions = this.solutionRepo.getAll();
|
|
124
|
-
let totalDuration = 0;
|
|
125
|
-
let totalAttempts = 0;
|
|
126
|
-
let totalSuccessRate = 0;
|
|
127
|
-
let solutionCount = 0;
|
|
128
|
-
const solutionDurations: Array<{ solutionId: number; avgDuration: number; description: string }> = [];
|
|
129
|
-
|
|
130
|
-
for (const solution of allSolutions) {
|
|
131
|
-
const rate = this.solutionRepo.successRate(solution.id);
|
|
132
|
-
if (solution.success_count + solution.fail_count > 0) {
|
|
133
|
-
totalSuccessRate += rate;
|
|
134
|
-
solutionCount++;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Check attempts for duration data
|
|
138
|
-
const attempts = this.solutionRepo.getAttempts(solution.id);
|
|
139
|
-
if (attempts.length > 0) {
|
|
140
|
-
let solDuration = 0;
|
|
141
|
-
let solAttemptCount = 0;
|
|
142
|
-
for (const attempt of attempts) {
|
|
143
|
-
if (attempt.duration_ms && attempt.duration_ms > 0) {
|
|
144
|
-
solDuration += attempt.duration_ms;
|
|
145
|
-
solAttemptCount++;
|
|
146
|
-
totalDuration += attempt.duration_ms;
|
|
147
|
-
totalAttempts++;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (solAttemptCount > 0) {
|
|
151
|
-
solutionDurations.push({
|
|
152
|
-
solutionId: solution.id,
|
|
153
|
-
avgDuration: solDuration / solAttemptCount,
|
|
154
|
-
description: solution.description,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Find slow solutions (above 2x average)
|
|
161
|
-
const avgDurationMs = totalAttempts > 0 ? totalDuration / totalAttempts : 0;
|
|
162
|
-
const slowSolutions = solutionDurations
|
|
163
|
-
.filter(s => s.avgDuration > avgDurationMs * 2)
|
|
164
|
-
.sort((a, b) => b.avgDuration - a.avgDuration)
|
|
165
|
-
.slice(0, 10);
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
avgDurationMs,
|
|
169
|
-
slowSolutions,
|
|
170
|
-
successRateOverall: solutionCount > 0 ? totalSuccessRate / solutionCount : 0,
|
|
171
|
-
totalAttempts,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { NodeType, SynapseRecord, NetworkStats } from '../types/synapse.types.js';
|
|
2
|
-
import type { SynapseManager } from '../synapses/synapse-manager.js';
|
|
3
|
-
import type { ActivationResult } from '../synapses/activation.js';
|
|
4
|
-
import type { SynapsePath } from '../synapses/pathfinder.js';
|
|
5
|
-
|
|
6
|
-
export interface ErrorContext {
|
|
7
|
-
solutions: ActivationResult[];
|
|
8
|
-
relatedErrors: ActivationResult[];
|
|
9
|
-
relevantModules: ActivationResult[];
|
|
10
|
-
preventionRules: ActivationResult[];
|
|
11
|
-
insights: ActivationResult[];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface RelatedQuery {
|
|
15
|
-
nodeType: NodeType;
|
|
16
|
-
nodeId: number;
|
|
17
|
-
maxDepth?: number;
|
|
18
|
-
minWeight?: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class SynapseService {
|
|
22
|
-
constructor(private manager: SynapseManager) {}
|
|
23
|
-
|
|
24
|
-
getErrorContext(errorId: number): ErrorContext {
|
|
25
|
-
return this.manager.getErrorContext(errorId);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
findPath(
|
|
29
|
-
fromType: NodeType,
|
|
30
|
-
fromId: number,
|
|
31
|
-
toType: NodeType,
|
|
32
|
-
toId: number,
|
|
33
|
-
): SynapsePath | null {
|
|
34
|
-
return this.manager.findPath(
|
|
35
|
-
{ type: fromType, id: fromId },
|
|
36
|
-
{ type: toType, id: toId },
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
getRelated(query: RelatedQuery): ActivationResult[] {
|
|
41
|
-
return this.manager.activate(
|
|
42
|
-
{ type: query.nodeType, id: query.nodeId },
|
|
43
|
-
query.maxDepth,
|
|
44
|
-
query.minWeight,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getNetworkStats(): NetworkStats {
|
|
49
|
-
return this.manager.getNetworkStats();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
getStrongestSynapses(limit?: number): SynapseRecord[] {
|
|
53
|
-
return this.manager.getStrongestSynapses(limit);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
runDecay(): { decayed: number; pruned: number } {
|
|
57
|
-
return this.manager.runDecay();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import type { TerminalRepository } from '../db/repositories/terminal.repository.js';
|
|
2
|
-
import { getEventBus } from '../utils/events.js';
|
|
3
|
-
import { getLogger } from '../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
export interface RegisterTerminalInput {
|
|
6
|
-
uuid: string;
|
|
7
|
-
projectId?: number;
|
|
8
|
-
pid?: number;
|
|
9
|
-
shell?: string;
|
|
10
|
-
cwd?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class TerminalService {
|
|
14
|
-
private logger = getLogger();
|
|
15
|
-
private eventBus = getEventBus();
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
private terminalRepo: TerminalRepository,
|
|
19
|
-
private staleTimeout: number,
|
|
20
|
-
) {}
|
|
21
|
-
|
|
22
|
-
register(input: RegisterTerminalInput): number {
|
|
23
|
-
const existing = this.terminalRepo.findByUuid(input.uuid);
|
|
24
|
-
if (existing) {
|
|
25
|
-
this.terminalRepo.update(existing.id, {
|
|
26
|
-
last_seen: new Date().toISOString(),
|
|
27
|
-
disconnected_at: null,
|
|
28
|
-
project_id: input.projectId ?? existing.project_id,
|
|
29
|
-
cwd: input.cwd ?? existing.cwd,
|
|
30
|
-
});
|
|
31
|
-
this.logger.info(`Terminal reconnected (id=${existing.id}, uuid=${input.uuid})`);
|
|
32
|
-
this.eventBus.emit('terminal:connected', { terminalId: existing.id, uuid: input.uuid });
|
|
33
|
-
return existing.id;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const id = this.terminalRepo.create({
|
|
37
|
-
uuid: input.uuid,
|
|
38
|
-
project_id: input.projectId ?? null,
|
|
39
|
-
pid: input.pid ?? null,
|
|
40
|
-
shell: input.shell ?? null,
|
|
41
|
-
cwd: input.cwd ?? null,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
this.logger.info(`Terminal registered (id=${id}, uuid=${input.uuid})`);
|
|
45
|
-
this.eventBus.emit('terminal:connected', { terminalId: id, uuid: input.uuid });
|
|
46
|
-
return id;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
heartbeat(uuid: string): void {
|
|
50
|
-
const terminal = this.terminalRepo.findByUuid(uuid);
|
|
51
|
-
if (terminal) {
|
|
52
|
-
this.terminalRepo.update(terminal.id, {
|
|
53
|
-
last_seen: new Date().toISOString(),
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
disconnect(uuid: string): void {
|
|
59
|
-
const terminal = this.terminalRepo.findByUuid(uuid);
|
|
60
|
-
if (terminal) {
|
|
61
|
-
this.terminalRepo.update(terminal.id, {
|
|
62
|
-
disconnected_at: new Date().toISOString(),
|
|
63
|
-
});
|
|
64
|
-
this.eventBus.emit('terminal:disconnected', { terminalId: terminal.id });
|
|
65
|
-
this.logger.info(`Terminal disconnected (id=${terminal.id}, uuid=${uuid})`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
cleanup(): number {
|
|
70
|
-
const cutoff = new Date(Date.now() - this.staleTimeout).toISOString();
|
|
71
|
-
const count = this.terminalRepo.cleanupStale(cutoff);
|
|
72
|
-
if (count > 0) {
|
|
73
|
-
this.logger.info(`Cleaned up ${count} stale terminal(s)`);
|
|
74
|
-
}
|
|
75
|
-
return count;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
getConnected() {
|
|
79
|
-
return this.terminalRepo.findConnected();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { NodeType } from '../types/synapse.types.js';
|
|
2
|
-
import type { SynapseRepository } from '../db/repositories/synapse.repository.js';
|
|
3
|
-
|
|
4
|
-
export interface ActivationNode {
|
|
5
|
-
type: NodeType;
|
|
6
|
-
id: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ActivationResult {
|
|
10
|
-
node: ActivationNode;
|
|
11
|
-
activation: number;
|
|
12
|
-
depth: number;
|
|
13
|
-
path: string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function spreadingActivation(
|
|
17
|
-
repo: SynapseRepository,
|
|
18
|
-
startNode: ActivationNode,
|
|
19
|
-
maxDepth: number = 3,
|
|
20
|
-
minWeight: number = 0.2,
|
|
21
|
-
): ActivationResult[] {
|
|
22
|
-
const visited = new Set<string>();
|
|
23
|
-
const results: ActivationResult[] = [];
|
|
24
|
-
|
|
25
|
-
const queue: Array<{
|
|
26
|
-
node: ActivationNode;
|
|
27
|
-
depth: number;
|
|
28
|
-
pathWeight: number;
|
|
29
|
-
path: string[];
|
|
30
|
-
}> = [{ node: startNode, depth: 0, pathWeight: 1.0, path: [] }];
|
|
31
|
-
|
|
32
|
-
while (queue.length > 0) {
|
|
33
|
-
const current = queue.shift()!;
|
|
34
|
-
const key = `${current.node.type}:${current.node.id}`;
|
|
35
|
-
|
|
36
|
-
if (visited.has(key)) continue;
|
|
37
|
-
if (current.depth > maxDepth) continue;
|
|
38
|
-
if (current.pathWeight < minWeight) continue;
|
|
39
|
-
|
|
40
|
-
visited.add(key);
|
|
41
|
-
|
|
42
|
-
if (current.depth > 0) {
|
|
43
|
-
results.push({
|
|
44
|
-
node: current.node,
|
|
45
|
-
activation: current.pathWeight,
|
|
46
|
-
depth: current.depth,
|
|
47
|
-
path: current.path,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const outgoing = repo.getOutgoing(current.node.type, current.node.id);
|
|
52
|
-
const incoming = repo.getIncoming(current.node.type, current.node.id);
|
|
53
|
-
|
|
54
|
-
for (const synapse of outgoing) {
|
|
55
|
-
const nextWeight = current.pathWeight * synapse.weight;
|
|
56
|
-
if (nextWeight >= minWeight) {
|
|
57
|
-
queue.push({
|
|
58
|
-
node: { type: synapse.target_type, id: synapse.target_id },
|
|
59
|
-
depth: current.depth + 1,
|
|
60
|
-
pathWeight: nextWeight,
|
|
61
|
-
path: [...current.path, `--${synapse.synapse_type}-->`],
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
for (const synapse of incoming) {
|
|
67
|
-
const nextWeight = current.pathWeight * synapse.weight;
|
|
68
|
-
if (nextWeight >= minWeight) {
|
|
69
|
-
queue.push({
|
|
70
|
-
node: { type: synapse.source_type, id: synapse.source_id },
|
|
71
|
-
depth: current.depth + 1,
|
|
72
|
-
pathWeight: nextWeight,
|
|
73
|
-
path: [...current.path, `<--${synapse.synapse_type}--`],
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return results.sort((a, b) => b.activation - a.activation);
|
|
80
|
-
}
|