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.
Files changed (61) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +686 -0
  3. package/LICENSES.md +141 -0
  4. package/README.md +331 -0
  5. package/electron/main.cjs +2849 -0
  6. package/electron/plugin-loader.cjs +184 -0
  7. package/electron/preload.cjs +108 -0
  8. package/package.json +216 -0
  9. package/packages/core/bin/codesynapt-mcp.cjs +611 -0
  10. package/packages/core/bin/codesynapt.cjs +1933 -0
  11. package/packages/core/legacy.js +300 -0
  12. package/packages/core/lib/control-server.cjs +1539 -0
  13. package/packages/core/lib/embedding.cjs +89 -0
  14. package/packages/core/lib/logger.cjs +63 -0
  15. package/packages/core/lib/search-cache.cjs +140 -0
  16. package/packages/core/lib/search-worker.cjs +255 -0
  17. package/packages/core/lib/search.cjs +211 -0
  18. package/packages/core/lib/symbol-graph.cjs +402 -0
  19. package/packages/core/lib/symbol-parser-js.cjs +542 -0
  20. package/packages/core/lib/symbol-parser-misc.cjs +394 -0
  21. package/packages/core/lib/symbol-parser-py.cjs +215 -0
  22. package/packages/core/lib/symbol-parser-treesitter.cjs +658 -0
  23. package/packages/core/lib/symbol-parser-tsc.cjs +332 -0
  24. package/packages/core/monorepo.js +310 -0
  25. package/packages/core/parser.js +2234 -0
  26. package/packages/core/scanner.js +623 -0
  27. package/plugin-api/LICENSE +21 -0
  28. package/plugin-api/README.md +114 -0
  29. package/plugin-api/docs/01-getting-started.md +197 -0
  30. package/plugin-api/docs/02-concepts.md +269 -0
  31. package/plugin-api/docs/api-reference.md +463 -0
  32. package/plugin-api/docs/troubleshooting.md +332 -0
  33. package/plugin-api/docs/types/exporter.md +377 -0
  34. package/plugin-api/docs/types/theme.md +312 -0
  35. package/plugin-api/examples/hello-world-plugin/README.md +70 -0
  36. package/plugin-api/examples/hello-world-plugin/main.js +36 -0
  37. package/plugin-api/examples/hello-world-plugin/manifest.json +12 -0
  38. package/plugin-api/examples/mermaid-exporter/README.md +125 -0
  39. package/plugin-api/examples/mermaid-exporter/main.js +58 -0
  40. package/plugin-api/examples/mermaid-exporter/manifest.json +12 -0
  41. package/plugin-api/examples/rust-parser/README.md +71 -0
  42. package/plugin-api/examples/rust-parser/main.js +123 -0
  43. package/plugin-api/examples/rust-parser/manifest.json +12 -0
  44. package/plugin-api/examples/sunset-theme/README.md +95 -0
  45. package/plugin-api/examples/sunset-theme/manifest.json +12 -0
  46. package/plugin-api/examples/sunset-theme/theme.css +31 -0
  47. package/plugin-api/package.json +20 -0
  48. package/plugin-api/types.d.ts +395 -0
  49. package/public/app.js +6837 -0
  50. package/public/backend.js +285 -0
  51. package/public/index.html +647 -0
  52. package/public/plugin-host.js +321 -0
  53. package/public/style.css +4359 -0
  54. package/public/vendor/three.module.js +53044 -0
  55. package/scripts/competitor-watch.mjs +144 -0
  56. package/scripts/copy-vendor.js +21 -0
  57. package/scripts/download-bundled-node.cjs +53 -0
  58. package/scripts/fuses-after-pack.cjs +34 -0
  59. package/scripts/license-check.js +119 -0
  60. package/scripts/perf-test.js +200 -0
  61. 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
+ }