@theihtisham/agent-shadow-brain 1.2.0 → 2.1.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 +837 -73
- package/dist/adapters/aider.d.ts +11 -0
- package/dist/adapters/aider.d.ts.map +1 -0
- package/dist/adapters/aider.js +149 -0
- package/dist/adapters/aider.js.map +1 -0
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +5 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/roo-code.d.ts +14 -0
- package/dist/adapters/roo-code.d.ts.map +1 -0
- package/dist/adapters/roo-code.js +186 -0
- package/dist/adapters/roo-code.js.map +1 -0
- package/dist/brain/adr-engine.d.ts +58 -0
- package/dist/brain/adr-engine.d.ts.map +1 -0
- package/dist/brain/adr-engine.js +400 -0
- package/dist/brain/adr-engine.js.map +1 -0
- package/dist/brain/code-similarity.d.ts +43 -0
- package/dist/brain/code-similarity.d.ts.map +1 -0
- package/dist/brain/code-similarity.js +227 -0
- package/dist/brain/code-similarity.js.map +1 -0
- package/dist/brain/context-completion.d.ts +39 -0
- package/dist/brain/context-completion.d.ts.map +1 -0
- package/dist/brain/context-completion.js +851 -0
- package/dist/brain/context-completion.js.map +1 -0
- package/dist/brain/dependency-graph.d.ts +35 -0
- package/dist/brain/dependency-graph.d.ts.map +1 -0
- package/dist/brain/dependency-graph.js +310 -0
- package/dist/brain/dependency-graph.js.map +1 -0
- package/dist/brain/learning-engine.d.ts +54 -0
- package/dist/brain/learning-engine.d.ts.map +1 -0
- package/dist/brain/learning-engine.js +855 -0
- package/dist/brain/learning-engine.js.map +1 -0
- package/dist/brain/mcp-server.d.ts +30 -0
- package/dist/brain/mcp-server.d.ts.map +1 -0
- package/dist/brain/mcp-server.js +408 -0
- package/dist/brain/mcp-server.js.map +1 -0
- package/dist/brain/multi-project.d.ts +13 -0
- package/dist/brain/multi-project.d.ts.map +1 -0
- package/dist/brain/multi-project.js +163 -0
- package/dist/brain/multi-project.js.map +1 -0
- package/dist/brain/neural-mesh.d.ts +69 -0
- package/dist/brain/neural-mesh.d.ts.map +1 -0
- package/dist/brain/neural-mesh.js +677 -0
- package/dist/brain/neural-mesh.js.map +1 -0
- package/dist/brain/orchestrator.d.ts +111 -1
- package/dist/brain/orchestrator.d.ts.map +1 -1
- package/dist/brain/orchestrator.js +302 -0
- package/dist/brain/orchestrator.js.map +1 -1
- package/dist/brain/perf-profiler.d.ts +14 -0
- package/dist/brain/perf-profiler.d.ts.map +1 -0
- package/dist/brain/perf-profiler.js +289 -0
- package/dist/brain/perf-profiler.js.map +1 -0
- package/dist/brain/semantic-analyzer.d.ts +46 -0
- package/dist/brain/semantic-analyzer.d.ts.map +1 -0
- package/dist/brain/semantic-analyzer.js +496 -0
- package/dist/brain/semantic-analyzer.js.map +1 -0
- package/dist/brain/team-mode.d.ts +27 -0
- package/dist/brain/team-mode.d.ts.map +1 -0
- package/dist/brain/team-mode.js +262 -0
- package/dist/brain/team-mode.js.map +1 -0
- package/dist/brain/type-safety.d.ts +13 -0
- package/dist/brain/type-safety.d.ts.map +1 -0
- package/dist/brain/type-safety.js +217 -0
- package/dist/brain/type-safety.js.map +1 -0
- package/dist/cli.js +593 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +228 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// src/brain/team-mode.ts — Shared team insights, patterns, and stats
|
|
2
|
+
// Stores team data in .shadow-brain-team/ directory under the project
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as crypto from 'crypto';
|
|
6
|
+
export class TeamMode {
|
|
7
|
+
constructor(projectDir, userName) {
|
|
8
|
+
this.projectDir = projectDir;
|
|
9
|
+
this.userName = userName || this.detectUser();
|
|
10
|
+
this.teamDir = path.join(projectDir, '.shadow-brain-team');
|
|
11
|
+
this.insightsDir = path.join(this.teamDir, 'insights');
|
|
12
|
+
this.patternsDir = path.join(this.teamDir, 'patterns');
|
|
13
|
+
this.statsFile = path.join(this.teamDir, 'stats.json');
|
|
14
|
+
this.ensureDirectories();
|
|
15
|
+
}
|
|
16
|
+
shareInsight(insight) {
|
|
17
|
+
const id = crypto.randomUUID();
|
|
18
|
+
const teamInsight = {
|
|
19
|
+
id,
|
|
20
|
+
insight: {
|
|
21
|
+
...insight,
|
|
22
|
+
timestamp: insight.timestamp instanceof Date
|
|
23
|
+
? insight.timestamp
|
|
24
|
+
: new Date(insight.timestamp),
|
|
25
|
+
},
|
|
26
|
+
sharedBy: this.userName,
|
|
27
|
+
sharedAt: new Date(),
|
|
28
|
+
upvotes: 0,
|
|
29
|
+
downvotes: 0,
|
|
30
|
+
tags: this.extractTags(insight),
|
|
31
|
+
};
|
|
32
|
+
const filePath = path.join(this.insightsDir, `${id}.json`);
|
|
33
|
+
const serialized = this.serializeWithDates(teamInsight);
|
|
34
|
+
fs.writeFileSync(filePath, serialized, 'utf-8');
|
|
35
|
+
this.updateStats('share-insight', teamInsight);
|
|
36
|
+
return teamInsight;
|
|
37
|
+
}
|
|
38
|
+
getTeamInsights(limit) {
|
|
39
|
+
const files = this.readJsonFiles(this.insightsDir);
|
|
40
|
+
const insights = files
|
|
41
|
+
.map((data) => this.deserializeTeamInsight(data))
|
|
42
|
+
.sort((a, b) => {
|
|
43
|
+
const dateA = a.sharedAt instanceof Date ? a.sharedAt.getTime() : new Date(a.sharedAt).getTime();
|
|
44
|
+
const dateB = b.sharedAt instanceof Date ? b.sharedAt.getTime() : new Date(b.sharedAt).getTime();
|
|
45
|
+
return dateB - dateA; // newest first
|
|
46
|
+
});
|
|
47
|
+
return limit ? insights.slice(0, limit) : insights;
|
|
48
|
+
}
|
|
49
|
+
getStats() {
|
|
50
|
+
// Read existing stats or compute fresh
|
|
51
|
+
let stats = this.readStatsFile();
|
|
52
|
+
// Refresh stats by scanning actual data
|
|
53
|
+
const insightFiles = this.readJsonFiles(this.insightsDir);
|
|
54
|
+
const patternFiles = this.readJsonFiles(this.patternsDir);
|
|
55
|
+
// Count unique contributors
|
|
56
|
+
const contributors = new Map();
|
|
57
|
+
for (const data of insightFiles) {
|
|
58
|
+
const sharedBy = data.sharedBy;
|
|
59
|
+
if (sharedBy) {
|
|
60
|
+
contributors.set(sharedBy, (contributors.get(sharedBy) ?? 0) + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Build top contributors
|
|
64
|
+
const topContributors = Array.from(contributors.entries())
|
|
65
|
+
.map(([name, insights]) => ({ name, insights }))
|
|
66
|
+
.sort((a, b) => b.insights - a.insights);
|
|
67
|
+
// Gather recent activity from stats log
|
|
68
|
+
const recentActivity = (stats.recentActivity ?? [])
|
|
69
|
+
.sort((a, b) => {
|
|
70
|
+
const tA = a.timestamp instanceof Date ? a.timestamp.getTime() : new Date(a.timestamp).getTime();
|
|
71
|
+
const tB = b.timestamp instanceof Date ? b.timestamp.getTime() : new Date(b.timestamp).getTime();
|
|
72
|
+
return tB - tA;
|
|
73
|
+
})
|
|
74
|
+
.slice(0, 50);
|
|
75
|
+
stats = {
|
|
76
|
+
members: contributors.size || 1,
|
|
77
|
+
totalInsights: insightFiles.length,
|
|
78
|
+
totalPatterns: patternFiles.length,
|
|
79
|
+
topContributors,
|
|
80
|
+
recentActivity,
|
|
81
|
+
};
|
|
82
|
+
// Persist updated stats
|
|
83
|
+
this.writeStatsFile(stats);
|
|
84
|
+
return stats;
|
|
85
|
+
}
|
|
86
|
+
addPattern(pattern) {
|
|
87
|
+
const filePath = path.join(this.patternsDir, `${pattern.id}.json`);
|
|
88
|
+
// Check if pattern already exists — increment occurrences
|
|
89
|
+
if (fs.existsSync(filePath)) {
|
|
90
|
+
const existing = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
91
|
+
existing.occurrences = (existing.occurrences ?? 0) + 1;
|
|
92
|
+
existing.lastSeen = new Date().toISOString();
|
|
93
|
+
fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const serialized = JSON.stringify({
|
|
97
|
+
...pattern,
|
|
98
|
+
addedAt: pattern.addedAt instanceof Date ? pattern.addedAt.toISOString() : pattern.addedAt,
|
|
99
|
+
occurrences: pattern.occurrences ?? 1,
|
|
100
|
+
}, null, 2);
|
|
101
|
+
fs.writeFileSync(filePath, serialized, 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
this.updateStats('add-pattern', pattern);
|
|
104
|
+
}
|
|
105
|
+
getPatterns(language) {
|
|
106
|
+
const files = this.readJsonFiles(this.patternsDir);
|
|
107
|
+
let patterns = files.map((data) => this.deserializeSharedPattern(data));
|
|
108
|
+
if (language) {
|
|
109
|
+
patterns = patterns.filter((p) => p.language === language);
|
|
110
|
+
}
|
|
111
|
+
return patterns.sort((a, b) => b.occurrences - a.occurrences);
|
|
112
|
+
}
|
|
113
|
+
// ── Private Helpers ────────────────────────────────────────────────────
|
|
114
|
+
detectUser() {
|
|
115
|
+
// Try common environment variables
|
|
116
|
+
const envUser = process.env.USER ||
|
|
117
|
+
process.env.USERNAME ||
|
|
118
|
+
process.env.GITHUB_USER ||
|
|
119
|
+
null;
|
|
120
|
+
if (envUser)
|
|
121
|
+
return envUser;
|
|
122
|
+
// Fall back to git config
|
|
123
|
+
try {
|
|
124
|
+
const { execSync } = require('child_process');
|
|
125
|
+
const gitUser = execSync('git config user.name', {
|
|
126
|
+
cwd: this.projectDir,
|
|
127
|
+
encoding: 'utf-8',
|
|
128
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
129
|
+
}).trim();
|
|
130
|
+
if (gitUser)
|
|
131
|
+
return gitUser;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// git not available or not a git repo
|
|
135
|
+
}
|
|
136
|
+
return 'unknown';
|
|
137
|
+
}
|
|
138
|
+
ensureDirectories() {
|
|
139
|
+
const dirs = [this.teamDir, this.insightsDir, this.patternsDir];
|
|
140
|
+
for (const dir of dirs) {
|
|
141
|
+
if (!fs.existsSync(dir)) {
|
|
142
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
readJsonFiles(dirPath) {
|
|
147
|
+
if (!fs.existsSync(dirPath))
|
|
148
|
+
return [];
|
|
149
|
+
try {
|
|
150
|
+
const files = fs.readdirSync(dirPath).filter((f) => f.endsWith('.json'));
|
|
151
|
+
const results = [];
|
|
152
|
+
for (const file of files) {
|
|
153
|
+
try {
|
|
154
|
+
const content = fs.readFileSync(path.join(dirPath, file), 'utf-8');
|
|
155
|
+
results.push(JSON.parse(content));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// skip corrupt files
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return results;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
serializeWithDates(obj) {
|
|
168
|
+
return JSON.stringify(obj, (_, value) => {
|
|
169
|
+
if (value instanceof Date) {
|
|
170
|
+
return { __type: 'Date', iso: value.toISOString() };
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}, 2);
|
|
174
|
+
}
|
|
175
|
+
deserializeTeamInsight(data) {
|
|
176
|
+
return {
|
|
177
|
+
id: data.id,
|
|
178
|
+
insight: {
|
|
179
|
+
...data.insight,
|
|
180
|
+
timestamp: this.parseDate(data.insight?.timestamp),
|
|
181
|
+
},
|
|
182
|
+
sharedBy: data.sharedBy,
|
|
183
|
+
sharedAt: this.parseDate(data.sharedAt),
|
|
184
|
+
upvotes: data.upvotes ?? 0,
|
|
185
|
+
downvotes: data.downvotes ?? 0,
|
|
186
|
+
tags: data.tags ?? [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
deserializeSharedPattern(data) {
|
|
190
|
+
return {
|
|
191
|
+
id: data.id,
|
|
192
|
+
pattern: data.pattern,
|
|
193
|
+
description: data.description,
|
|
194
|
+
language: data.language,
|
|
195
|
+
category: data.category,
|
|
196
|
+
addedBy: data.addedBy,
|
|
197
|
+
addedAt: this.parseDate(data.addedAt),
|
|
198
|
+
occurrences: data.occurrences ?? 1,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
parseDate(value) {
|
|
202
|
+
if (value instanceof Date)
|
|
203
|
+
return value;
|
|
204
|
+
if (typeof value === 'string')
|
|
205
|
+
return new Date(value);
|
|
206
|
+
if (value?.__type === 'Date' && value.iso)
|
|
207
|
+
return new Date(value.iso);
|
|
208
|
+
return new Date();
|
|
209
|
+
}
|
|
210
|
+
extractTags(insight) {
|
|
211
|
+
const tags = new Set();
|
|
212
|
+
tags.add(insight.type);
|
|
213
|
+
tags.add(insight.priority);
|
|
214
|
+
if (insight.files) {
|
|
215
|
+
for (const file of insight.files) {
|
|
216
|
+
const ext = path.extname(file).toLowerCase();
|
|
217
|
+
if (ext)
|
|
218
|
+
tags.add(ext.replace('.', ''));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (insight.sourceAgent) {
|
|
222
|
+
tags.add(insight.sourceAgent);
|
|
223
|
+
}
|
|
224
|
+
return Array.from(tags);
|
|
225
|
+
}
|
|
226
|
+
readStatsFile() {
|
|
227
|
+
if (!fs.existsSync(this.statsFile)) {
|
|
228
|
+
return { members: 0, totalInsights: 0, totalPatterns: 0, topContributors: [], recentActivity: [] };
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(fs.readFileSync(this.statsFile, 'utf-8'));
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return { members: 0, totalInsights: 0, totalPatterns: 0, topContributors: [], recentActivity: [] };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
writeStatsFile(stats) {
|
|
238
|
+
const serialized = JSON.stringify(stats, (_, value) => {
|
|
239
|
+
if (value instanceof Date)
|
|
240
|
+
return value.toISOString();
|
|
241
|
+
return value;
|
|
242
|
+
}, 2);
|
|
243
|
+
fs.writeFileSync(this.statsFile, serialized, 'utf-8');
|
|
244
|
+
}
|
|
245
|
+
updateStats(action, payload) {
|
|
246
|
+
const stats = this.readStatsFile();
|
|
247
|
+
if (!stats.recentActivity) {
|
|
248
|
+
stats.recentActivity = [];
|
|
249
|
+
}
|
|
250
|
+
stats.recentActivity.push({
|
|
251
|
+
user: this.userName,
|
|
252
|
+
action,
|
|
253
|
+
timestamp: new Date().toISOString(),
|
|
254
|
+
});
|
|
255
|
+
// Keep only last 100 activity entries
|
|
256
|
+
if (stats.recentActivity.length > 100) {
|
|
257
|
+
stats.recentActivity = stats.recentActivity.slice(-100);
|
|
258
|
+
}
|
|
259
|
+
this.writeStatsFile(stats);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=team-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-mode.js","sourceRoot":"","sources":["../../src/brain/team-mode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BrainInsight } from '../types.js';
|
|
2
|
+
export declare class TypeSafetyAnalyzer {
|
|
3
|
+
private projectDir;
|
|
4
|
+
constructor(projectDir: string);
|
|
5
|
+
analyzeProject(maxFiles?: number): Promise<BrainInsight[]>;
|
|
6
|
+
analyzeFile(filePath: string): BrainInsight[];
|
|
7
|
+
private checkMissingReturnTypes;
|
|
8
|
+
private checkExcessiveTypeAssertions;
|
|
9
|
+
private buildInsight;
|
|
10
|
+
private collectTsFiles;
|
|
11
|
+
private getLineNumberForIndex;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=type-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-safety.d.ts","sourceRoot":"","sources":["../../src/brain/type-safety.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA6E3C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,cAAc,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAYrE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE;IAsD7C,OAAO,CAAC,uBAAuB;IAkC/B,OAAO,CAAC,4BAA4B;IA0BpC,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,qBAAqB;CAO9B"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// src/brain/type-safety.ts — TypeScript type safety analyzer: detect `any`, ts-ignore, missing return types, etc.
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const IGNORE_DIRS = new Set([
|
|
5
|
+
'node_modules', '.git', 'dist', 'build', 'out', 'target', 'vendor',
|
|
6
|
+
'__pycache__', '.next', '.nuxt', 'coverage', '.cache', '.tox',
|
|
7
|
+
'venv', '.venv', 'env', '.env', 'bin', 'obj', 'Pods', '.gradle',
|
|
8
|
+
]);
|
|
9
|
+
const TS_EXTENSIONS = new Set(['.ts', '.tsx']);
|
|
10
|
+
const RULES = [
|
|
11
|
+
{
|
|
12
|
+
name: 'any-type-usage',
|
|
13
|
+
regex: /:\s*any\b|\bas\s+any\b|<any>/g,
|
|
14
|
+
type: 'warning',
|
|
15
|
+
priority: 'medium',
|
|
16
|
+
buildContent: (match, line, lineNum, filePath) => `Usage of \`any\` type detected in ${path.basename(filePath)}:${lineNum}\n` +
|
|
17
|
+
` Line: ${line.trim()}\n` +
|
|
18
|
+
` Match: "${match[0]}"\n` +
|
|
19
|
+
` Replace with a specific type to maintain type safety.`,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'ts-ignore',
|
|
23
|
+
regex: /\/\/\s*@ts-ignore/g,
|
|
24
|
+
type: 'warning',
|
|
25
|
+
priority: 'high',
|
|
26
|
+
buildContent: (match, line, lineNum, filePath) => `@ts-ignore suppresses all type errors on the next line in ${path.basename(filePath)}:${lineNum}\n` +
|
|
27
|
+
` Line: ${line.trim()}\n` +
|
|
28
|
+
` This hides real type errors. Use \`@ts-expect-error\` with an error code instead,\n` +
|
|
29
|
+
` or fix the underlying type issue.`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'ts-nocheck',
|
|
33
|
+
regex: /\/\/\s*@ts-nocheck/g,
|
|
34
|
+
type: 'warning',
|
|
35
|
+
priority: 'high',
|
|
36
|
+
buildContent: (match, line, lineNum, filePath) => `@ts-nocheck disables type checking for the entire file: ${path.basename(filePath)}:${lineNum}\n` +
|
|
37
|
+
` This completely defeats the purpose of TypeScript.\n` +
|
|
38
|
+
` Fix the type errors individually instead of suppressing all checks.`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'ts-expect-error',
|
|
42
|
+
regex: /\/\/\s*@ts-expect-error/g,
|
|
43
|
+
type: 'suggestion',
|
|
44
|
+
priority: 'low',
|
|
45
|
+
buildContent: (match, line, lineNum, filePath) => `@ts-expect-error found in ${path.basename(filePath)}:${lineNum}\n` +
|
|
46
|
+
` Line: ${line.trim()}\n` +
|
|
47
|
+
` Acceptable if accompanied by a TypeScript error code comment.\n` +
|
|
48
|
+
` Consider adding an error number for traceability.`,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'non-null-assertion',
|
|
52
|
+
regex: /\w+!/g,
|
|
53
|
+
type: 'suggestion',
|
|
54
|
+
priority: 'low',
|
|
55
|
+
buildContent: (match, line, lineNum, filePath) => `Non-null assertion (\`!\`) used in ${path.basename(filePath)}:${lineNum}\n` +
|
|
56
|
+
` Line: ${line.trim()}\n` +
|
|
57
|
+
` Match: "${match[0]}"\n` +
|
|
58
|
+
` Consider using optional chaining (\`?.\`) or a proper null check instead.`,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
export class TypeSafetyAnalyzer {
|
|
62
|
+
constructor(projectDir) {
|
|
63
|
+
this.projectDir = projectDir;
|
|
64
|
+
}
|
|
65
|
+
async analyzeProject(maxFiles = 100) {
|
|
66
|
+
const tsFiles = this.collectTsFiles(this.projectDir, maxFiles);
|
|
67
|
+
const allInsights = [];
|
|
68
|
+
for (const filePath of tsFiles) {
|
|
69
|
+
const insights = this.analyzeFile(filePath);
|
|
70
|
+
allInsights.push(...insights);
|
|
71
|
+
}
|
|
72
|
+
return allInsights;
|
|
73
|
+
}
|
|
74
|
+
analyzeFile(filePath) {
|
|
75
|
+
const insights = [];
|
|
76
|
+
let content;
|
|
77
|
+
try {
|
|
78
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return insights;
|
|
82
|
+
}
|
|
83
|
+
const lines = content.split('\n');
|
|
84
|
+
const relPath = path.relative(this.projectDir, filePath);
|
|
85
|
+
// Run per-line rules
|
|
86
|
+
for (let i = 0; i < lines.length; i++) {
|
|
87
|
+
const line = lines[i];
|
|
88
|
+
const lineNum = i + 1;
|
|
89
|
+
for (const rule of RULES) {
|
|
90
|
+
const matches = [...line.matchAll(rule.regex)];
|
|
91
|
+
// Filter false positives for non-null assertions
|
|
92
|
+
if (rule.name === 'non-null-assertion') {
|
|
93
|
+
const filtered = matches.filter(m => {
|
|
94
|
+
const full = m[0];
|
|
95
|
+
// Skip logical NOT: standalone ! operator
|
|
96
|
+
if (full === '!')
|
|
97
|
+
return false;
|
|
98
|
+
// Skip != and !==
|
|
99
|
+
const idx = m.index;
|
|
100
|
+
if (idx > 0 && line[idx - 1] === '!')
|
|
101
|
+
return false;
|
|
102
|
+
if (idx + 1 < line.length && line[idx + 1] === '=')
|
|
103
|
+
return false;
|
|
104
|
+
return true;
|
|
105
|
+
});
|
|
106
|
+
if (filtered.length > 0) {
|
|
107
|
+
insights.push(this.buildInsight(rule, filtered[0], line, lineNum, relPath));
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
for (const match of matches) {
|
|
112
|
+
insights.push(this.buildInsight(rule, match, line, lineNum, relPath));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// File-level rules
|
|
117
|
+
this.checkMissingReturnTypes(content, lines, relPath, insights);
|
|
118
|
+
this.checkExcessiveTypeAssertions(content, relPath, insights);
|
|
119
|
+
return insights;
|
|
120
|
+
}
|
|
121
|
+
// ── File-level checks ─────────────────────────────────────────────────────────
|
|
122
|
+
checkMissingReturnTypes(content, lines, filePath, insights) {
|
|
123
|
+
// Match exported functions without a return type annotation after the closing paren.
|
|
124
|
+
// Pattern: export (async) function name(params) { — no `: Type` between `)` and `{`
|
|
125
|
+
const exportFnRegex = /^export\s+(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)\s*\{/gm;
|
|
126
|
+
let match;
|
|
127
|
+
while ((match = exportFnRegex.exec(content)) !== null) {
|
|
128
|
+
const fullMatch = match[0];
|
|
129
|
+
const fnName = match[1];
|
|
130
|
+
const afterParams = fullMatch.substring(fullMatch.lastIndexOf(')') + 1, fullMatch.lastIndexOf('{'));
|
|
131
|
+
// If there's no `:` between the closing paren and the opening brace, it's missing a return type
|
|
132
|
+
if (!afterParams.includes(':')) {
|
|
133
|
+
const lineNum = this.getLineNumberForIndex(content, match.index);
|
|
134
|
+
insights.push({
|
|
135
|
+
type: 'suggestion',
|
|
136
|
+
priority: 'low',
|
|
137
|
+
title: `Missing return type on exported function "${fnName}"`,
|
|
138
|
+
content: `Exported function \`${fnName}\` in ${path.basename(filePath)}:${lineNum} lacks a return type annotation.\n` +
|
|
139
|
+
` Adding explicit return types improves documentation, enables better IDE support,\n` +
|
|
140
|
+
` and catches accidental return type changes.`,
|
|
141
|
+
files: [filePath],
|
|
142
|
+
timestamp: new Date(),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
checkExcessiveTypeAssertions(content, filePath, insights) {
|
|
148
|
+
const assertionRegex = /\bas\s+[A-Z]\w+/g;
|
|
149
|
+
const matches = content.match(assertionRegex);
|
|
150
|
+
if (matches && matches.length > 5) {
|
|
151
|
+
insights.push({
|
|
152
|
+
type: 'warning',
|
|
153
|
+
priority: 'medium',
|
|
154
|
+
title: `Excessive type assertions (${matches.length} found)`,
|
|
155
|
+
content: `File ${path.basename(filePath)} contains ${matches.length} \`as SomeType\` assertions.\n` +
|
|
156
|
+
` Assertions: ${matches.slice(0, 8).join(', ')}${matches.length > 8 ? ', ...' : ''}\n` +
|
|
157
|
+
` Excessive type assertions indicate a mismatch between code and type definitions.\n` +
|
|
158
|
+
` Consider refining types or using type guards instead of assertions.`,
|
|
159
|
+
files: [filePath],
|
|
160
|
+
timestamp: new Date(),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
165
|
+
buildInsight(rule, match, line, lineNum, filePath) {
|
|
166
|
+
return {
|
|
167
|
+
type: rule.type,
|
|
168
|
+
priority: rule.priority,
|
|
169
|
+
title: `[type-safety] ${rule.name}`,
|
|
170
|
+
content: rule.buildContent(match, line, lineNum, filePath),
|
|
171
|
+
files: [filePath],
|
|
172
|
+
timestamp: new Date(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
collectTsFiles(dir, maxFiles) {
|
|
176
|
+
const results = [];
|
|
177
|
+
const walk = (currentDir, depth) => {
|
|
178
|
+
if (results.length >= maxFiles)
|
|
179
|
+
return;
|
|
180
|
+
if (depth > 10)
|
|
181
|
+
return;
|
|
182
|
+
let entries;
|
|
183
|
+
try {
|
|
184
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (results.length >= maxFiles)
|
|
191
|
+
return;
|
|
192
|
+
if (entry.name.startsWith('.') && entry.name !== '.env.example')
|
|
193
|
+
continue;
|
|
194
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
if (IGNORE_DIRS.has(entry.name))
|
|
197
|
+
continue;
|
|
198
|
+
walk(fullPath, depth + 1);
|
|
199
|
+
}
|
|
200
|
+
else if (entry.isFile() && TS_EXTENSIONS.has(path.extname(entry.name))) {
|
|
201
|
+
results.push(fullPath);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
walk(dir, 0);
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
getLineNumberForIndex(content, index) {
|
|
209
|
+
let count = 1;
|
|
210
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
211
|
+
if (content[i] === '\n')
|
|
212
|
+
count++;
|
|
213
|
+
}
|
|
214
|
+
return count;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=type-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-safety.js","sourceRoot":"","sources":["../../src/brain/type-safety.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAGlH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IAClE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;IAC7D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS;CAChE,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAU/C,MAAM,KAAK,GAAoB;IAC7B;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,+BAA+B;QACtC,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAC/C,qCAAqC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI;YAC3E,WAAW,IAAI,CAAC,IAAI,EAAE,IAAI;YAC1B,aAAa,KAAK,CAAC,CAAC,CAAC,KAAK;YAC1B,yDAAyD;KAC5D;IACD;QACE,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAC/C,6DAA6D,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI;YACnG,WAAW,IAAI,CAAC,IAAI,EAAE,IAAI;YAC1B,uFAAuF;YACvF,qCAAqC;KACxC;IACD;QACE,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAC/C,2DAA2D,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI;YACjG,wDAAwD;YACxD,uEAAuE;KAC1E;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,0BAA0B;QACjC,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAC/C,6BAA6B,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI;YACnE,WAAW,IAAI,CAAC,IAAI,EAAE,IAAI;YAC1B,mEAAmE;YACnE,qDAAqD;KACxD;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,OAAO;QACd,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAC/C,sCAAsC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI;YAC5E,WAAW,IAAI,CAAC,IAAI,EAAE,IAAI;YAC1B,aAAa,KAAK,CAAC,CAAC,CAAC,KAAK;YAC1B,6EAA6E;KAChF;CACF,CAAC;AAEF,MAAM,OAAO,kBAAkB;IAG7B,YAAY,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,WAAmB,GAAG;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAmB,EAAE,CAAC;QAEvC,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC5C,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,WAAW,CAAC,QAAgB;QAC1B,MAAM,QAAQ,GAAmB,EAAE,CAAC;QAEpC,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAEzD,qBAAqB;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAE/C,iDAAiD;gBACjD,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;oBACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;wBAClC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBAClB,0CAA0C;wBAC1C,IAAI,IAAI,KAAK,GAAG;4BAAE,OAAO,KAAK,CAAC;wBAC/B,kBAAkB;wBAClB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAM,CAAC;wBACrB,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG;4BAAE,OAAO,KAAK,CAAC;wBACnD,IAAI,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG;4BAAE,OAAO,KAAK,CAAC;wBACjE,OAAO,IAAI,CAAC;oBACd,CAAC,CAAC,CAAC;oBACH,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACxB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;oBAC9E,CAAC;oBACD,SAAS;gBACX,CAAC;gBAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC5B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC;QACH,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAChE,IAAI,CAAC,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE9D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,iFAAiF;IAEzE,uBAAuB,CAC7B,OAAe,EACf,KAAe,EACf,QAAgB,EAChB,QAAwB;QAExB,qFAAqF;QACrF,qFAAqF;QACrF,MAAM,aAAa,GAAG,8DAA8D,CAAC;QACrF,IAAI,KAA6B,CAAC;QAElC,OAAO,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACtD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAEpG,gGAAgG;YAChG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBACjE,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,YAAY;oBAClB,QAAQ,EAAE,KAAK;oBACf,KAAK,EAAE,6CAA6C,MAAM,GAAG;oBAC7D,OAAO,EACL,uBAAuB,MAAM,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,oCAAoC;wBAC5G,sFAAsF;wBACtF,+CAA+C;oBACjD,KAAK,EAAE,CAAC,QAAQ,CAAC;oBACjB,SAAS,EAAE,IAAI,IAAI,EAAE;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAEO,4BAA4B,CAClC,OAAe,EACf,QAAgB,EAChB,QAAwB;QAExB,MAAM,cAAc,GAAG,kBAAkB,CAAC;QAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAE9C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,8BAA8B,OAAO,CAAC,MAAM,SAAS;gBAC5D,OAAO,EACL,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,OAAO,CAAC,MAAM,gCAAgC;oBAC1F,iBAAiB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI;oBACvF,sFAAsF;oBACtF,uEAAuE;gBACzE,KAAK,EAAE,CAAC,QAAQ,CAAC;gBACjB,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,iFAAiF;IAEzE,YAAY,CAClB,IAAmB,EACnB,KAAuB,EACvB,IAAY,EACZ,OAAe,EACf,QAAgB;QAEhB,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,KAAK,EAAE,iBAAiB,IAAI,CAAC,IAAI,EAAE;YACnC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC;YAC1D,KAAK,EAAE,CAAC,QAAQ,CAAC;YACjB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,GAAW,EAAE,QAAgB;QAClD,MAAM,OAAO,GAAa,EAAE,CAAC;QAE7B,MAAM,IAAI,GAAG,CAAC,UAAkB,EAAE,KAAa,EAAQ,EAAE;YACvD,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;gBAAE,OAAO;YACvC,IAAI,KAAK,GAAG,EAAE;gBAAE,OAAO;YAEvB,IAAI,OAAoB,CAAC;YACzB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAChE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;YAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ;oBAAE,OAAO;gBACvC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;oBAAE,SAAS;gBAE1E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAEnD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;wBAAE,SAAS;oBAC1C,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBAC5B,CAAC;qBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBACzE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACb,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,qBAAqB,CAAC,OAAe,EAAE,KAAa;QAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrD,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,KAAK,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|