codesynapt 0.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/CHANGELOG.md +17 -0
- package/LICENSE +686 -0
- package/LICENSES.md +141 -0
- package/README.md +331 -0
- package/electron/main.cjs +2849 -0
- package/electron/plugin-loader.cjs +184 -0
- package/electron/preload.cjs +108 -0
- package/package.json +216 -0
- package/packages/core/bin/codesynapt-mcp.cjs +611 -0
- package/packages/core/bin/codesynapt.cjs +1933 -0
- package/packages/core/legacy.js +300 -0
- package/packages/core/lib/control-server.cjs +1539 -0
- package/packages/core/lib/embedding.cjs +89 -0
- package/packages/core/lib/logger.cjs +63 -0
- package/packages/core/lib/search-cache.cjs +140 -0
- package/packages/core/lib/search-worker.cjs +255 -0
- package/packages/core/lib/search.cjs +211 -0
- package/packages/core/lib/symbol-graph.cjs +402 -0
- package/packages/core/lib/symbol-parser-js.cjs +542 -0
- package/packages/core/lib/symbol-parser-misc.cjs +394 -0
- package/packages/core/lib/symbol-parser-py.cjs +215 -0
- package/packages/core/lib/symbol-parser-treesitter.cjs +658 -0
- package/packages/core/lib/symbol-parser-tsc.cjs +332 -0
- package/packages/core/monorepo.js +310 -0
- package/packages/core/parser.js +2234 -0
- package/packages/core/scanner.js +623 -0
- package/plugin-api/LICENSE +21 -0
- package/plugin-api/README.md +114 -0
- package/plugin-api/docs/01-getting-started.md +197 -0
- package/plugin-api/docs/02-concepts.md +269 -0
- package/plugin-api/docs/api-reference.md +463 -0
- package/plugin-api/docs/troubleshooting.md +332 -0
- package/plugin-api/docs/types/exporter.md +377 -0
- package/plugin-api/docs/types/theme.md +312 -0
- package/plugin-api/examples/hello-world-plugin/README.md +70 -0
- package/plugin-api/examples/hello-world-plugin/main.js +36 -0
- package/plugin-api/examples/hello-world-plugin/manifest.json +12 -0
- package/plugin-api/examples/mermaid-exporter/README.md +125 -0
- package/plugin-api/examples/mermaid-exporter/main.js +58 -0
- package/plugin-api/examples/mermaid-exporter/manifest.json +12 -0
- package/plugin-api/examples/rust-parser/README.md +71 -0
- package/plugin-api/examples/rust-parser/main.js +123 -0
- package/plugin-api/examples/rust-parser/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/README.md +95 -0
- package/plugin-api/examples/sunset-theme/manifest.json +12 -0
- package/plugin-api/examples/sunset-theme/theme.css +31 -0
- package/plugin-api/package.json +20 -0
- package/plugin-api/types.d.ts +395 -0
- package/public/app.js +6837 -0
- package/public/backend.js +285 -0
- package/public/index.html +647 -0
- package/public/plugin-host.js +321 -0
- package/public/style.css +4359 -0
- package/public/vendor/three.module.js +53044 -0
- package/scripts/competitor-watch.mjs +144 -0
- package/scripts/copy-vendor.js +21 -0
- package/scripts/download-bundled-node.cjs +53 -0
- package/scripts/fuses-after-pack.cjs +34 -0
- package/scripts/license-check.js +119 -0
- package/scripts/perf-test.js +200 -0
- package/server.js +132 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// Legacy / migration audit — surfaces candidates for cleanup with a
|
|
2
|
+
// confidence score and a human-readable reason for each. The goal is
|
|
3
|
+
// to answer "what can I safely delete?" — a question every long-lived
|
|
4
|
+
// codebase needs answered eventually but no static tool gives a
|
|
5
|
+
// trustworthy answer to.
|
|
6
|
+
//
|
|
7
|
+
// Output shape:
|
|
8
|
+
// {
|
|
9
|
+
// orphans: [{ id, confidence, reason, mass, loc }] no in + no out
|
|
10
|
+
// pathPatterns: [{ id, pattern, confidence, reason, mass, loc }]
|
|
11
|
+
// filenamePatterns: [{ id, marker, confidence, reason, mass, loc }]
|
|
12
|
+
// duplicates: [{ basename, files: [{ id, mass, loc, isNewer? }] }]
|
|
13
|
+
// summary: { totalFiles, candidateCount, totalLoc, byCategory }
|
|
14
|
+
// }
|
|
15
|
+
//
|
|
16
|
+
// Confidence is a number 0..1. Above 0.85 means "safe to delete in
|
|
17
|
+
// most cases"; 0.5-0.85 means "review"; below 0.5 means "noise but
|
|
18
|
+
// flagged for completeness." We deliberately do NOT call anything
|
|
19
|
+
// "definitely dead" — dynamic imports, plugins, build-tool entry
|
|
20
|
+
// points all evade static analysis.
|
|
21
|
+
|
|
22
|
+
// Path components that strongly imply abandonment. Order matters only
|
|
23
|
+
// for reason text — all patterns are checked.
|
|
24
|
+
const PATH_MARKERS = [
|
|
25
|
+
{ rx: /(^|\/)_legacy(\/|$|_)/i, label: '_legacy', conf: 0.92 },
|
|
26
|
+
{ rx: /(^|\/)_archive(\/|$|_)/i, label: '_archive', conf: 0.92 },
|
|
27
|
+
{ rx: /(^|\/)_old(\/|$|_)/i, label: '_old', conf: 0.88 },
|
|
28
|
+
{ rx: /(^|\/)legacy(\/|$)/i, label: 'legacy', conf: 0.80 },
|
|
29
|
+
{ rx: /(^|\/)archive(\/|$)/i, label: 'archive', conf: 0.80 },
|
|
30
|
+
{ rx: /(^|\/)deprecated(\/|$)/i, label: 'deprecated', conf: 0.90 },
|
|
31
|
+
{ rx: /(^|\/)old(\/|$)/i, label: 'old', conf: 0.75 },
|
|
32
|
+
{ rx: /(^|\/)backup(s)?(\/|$)/i, label: 'backup', conf: 0.85 },
|
|
33
|
+
{ rx: /(^|\/)v[0-9]+_old(\/|$)/i, label: 'vN_old', conf: 0.92 },
|
|
34
|
+
{ rx: /(^|\/)trash(\/|$)/i, label: 'trash', conf: 0.95 },
|
|
35
|
+
{ rx: /(^|\/)unused(\/|$)/i, label: 'unused', conf: 0.88 },
|
|
36
|
+
// Common version-bumped folders that often outlive their use
|
|
37
|
+
{ rx: /(^|\/)v[0-9]+(\/|$)/i, label: 'vN folder', conf: 0.45 },
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
// Filename suffix/prefix patterns. `confidence` here is the per-file
|
|
41
|
+
// signal; ultimate confidence factors in graph mass too.
|
|
42
|
+
const FILENAME_MARKERS = [
|
|
43
|
+
{ rx: /[._-]old[._]/i, label: '_old marker', conf: 0.80 },
|
|
44
|
+
{ rx: /[._-]old\.[a-z]+$/i, label: 'name_old.ext', conf: 0.85 },
|
|
45
|
+
{ rx: /[._-]legacy[._]/i, label: '_legacy marker', conf: 0.85 },
|
|
46
|
+
{ rx: /[._-]legacy\.[a-z]+$/i, label: 'name_legacy.ext', conf: 0.88 },
|
|
47
|
+
{ rx: /[._-]deprecated[._]/i, label: '_deprecated marker', conf: 0.90 },
|
|
48
|
+
{ rx: /[._-]backup[._]/i, label: '_backup marker', conf: 0.85 },
|
|
49
|
+
{ rx: /[._-]bak\.[a-z]+$/i, label: '.bak.ext', conf: 0.90 },
|
|
50
|
+
{ rx: /\.bak$/i, label: '.bak suffix', conf: 0.92 },
|
|
51
|
+
{ rx: /\.orig$/i, label: '.orig suffix', conf: 0.90 },
|
|
52
|
+
{ rx: /\.tmp\./i, label: '.tmp marker', conf: 0.70 },
|
|
53
|
+
{ rx: /[._-]copy(\d+)?\.[a-z]+$/i, label: 'copy variant', conf: 0.85 },
|
|
54
|
+
// Common version markers — flagged at lower confidence because v2/v3
|
|
55
|
+
// sometimes IS the current code. The duplicate-name check decides
|
|
56
|
+
// whether to elevate.
|
|
57
|
+
{ rx: /[._-]v[0-9]+\.[a-z]+$/i, label: 'vN variant', conf: 0.40 },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
// Files that are typically entry points and shouldn't be flagged as
|
|
61
|
+
// orphans even if nothing imports them statically (the host imports
|
|
62
|
+
// them externally — Electron main, CLI bin, server entry, etc.).
|
|
63
|
+
const ENTRY_PATTERNS = [
|
|
64
|
+
// Universal entry-point filenames (anywhere in the tree)
|
|
65
|
+
/(^|\/)index\.[cm]?[jt]sx?$/,
|
|
66
|
+
/(^|\/)main\.[cm]?[jt]sx?$/,
|
|
67
|
+
/(^|\/)server\.[cm]?[jt]sx?$/,
|
|
68
|
+
/(^|\/)app\.[cm]?[jt]sx?$/,
|
|
69
|
+
/(^|\/)cli\.[cm]?[jt]sx?$/,
|
|
70
|
+
/(^|\/)preload\.[cm]?[jt]sx?$/,
|
|
71
|
+
// Standard build/runtime entry directories
|
|
72
|
+
/(^|\/)bin\/[^/]+$/,
|
|
73
|
+
/(^|\/)scripts\/[^/]+$/,
|
|
74
|
+
/(^|\/)examples?\//, // example files are entry points for users
|
|
75
|
+
/(^|\/)demos?\//,
|
|
76
|
+
/(^|\/)playground\//,
|
|
77
|
+
/(^|\/)fixtures?\//, // test fixtures, loaded by runner
|
|
78
|
+
// Plugin / manifest entry points
|
|
79
|
+
/(^|\/)manifest\.json$/,
|
|
80
|
+
/(^|\/)theme\.css$/,
|
|
81
|
+
// Config & docs
|
|
82
|
+
/(^|\/)vite\.config\./,
|
|
83
|
+
/(^|\/)webpack\.config\./,
|
|
84
|
+
/(^|\/)next\.config\./,
|
|
85
|
+
/(^|\/)rollup\.config\./,
|
|
86
|
+
/(^|\/)esbuild\.config\./,
|
|
87
|
+
/(^|\/)tsconfig(\.[a-z]+)?\.json$/,
|
|
88
|
+
/(^|\/)package(-lock)?\.json$/,
|
|
89
|
+
/(^|\/)pnpm-lock\.yaml$/,
|
|
90
|
+
/(^|\/)yarn\.lock$/,
|
|
91
|
+
/(^|\/)README\.md$/i,
|
|
92
|
+
/(^|\/)CHANGELOG\.md$/i,
|
|
93
|
+
/(^|\/)LICENSE/i,
|
|
94
|
+
/(^|\/)CONTRIBUTING\.md$/i,
|
|
95
|
+
/(^|\/)CODE_OF_CONDUCT\.md$/i,
|
|
96
|
+
/(^|\/)\.[a-z]+rc(\.[a-z]+)?$/, // .eslintrc, .prettierrc, etc.
|
|
97
|
+
/\.config\.[cm]?[jt]s$/,
|
|
98
|
+
/\.d\.ts$/, // type declaration files
|
|
99
|
+
// GitHub project files
|
|
100
|
+
/(^|\/)\.github\//,
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
// Files that are often the test sibling of a real file — distinct
|
|
104
|
+
// from production orphans.
|
|
105
|
+
const TEST_PATTERNS = [
|
|
106
|
+
/\.test\.[a-z]+$/i,
|
|
107
|
+
/\.spec\.[a-z]+$/i,
|
|
108
|
+
/(^|\/)__tests__\//,
|
|
109
|
+
/(^|\/)tests?\//,
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
function looksLikeEntry(id) {
|
|
113
|
+
for (const rx of ENTRY_PATTERNS) if (rx.test(id)) return true
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
function looksLikeTest(id) {
|
|
117
|
+
for (const rx of TEST_PATTERNS) if (rx.test(id)) return true
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
function basenameOf(id) {
|
|
121
|
+
const i = id.lastIndexOf('/')
|
|
122
|
+
return i >= 0 ? id.slice(i + 1) : id
|
|
123
|
+
}
|
|
124
|
+
function stripExt(name) {
|
|
125
|
+
const i = name.lastIndexOf('.')
|
|
126
|
+
return i > 0 ? name.slice(0, i) : name
|
|
127
|
+
}
|
|
128
|
+
// Strip common version/legacy markers from a basename so we can find
|
|
129
|
+
// the "logical name" — `foo_old.js` and `foo.js` both → `foo`.
|
|
130
|
+
function logicalName(name) {
|
|
131
|
+
let n = stripExt(name)
|
|
132
|
+
n = n.replace(/[._-](?:old|legacy|deprecated|backup|bak|orig|copy\d*|v\d+)$/i, '')
|
|
133
|
+
return n.toLowerCase()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function auditLegacy(scanner) {
|
|
137
|
+
if (!scanner) return null
|
|
138
|
+
const files = [...scanner.files.values()]
|
|
139
|
+
const incoming = new Map()
|
|
140
|
+
const outgoing = new Map()
|
|
141
|
+
for (const e of scanner.edges) {
|
|
142
|
+
incoming.set(e.t, (incoming.get(e.t) || 0) + 1)
|
|
143
|
+
outgoing.set(e.s, (outgoing.get(e.s) || 0) + 1)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const orphans = []
|
|
147
|
+
const pathPatterns = []
|
|
148
|
+
const filenamePatterns = []
|
|
149
|
+
const flagged = new Set() // dedup across categories
|
|
150
|
+
|
|
151
|
+
// ── Orphans: no in, no out, not an entry point ──
|
|
152
|
+
for (const f of files) {
|
|
153
|
+
const inc = incoming.get(f.id) || 0
|
|
154
|
+
const out = outgoing.get(f.id) || 0
|
|
155
|
+
if (inc !== 0 || out !== 0) continue
|
|
156
|
+
if (looksLikeEntry(f.id)) continue
|
|
157
|
+
// Honest disclaimer: dynamic patterns reduce confidence sharply.
|
|
158
|
+
const hasDynamic = (f.dynamicPatterns || []).length > 0
|
|
159
|
+
const isTest = looksLikeTest(f.id)
|
|
160
|
+
let conf = 0.85
|
|
161
|
+
let reason = 'no incoming or outgoing edges'
|
|
162
|
+
if (hasDynamic) { conf -= 0.35; reason += '; has dynamic import patterns' }
|
|
163
|
+
if (isTest) { conf -= 0.20; reason += '; appears to be a test file (loaded by runner)' }
|
|
164
|
+
if (f.ext === 'md' || f.ext === 'json' || f.ext === 'yaml' || f.ext === 'yml') {
|
|
165
|
+
conf -= 0.25; reason += '; data/doc file (may be referenced outside the graph)'
|
|
166
|
+
}
|
|
167
|
+
orphans.push({
|
|
168
|
+
id: f.id, confidence: Math.max(0.1, Math.min(0.99, conf)),
|
|
169
|
+
reason, mass: 0, loc: f.loc, size: f.size,
|
|
170
|
+
})
|
|
171
|
+
flagged.add(f.id)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Path-based markers ──
|
|
175
|
+
for (const f of files) {
|
|
176
|
+
for (const m of PATH_MARKERS) {
|
|
177
|
+
if (!m.rx.test(f.id)) continue
|
|
178
|
+
const inc = incoming.get(f.id) || 0
|
|
179
|
+
const out = outgoing.get(f.id) || 0
|
|
180
|
+
// If something still imports it, lower confidence — it's not
|
|
181
|
+
// truly abandoned even if it lives in a legacy folder.
|
|
182
|
+
let conf = m.conf
|
|
183
|
+
let reason = `lives under \`${m.label}\` directory`
|
|
184
|
+
if (inc > 0) { conf -= Math.min(0.4, 0.1 * inc); reason += ` (${inc} dependents — verify before removing)` }
|
|
185
|
+
else { reason += ' and nothing imports it' }
|
|
186
|
+
pathPatterns.push({
|
|
187
|
+
id: f.id, pattern: m.label,
|
|
188
|
+
confidence: Math.max(0.1, Math.min(0.99, conf)),
|
|
189
|
+
reason, mass: inc, loc: f.loc, size: f.size,
|
|
190
|
+
})
|
|
191
|
+
flagged.add(f.id)
|
|
192
|
+
break // one match per file is enough
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── Filename markers ──
|
|
197
|
+
for (const f of files) {
|
|
198
|
+
const base = basenameOf(f.id)
|
|
199
|
+
for (const m of FILENAME_MARKERS) {
|
|
200
|
+
if (!m.rx.test(base)) continue
|
|
201
|
+
const inc = incoming.get(f.id) || 0
|
|
202
|
+
let conf = m.conf
|
|
203
|
+
let reason = `filename has ${m.label}`
|
|
204
|
+
if (inc > 0) { conf -= Math.min(0.35, 0.08 * inc); reason += ` (${inc} dependents)` }
|
|
205
|
+
filenamePatterns.push({
|
|
206
|
+
id: f.id, marker: m.label,
|
|
207
|
+
confidence: Math.max(0.1, Math.min(0.99, conf)),
|
|
208
|
+
reason, mass: inc, loc: f.loc, size: f.size,
|
|
209
|
+
})
|
|
210
|
+
flagged.add(f.id)
|
|
211
|
+
break
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Duplicate-basename detection ──
|
|
216
|
+
//
|
|
217
|
+
// Group files by their *logical* basename (after stripping legacy/version
|
|
218
|
+
// markers). When a logical name has multiple files AND at least one
|
|
219
|
+
// looks legacy (path or filename pattern), report the cluster — the
|
|
220
|
+
// legacy ones are likely the supersedees.
|
|
221
|
+
const byLogical = new Map()
|
|
222
|
+
for (const f of files) {
|
|
223
|
+
const base = basenameOf(f.id)
|
|
224
|
+
const key = logicalName(base) + '.' + (f.ext || '')
|
|
225
|
+
const arr = byLogical.get(key) || []
|
|
226
|
+
arr.push(f)
|
|
227
|
+
byLogical.set(key, arr)
|
|
228
|
+
}
|
|
229
|
+
const duplicates = []
|
|
230
|
+
for (const [key, group] of byLogical) {
|
|
231
|
+
if (group.length < 2) continue
|
|
232
|
+
if (group.length > 6) continue // probably a real pattern (e.g., many "index.js"), skip
|
|
233
|
+
const entries = group.map((f) => ({
|
|
234
|
+
id: f.id,
|
|
235
|
+
mass: incoming.get(f.id) || 0,
|
|
236
|
+
loc: f.loc,
|
|
237
|
+
size: f.size,
|
|
238
|
+
hasLegacyMarker: PATH_MARKERS.some((m) => m.rx.test(f.id))
|
|
239
|
+
|| FILENAME_MARKERS.some((m) => m.rx.test(basenameOf(f.id))),
|
|
240
|
+
}))
|
|
241
|
+
// Skip if NONE has a legacy marker — almost certainly a true duplicate
|
|
242
|
+
// by coincidence (e.g. apps/A/index.js vs apps/B/index.js) rather than a
|
|
243
|
+
// migration leftover.
|
|
244
|
+
if (!entries.some((e) => e.hasLegacyMarker)) continue
|
|
245
|
+
// Mark the one with highest mass as "current"
|
|
246
|
+
entries.sort((a, b) => b.mass - a.mass)
|
|
247
|
+
entries[0].isCurrent = true
|
|
248
|
+
duplicates.push({ basename: key, files: entries })
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ── Sort everything by confidence (descending) ──
|
|
252
|
+
orphans.sort((a, b) => b.confidence - a.confidence)
|
|
253
|
+
pathPatterns.sort((a, b) => b.confidence - a.confidence)
|
|
254
|
+
filenamePatterns.sort((a, b) => b.confidence - a.confidence)
|
|
255
|
+
duplicates.sort((a, b) => a.basename.localeCompare(b.basename))
|
|
256
|
+
|
|
257
|
+
// ── Summary ──
|
|
258
|
+
const candidateIds = new Set([
|
|
259
|
+
...orphans.map((x) => x.id),
|
|
260
|
+
...pathPatterns.map((x) => x.id),
|
|
261
|
+
...filenamePatterns.map((x) => x.id),
|
|
262
|
+
...duplicates.flatMap((d) => d.files.filter((f) => f.hasLegacyMarker).map((f) => f.id)),
|
|
263
|
+
])
|
|
264
|
+
const totalLoc = [...candidateIds].reduce((s, id) => s + (scanner.files.get(id)?.loc || 0), 0)
|
|
265
|
+
return {
|
|
266
|
+
orphans,
|
|
267
|
+
pathPatterns,
|
|
268
|
+
filenamePatterns,
|
|
269
|
+
duplicates,
|
|
270
|
+
summary: {
|
|
271
|
+
totalFiles: files.length,
|
|
272
|
+
candidateCount: candidateIds.size,
|
|
273
|
+
totalLoc,
|
|
274
|
+
byCategory: {
|
|
275
|
+
orphan: orphans.length,
|
|
276
|
+
path: pathPatterns.length,
|
|
277
|
+
filename: filenamePatterns.length,
|
|
278
|
+
duplicate: duplicates.length,
|
|
279
|
+
},
|
|
280
|
+
// Highest-confidence cleanup candidates — flat list, dedup'd,
|
|
281
|
+
// capped at 100, sorted by confidence desc. Useful for "show me
|
|
282
|
+
// the top N things to delete" without paging through categories.
|
|
283
|
+
topCandidates: (() => {
|
|
284
|
+
const all = [
|
|
285
|
+
...orphans.map((o) => ({ ...o, category: 'orphan' })),
|
|
286
|
+
...pathPatterns.map((o) => ({ ...o, category: 'path' })),
|
|
287
|
+
...filenamePatterns.map((o) => ({ ...o, category: 'filename' })),
|
|
288
|
+
]
|
|
289
|
+
const dedup = new Map()
|
|
290
|
+
for (const c of all) {
|
|
291
|
+
const prev = dedup.get(c.id)
|
|
292
|
+
if (!prev || prev.confidence < c.confidence) dedup.set(c.id, c)
|
|
293
|
+
}
|
|
294
|
+
return [...dedup.values()]
|
|
295
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
296
|
+
.slice(0, 100)
|
|
297
|
+
})(),
|
|
298
|
+
},
|
|
299
|
+
}
|
|
300
|
+
}
|