@kopikocappu/mycelium 0.2.2 → 0.2.4
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/dist/cli.js +923 -39
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15698,7 +15698,7 @@ function getEngine() {
|
|
|
15698
15698
|
var import_child_process = require("child_process");
|
|
15699
15699
|
var import_crypto2 = __toESM(require("crypto"));
|
|
15700
15700
|
var import_path2 = __toESM(require("path"));
|
|
15701
|
-
var CbmAdapter = class {
|
|
15701
|
+
var CbmAdapter = class _CbmAdapter {
|
|
15702
15702
|
constructor() {
|
|
15703
15703
|
this.cbmBin = this.findBinary();
|
|
15704
15704
|
}
|
|
@@ -15844,6 +15844,71 @@ var CbmAdapter = class {
|
|
|
15844
15844
|
return null;
|
|
15845
15845
|
}
|
|
15846
15846
|
}
|
|
15847
|
+
static {
|
|
15848
|
+
// Directories/patterns that are never application source code
|
|
15849
|
+
// This is a blacklist approach — custom dirs are included automatically
|
|
15850
|
+
this.NEVER_SOURCE = [
|
|
15851
|
+
"node_modules",
|
|
15852
|
+
".expo",
|
|
15853
|
+
".git",
|
|
15854
|
+
"dist",
|
|
15855
|
+
"build",
|
|
15856
|
+
"out",
|
|
15857
|
+
".next",
|
|
15858
|
+
".nuxt",
|
|
15859
|
+
".output",
|
|
15860
|
+
"coverage",
|
|
15861
|
+
"android",
|
|
15862
|
+
"ios",
|
|
15863
|
+
"Pods",
|
|
15864
|
+
"assets",
|
|
15865
|
+
"public",
|
|
15866
|
+
"static",
|
|
15867
|
+
".mycelium",
|
|
15868
|
+
".graphmem",
|
|
15869
|
+
".cache",
|
|
15870
|
+
".turbo",
|
|
15871
|
+
"generated",
|
|
15872
|
+
"__generated__",
|
|
15873
|
+
"docs",
|
|
15874
|
+
"fixtures",
|
|
15875
|
+
"e2e",
|
|
15876
|
+
".vscode",
|
|
15877
|
+
".idea"
|
|
15878
|
+
];
|
|
15879
|
+
}
|
|
15880
|
+
static {
|
|
15881
|
+
this.NEVER_EXTENSIONS = [
|
|
15882
|
+
".d.ts",
|
|
15883
|
+
".min.js",
|
|
15884
|
+
".min.css",
|
|
15885
|
+
".map",
|
|
15886
|
+
".png",
|
|
15887
|
+
".jpg",
|
|
15888
|
+
".jpeg",
|
|
15889
|
+
".gif",
|
|
15890
|
+
".svg",
|
|
15891
|
+
".ico",
|
|
15892
|
+
".woff",
|
|
15893
|
+
".woff2",
|
|
15894
|
+
".ttf",
|
|
15895
|
+
".mp4",
|
|
15896
|
+
".mp3"
|
|
15897
|
+
];
|
|
15898
|
+
}
|
|
15899
|
+
isSourceFile(filePath) {
|
|
15900
|
+
const parts = filePath.split("/");
|
|
15901
|
+
for (const part of parts) {
|
|
15902
|
+
if (_CbmAdapter.NEVER_SOURCE.includes(part)) return false;
|
|
15903
|
+
}
|
|
15904
|
+
for (const ext2 of _CbmAdapter.NEVER_EXTENSIONS) {
|
|
15905
|
+
if (filePath.endsWith(ext2)) return false;
|
|
15906
|
+
}
|
|
15907
|
+
if (filePath.includes(".test.") || filePath.includes(".spec.")) return false;
|
|
15908
|
+
if (filePath.includes(".stories.")) return false;
|
|
15909
|
+
if (filePath.includes("__tests__") || filePath.includes("__mocks__")) return false;
|
|
15910
|
+
return true;
|
|
15911
|
+
}
|
|
15847
15912
|
convertNodes(cbmNodes, repoPath) {
|
|
15848
15913
|
const now = Date.now();
|
|
15849
15914
|
const nodes = [];
|
|
@@ -15851,6 +15916,11 @@ var CbmAdapter = class {
|
|
|
15851
15916
|
const label = n.label?.toLowerCase() ?? "file";
|
|
15852
15917
|
const kind = this.mapLabel(label);
|
|
15853
15918
|
const filePath = n.file ? import_path2.default.relative(repoPath, n.file).replace(/\\/g, "/") : n.name;
|
|
15919
|
+
if (kind === "file" && !this.isSourceFile(filePath)) continue;
|
|
15920
|
+
if (kind !== "file") {
|
|
15921
|
+
const parentFile = filePath.split("::")[0];
|
|
15922
|
+
if (!this.isSourceFile(parentFile)) continue;
|
|
15923
|
+
}
|
|
15854
15924
|
const id = kind === "file" ? filePath : `${filePath}::${n.qualified_name ?? n.name}`;
|
|
15855
15925
|
nodes.push({
|
|
15856
15926
|
id,
|
|
@@ -15960,7 +16030,51 @@ var EMBEDDED_VIEWER = `<!DOCTYPE html>
|
|
|
15960
16030
|
flex-direction: column;
|
|
15961
16031
|
z-index: 10;
|
|
15962
16032
|
overflow: hidden;
|
|
16033
|
+
position: relative;
|
|
16034
|
+
}
|
|
16035
|
+
|
|
16036
|
+
.sidebar-tab {
|
|
16037
|
+
flex: 1;
|
|
16038
|
+
padding: 8px 4px;
|
|
16039
|
+
background: none;
|
|
16040
|
+
border: none;
|
|
16041
|
+
border-bottom: 2px solid transparent;
|
|
16042
|
+
color: var(--text-dim);
|
|
16043
|
+
cursor: pointer;
|
|
16044
|
+
font-family: inherit;
|
|
16045
|
+
font-size: 11px;
|
|
16046
|
+
letter-spacing: 0.05em;
|
|
16047
|
+
transition: color 0.15s, border-color 0.15s;
|
|
16048
|
+
}
|
|
16049
|
+
.sidebar-tab:hover { color: var(--text); }
|
|
16050
|
+
.sidebar-tab.active { color: var(--text); border-bottom-color: var(--accent); }
|
|
16051
|
+
|
|
16052
|
+
.tab-content { display: flex; flex-direction: column; flex: 1; overflow: hidden; }
|
|
16053
|
+
|
|
16054
|
+
/* \u2500\u2500 History items \u2500\u2500 */
|
|
16055
|
+
.history-item {
|
|
16056
|
+
padding: 10px 12px;
|
|
16057
|
+
border-bottom: 1px solid var(--border);
|
|
16058
|
+
font-size: 11px;
|
|
15963
16059
|
}
|
|
16060
|
+
.history-item:hover { background: rgba(255,255,255,0.02); }
|
|
16061
|
+
.history-file { color: var(--text); margin-bottom: 3px; font-family: 'SF Mono', monospace; }
|
|
16062
|
+
.history-meta { color: var(--text-dim); display: flex; gap: 8px; flex-wrap: wrap; }
|
|
16063
|
+
.history-task { color: var(--accent2); }
|
|
16064
|
+
.history-agent { color: var(--accent3); }
|
|
16065
|
+
|
|
16066
|
+
/* \u2500\u2500 Ignore list items \u2500\u2500 */
|
|
16067
|
+
.ignore-item {
|
|
16068
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
16069
|
+
padding: 4px 0; gap: 8px;
|
|
16070
|
+
}
|
|
16071
|
+
.ignore-item:hover { background: rgba(255,255,255,0.03); }
|
|
16072
|
+
.ignore-pattern { font-size: 11px; color: var(--text); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: 'SF Mono', monospace; }
|
|
16073
|
+
.ignore-pattern.removed { color: var(--text-dim); text-decoration: line-through; }
|
|
16074
|
+
.ignore-pattern.custom { color: #6B8AFF; }
|
|
16075
|
+
.ignore-btn { background: none; border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; font-family: inherit; font-size: 10px; padding: 2px 7px; flex-shrink: 0; }
|
|
16076
|
+
.ignore-btn:hover { color: var(--text); }
|
|
16077
|
+
.ignore-btn.danger:hover { color: #F28B82; border-color: #F28B82; }
|
|
15964
16078
|
|
|
15965
16079
|
#sidebar-header {
|
|
15966
16080
|
padding: 16px;
|
|
@@ -16338,6 +16452,184 @@ var EMBEDDED_VIEWER = `<!DOCTYPE html>
|
|
|
16338
16452
|
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
|
16339
16453
|
}
|
|
16340
16454
|
|
|
16455
|
+
/* \u2500\u2500 Button tooltips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
16456
|
+
[data-tip] { position: relative; }
|
|
16457
|
+
[data-tip]::after {
|
|
16458
|
+
content: attr(data-tip);
|
|
16459
|
+
position: absolute;
|
|
16460
|
+
bottom: calc(100% + 8px);
|
|
16461
|
+
left: 50%;
|
|
16462
|
+
transform: translateX(-50%);
|
|
16463
|
+
background: #1a1a1a;
|
|
16464
|
+
border: 1px solid rgba(255,255,255,0.15);
|
|
16465
|
+
color: rgba(255,255,255,0.85);
|
|
16466
|
+
font-family: var(--font-code);
|
|
16467
|
+
font-size: 11px;
|
|
16468
|
+
white-space: pre;
|
|
16469
|
+
padding: 6px 10px;
|
|
16470
|
+
line-height: 1.5;
|
|
16471
|
+
pointer-events: none;
|
|
16472
|
+
opacity: 0;
|
|
16473
|
+
transition: opacity 0.15s;
|
|
16474
|
+
z-index: 50;
|
|
16475
|
+
text-align: left;
|
|
16476
|
+
min-width: 160px;
|
|
16477
|
+
}
|
|
16478
|
+
[data-tip]:hover::after { opacity: 1; }
|
|
16479
|
+
|
|
16480
|
+
/* \u2500\u2500 Settings panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
16481
|
+
#settings-panel {
|
|
16482
|
+
display: none;
|
|
16483
|
+
position: absolute;
|
|
16484
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
16485
|
+
background: var(--bg);
|
|
16486
|
+
z-index: 30;
|
|
16487
|
+
overflow-y: auto;
|
|
16488
|
+
padding: 20px;
|
|
16489
|
+
}
|
|
16490
|
+
|
|
16491
|
+
#settings-panel.open { display: block; }
|
|
16492
|
+
|
|
16493
|
+
.settings-header {
|
|
16494
|
+
display: flex;
|
|
16495
|
+
align-items: center;
|
|
16496
|
+
justify-content: space-between;
|
|
16497
|
+
margin-bottom: 20px;
|
|
16498
|
+
padding-bottom: 12px;
|
|
16499
|
+
border-bottom: 1px solid var(--border);
|
|
16500
|
+
}
|
|
16501
|
+
|
|
16502
|
+
.settings-title {
|
|
16503
|
+
font-family: var(--font-mono);
|
|
16504
|
+
font-size: 13px;
|
|
16505
|
+
font-weight: 700;
|
|
16506
|
+
color: var(--text);
|
|
16507
|
+
}
|
|
16508
|
+
|
|
16509
|
+
.settings-close {
|
|
16510
|
+
background: none;
|
|
16511
|
+
border: 1px solid var(--border);
|
|
16512
|
+
color: var(--text-dim);
|
|
16513
|
+
cursor: pointer;
|
|
16514
|
+
font-size: 14px;
|
|
16515
|
+
padding: 2px 8px;
|
|
16516
|
+
font-family: var(--font-code);
|
|
16517
|
+
}
|
|
16518
|
+
|
|
16519
|
+
.settings-close:hover { color: var(--text); background: rgba(255,255,255,0.05); }
|
|
16520
|
+
|
|
16521
|
+
.settings-section {
|
|
16522
|
+
margin-bottom: 20px;
|
|
16523
|
+
}
|
|
16524
|
+
|
|
16525
|
+
.settings-section-title {
|
|
16526
|
+
font-family: var(--font-code);
|
|
16527
|
+
font-size: 10px;
|
|
16528
|
+
letter-spacing: 0.12em;
|
|
16529
|
+
text-transform: uppercase;
|
|
16530
|
+
color: var(--text-dim);
|
|
16531
|
+
margin-bottom: 10px;
|
|
16532
|
+
}
|
|
16533
|
+
|
|
16534
|
+
.ignore-item {
|
|
16535
|
+
display: flex;
|
|
16536
|
+
align-items: center;
|
|
16537
|
+
justify-content: space-between;
|
|
16538
|
+
padding: 5px 8px;
|
|
16539
|
+
border-radius: 3px;
|
|
16540
|
+
gap: 8px;
|
|
16541
|
+
}
|
|
16542
|
+
|
|
16543
|
+
.ignore-item:hover { background: rgba(255,255,255,0.04); }
|
|
16544
|
+
|
|
16545
|
+
.ignore-pattern {
|
|
16546
|
+
font-family: var(--font-code);
|
|
16547
|
+
font-size: 11px;
|
|
16548
|
+
color: var(--text);
|
|
16549
|
+
flex: 1;
|
|
16550
|
+
overflow: hidden;
|
|
16551
|
+
text-overflow: ellipsis;
|
|
16552
|
+
white-space: nowrap;
|
|
16553
|
+
}
|
|
16554
|
+
|
|
16555
|
+
.ignore-pattern.removed { color: var(--text-dim); text-decoration: line-through; }
|
|
16556
|
+
.ignore-pattern.custom { color: #6B8AFF; }
|
|
16557
|
+
|
|
16558
|
+
.ignore-btn {
|
|
16559
|
+
background: none;
|
|
16560
|
+
border: 1px solid var(--border);
|
|
16561
|
+
color: var(--text-dim);
|
|
16562
|
+
cursor: pointer;
|
|
16563
|
+
font-family: var(--font-code);
|
|
16564
|
+
font-size: 10px;
|
|
16565
|
+
padding: 2px 7px;
|
|
16566
|
+
flex-shrink: 0;
|
|
16567
|
+
transition: all 0.12s;
|
|
16568
|
+
}
|
|
16569
|
+
|
|
16570
|
+
.ignore-btn:hover { color: var(--text); border-color: rgba(255,255,255,0.3); }
|
|
16571
|
+
.ignore-btn.danger:hover { color: #F28B82; border-color: #F28B82; }
|
|
16572
|
+
|
|
16573
|
+
.ignore-add-row {
|
|
16574
|
+
display: flex;
|
|
16575
|
+
gap: 8px;
|
|
16576
|
+
margin-top: 10px;
|
|
16577
|
+
}
|
|
16578
|
+
|
|
16579
|
+
.ignore-input {
|
|
16580
|
+
flex: 1;
|
|
16581
|
+
background: var(--surface2);
|
|
16582
|
+
border: 1px solid var(--border);
|
|
16583
|
+
color: var(--text);
|
|
16584
|
+
font-family: var(--font-code);
|
|
16585
|
+
font-size: 12px;
|
|
16586
|
+
padding: 7px 10px;
|
|
16587
|
+
outline: none;
|
|
16588
|
+
}
|
|
16589
|
+
|
|
16590
|
+
.ignore-input:focus { border-color: rgba(255,255,255,0.3); }
|
|
16591
|
+
.ignore-input::placeholder { color: var(--text-dim); }
|
|
16592
|
+
|
|
16593
|
+
.ignore-add-btn {
|
|
16594
|
+
background: rgba(107,138,255,0.15);
|
|
16595
|
+
border: 1px solid rgba(107,138,255,0.3);
|
|
16596
|
+
color: #6B8AFF;
|
|
16597
|
+
cursor: pointer;
|
|
16598
|
+
font-family: var(--font-code);
|
|
16599
|
+
font-size: 11px;
|
|
16600
|
+
padding: 7px 14px;
|
|
16601
|
+
transition: all 0.12s;
|
|
16602
|
+
}
|
|
16603
|
+
|
|
16604
|
+
.ignore-add-btn:hover { background: rgba(107,138,255,0.25); }
|
|
16605
|
+
|
|
16606
|
+
.settings-note {
|
|
16607
|
+
font-family: var(--font-code);
|
|
16608
|
+
font-size: 10px;
|
|
16609
|
+
color: var(--text-dim);
|
|
16610
|
+
margin-top: 12px;
|
|
16611
|
+
line-height: 1.6;
|
|
16612
|
+
padding: 8px;
|
|
16613
|
+
border: 1px solid var(--border);
|
|
16614
|
+
background: rgba(255,255,255,0.02);
|
|
16615
|
+
}
|
|
16616
|
+
|
|
16617
|
+
.rescan-btn {
|
|
16618
|
+
width: 100%;
|
|
16619
|
+
margin-top: 16px;
|
|
16620
|
+
padding: 10px;
|
|
16621
|
+
background: rgba(107,138,255,0.1);
|
|
16622
|
+
border: 1px solid rgba(107,138,255,0.25);
|
|
16623
|
+
color: #6B8AFF;
|
|
16624
|
+
font-family: var(--font-code);
|
|
16625
|
+
font-size: 12px;
|
|
16626
|
+
cursor: pointer;
|
|
16627
|
+
transition: all 0.12s;
|
|
16628
|
+
letter-spacing: 0.05em;
|
|
16629
|
+
}
|
|
16630
|
+
|
|
16631
|
+
.rescan-btn:hover { background: rgba(107,138,255,0.2); }
|
|
16632
|
+
|
|
16341
16633
|
#tooltip .tip-name { font-weight: 700; font-size: 13px; margin-bottom: 4px; color: var(--text); }
|
|
16342
16634
|
#tooltip .tip-desc { color: var(--text-dim); font-size: 11px; line-height: 1.6; margin-top: 4px; }
|
|
16343
16635
|
#tooltip .tip-tags { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 3px; }
|
|
@@ -16360,12 +16652,12 @@ var EMBEDDED_VIEWER = `<!DOCTYPE html>
|
|
|
16360
16652
|
|
|
16361
16653
|
.link {
|
|
16362
16654
|
fill: none;
|
|
16363
|
-
stroke:
|
|
16364
|
-
stroke-opacity: 0.
|
|
16655
|
+
stroke: #4a8fff;
|
|
16656
|
+
stroke-opacity: 0.6;
|
|
16365
16657
|
}
|
|
16366
16658
|
|
|
16367
|
-
.link.imports-edge { stroke:
|
|
16368
|
-
.link.calls-edge { stroke:
|
|
16659
|
+
.link.imports-edge { stroke: #4a8fff; stroke-width: 1.5; stroke-opacity: 0.6; }
|
|
16660
|
+
.link.calls-edge { stroke: #f7916a; stroke-width: 1.5; stroke-dasharray: 5,4; stroke-opacity: 0.5; }
|
|
16369
16661
|
.link.contains-edge { stroke: #333344; stroke-width: 0.5; stroke-dasharray: 3,3; }
|
|
16370
16662
|
|
|
16371
16663
|
.link.highlighted { stroke-opacity: 1 !important; stroke-width: 3 !important; }
|
|
@@ -16391,41 +16683,136 @@ var EMBEDDED_VIEWER = `<!DOCTYPE html>
|
|
|
16391
16683
|
|
|
16392
16684
|
<!-- Sidebar -->
|
|
16393
16685
|
<div id="sidebar">
|
|
16394
|
-
<div id="sidebar-header">
|
|
16686
|
+
<div id="sidebar-header" style="display:flex;align-items:center;justify-content:space-between;padding:0 12px;">
|
|
16395
16687
|
<div class="logo">mycelium<span>.dev</span></div>
|
|
16688
|
+
<button id="settings-btn" title="Manage ignore list" style="background:none;border:1px solid var(--border);color:var(--text-dim);cursor:pointer;font-size:13px;padding:3px 8px;font-family:inherit;border-radius:3px;" onmouseenter="this.style.color='var(--text)'" onmouseleave="this.style.color='var(--text-dim)'">\u2699</button>
|
|
16396
16689
|
</div>
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16690
|
+
|
|
16691
|
+
<!-- Tab bar -->
|
|
16692
|
+
<div style="display:flex;border-bottom:1px solid var(--border);flex-shrink:0;">
|
|
16693
|
+
<button class="sidebar-tab active" id="tab-graph" onclick="switchTab('graph')">Graph</button>
|
|
16694
|
+
<button class="sidebar-tab" id="tab-history" onclick="switchTab('history')">History</button>
|
|
16695
|
+
<button class="sidebar-tab" id="tab-help" onclick="switchTab('help')">? Help</button>
|
|
16400
16696
|
</div>
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
<
|
|
16406
|
-
<
|
|
16697
|
+
|
|
16698
|
+
<!-- GRAPH TAB -->
|
|
16699
|
+
<div id="tab-content-graph" class="tab-content">
|
|
16700
|
+
<div id="search-wrap">
|
|
16701
|
+
<span class="search-icon">\u2315</span>
|
|
16702
|
+
<input id="search" type="text" placeholder="Search files, functions, tags\u2026" autocomplete="off">
|
|
16407
16703
|
</div>
|
|
16408
|
-
<div
|
|
16409
|
-
<
|
|
16410
|
-
|
|
16411
|
-
<button class="toggle-btn active" id="
|
|
16412
|
-
<button class="toggle-btn" id="
|
|
16704
|
+
<div id="controls">
|
|
16705
|
+
<div class="control-row">
|
|
16706
|
+
<span>Edges:</span>
|
|
16707
|
+
<button class="toggle-btn active" id="toggle-imports" data-tip="Show/hide import edges Blue solid lines File A imports from File B">Imports</button>
|
|
16708
|
+
<button class="toggle-btn active" id="toggle-calls" data-tip="Show/hide call edges Orange dashed lines Function A calls Function B (needs codebase-memory-mcp)">Calls</button>
|
|
16709
|
+
<button class="toggle-btn" id="toggle-functions" data-tip="Show function nodes Symbols within each file Circles attached to parent file">Functions</button>
|
|
16710
|
+
</div>
|
|
16711
|
+
<div class="control-row">
|
|
16712
|
+
<span>Zoom level:</span>
|
|
16713
|
+
<div class="zoom-level-btns">
|
|
16714
|
+
<button class="toggle-btn active" id="zoom-files" data-tip="Files only (default) One dot = one file Best for codebase structure">Files</button>
|
|
16715
|
+
<button class="toggle-btn" id="zoom-symbols" data-tip="Show symbols Functions and classes appear orbiting their parent file">Symbols</button>
|
|
16716
|
+
</div>
|
|
16413
16717
|
</div>
|
|
16718
|
+
<div class="control-row">
|
|
16719
|
+
<button class="toggle-btn" id="toggle-clusters" data-tip="Directory grouping Dashed outlines show which folder each node is in">Directory clusters</button>
|
|
16720
|
+
</div>
|
|
16721
|
+
</div>
|
|
16722
|
+
<div id="node-info">
|
|
16723
|
+
<div class="node-info-empty" style="line-height:1.8">Click any dot to see<br>what it imports, exports,<br>and connects to</div>
|
|
16414
16724
|
</div>
|
|
16415
|
-
<div
|
|
16416
|
-
<
|
|
16725
|
+
<div id="legend">
|
|
16726
|
+
<div id="dir-legend"></div>
|
|
16727
|
+
<div class="legend-item">
|
|
16728
|
+
<div class="legend-line" style="background:#4a8fff;height:2px;"></div>
|
|
16729
|
+
<span style="font-size:10px;">Import (blue)</span>
|
|
16730
|
+
</div>
|
|
16731
|
+
<div class="legend-item" style="margin-top:4px;">
|
|
16732
|
+
<div style="width:24px;height:2px;background:repeating-linear-gradient(90deg,#f7916a 0,#f7916a 4px,transparent 4px,transparent 8px);flex-shrink:0;"></div>
|
|
16733
|
+
<span style="font-size:10px;">Call (orange dashed)</span>
|
|
16734
|
+
</div>
|
|
16417
16735
|
</div>
|
|
16418
16736
|
</div>
|
|
16419
|
-
|
|
16420
|
-
|
|
16737
|
+
|
|
16738
|
+
<!-- HISTORY TAB -->
|
|
16739
|
+
<div id="tab-content-history" class="tab-content" style="display:none;overflow-y:auto;flex:1;">
|
|
16740
|
+
<div style="padding:12px;border-bottom:1px solid var(--border);">
|
|
16741
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:4px;">Active task</div>
|
|
16742
|
+
<div id="history-task" style="font-size:13px;color:var(--text);">Loading\u2026</div>
|
|
16743
|
+
</div>
|
|
16744
|
+
<div id="history-list" style="padding:8px 0;"></div>
|
|
16745
|
+
<div style="padding:12px;border-top:1px solid var(--border);">
|
|
16746
|
+
<button onclick="loadHistory()" style="background:none;border:1px solid var(--border);color:var(--text-dim);cursor:pointer;font-family:inherit;font-size:11px;padding:5px 10px;width:100%;">\u21BB Refresh</button>
|
|
16747
|
+
</div>
|
|
16748
|
+
</div>
|
|
16749
|
+
|
|
16750
|
+
<!-- HELP TAB -->
|
|
16751
|
+
<div id="tab-content-help" class="tab-content" style="display:none;overflow-y:auto;flex:1;padding:14px;font-size:12px;line-height:1.7;">
|
|
16752
|
+
<div style="font-family:inherit;font-weight:700;font-size:13px;margin-bottom:12px;color:var(--text);">What Mycelium does</div>
|
|
16753
|
+
|
|
16754
|
+
<div style="color:var(--text-dim);margin-bottom:16px;">One command gives AI agents persistent, queryable memory of your entire codebase. Agents call /preflight before touching any file \u2014 instead of reading 40 files, they read 4.</div>
|
|
16755
|
+
|
|
16756
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;">Graph viewer</div>
|
|
16757
|
+
<div style="margin-bottom:14px;">
|
|
16758
|
+
<div style="margin-bottom:5px;"><span style="color:#4a8fff;">\u25CF</span> <strong>Blue solid lines</strong> \u2014 import edges. File A imports from File B.</div>
|
|
16759
|
+
<div style="margin-bottom:5px;"><span style="color:#f7916a;">\u25CF</span> <strong>Orange dashed lines</strong> \u2014 call edges. Function A calls Function B. Requires codebase-memory-mcp.</div>
|
|
16760
|
+
<div style="margin-bottom:5px;"><span style="color:#ffffff;opacity:0.5;">\u25CF</span> <strong>Node size</strong> \u2014 scales with file size (line count).</div>
|
|
16761
|
+
<div style="margin-bottom:5px;"><span style="color:#ffffff;opacity:0.5;">\u25CF</span> <strong>Node color</strong> \u2014 one color per directory.</div>
|
|
16762
|
+
<div style="margin-bottom:5px;"><strong>Click a node</strong> \u2014 see AI description, imports, callers.</div>
|
|
16763
|
+
<div style="margin-bottom:5px;"><strong>Scroll</strong> \u2014 zoom. <strong>Drag</strong> \u2014 pan. <strong>\u22A1</strong> \u2014 fit to screen.</div>
|
|
16764
|
+
</div>
|
|
16765
|
+
|
|
16766
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;">Agent API endpoints</div>
|
|
16767
|
+
<div style="margin-bottom:14px;">
|
|
16768
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/preflight?task=</code><br><span style="color:var(--text-dim);">Natural language task \u2192 exact files to read. Saves 7k+ tokens per agent task.</span></div>
|
|
16769
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/search?q=</code><br><span style="color:var(--text-dim);">Semantic search across all file descriptions and tags.</span></div>
|
|
16770
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/dependencies?file=</code><br><span style="color:var(--text-dim);">What a file imports and what imports it.</span></div>
|
|
16771
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/xref?file=&fn=</code><br><span style="color:var(--text-dim);">Every caller and callee of a function. Know the blast radius before changing anything.</span></div>
|
|
16772
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/history</code><br><span style="color:var(--text-dim);">Every file save by task and agent identity.</span></div>
|
|
16773
|
+
<div style="margin-bottom:6px;"><code style="color:#6af7c4;">/graph</code><br><span style="color:var(--text-dim);">Full graph \u2014 all nodes and edges.</span></div>
|
|
16774
|
+
</div>
|
|
16775
|
+
|
|
16776
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;">CLI commands</div>
|
|
16777
|
+
<div style="margin-bottom:14px;">
|
|
16778
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium init</code> \u2014 scan, summarize, serve, watch</div>
|
|
16779
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium serve</code> \u2014 start server from existing graph</div>
|
|
16780
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium scan</code> \u2014 re-scan without starting server</div>
|
|
16781
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium search</code> \u2014 search from terminal</div>
|
|
16782
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium status</code> \u2014 graph stats</div>
|
|
16783
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium key</code> \u2014 save Anthropic/OpenAI keys</div>
|
|
16784
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium embed</code> \u2014 generate semantic embeddings</div>
|
|
16785
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium history</code> \u2014 show change log</div>
|
|
16786
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium task</code> \u2014 set active task for history</div>
|
|
16787
|
+
<div style="margin-bottom:4px;"><code style="color:#6af7c4;">mycelium ignore</code> \u2014 manage scan ignore list</div>
|
|
16788
|
+
</div>
|
|
16789
|
+
|
|
16790
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;">CLAUDE.md</div>
|
|
16791
|
+
<div style="color:var(--text-dim);margin-bottom:16px;">Mycelium writes a 5-step checklist into CLAUDE.md automatically. Claude Code reads it and calls /preflight before touching any file.</div>
|
|
16792
|
+
|
|
16793
|
+
<div style="border-top:1px solid var(--border);padding-top:12px;">
|
|
16794
|
+
<a href="https://github.com/KopikoCappu/Mycelium" target="_blank" style="color:#6B8AFF;text-decoration:none;font-size:11px;">GitHub \u2197</a>
|
|
16795
|
+
|
|
16796
|
+
<a href="https://www.npmjs.com/package/@kopikocappu/mycelium" target="_blank" style="color:#6B8AFF;text-decoration:none;font-size:11px;">npm \u2197</a>
|
|
16797
|
+
</div>
|
|
16421
16798
|
</div>
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16799
|
+
|
|
16800
|
+
<!-- SETTINGS PANEL (slides over everything) -->
|
|
16801
|
+
<div id="settings-panel" style="display:none;position:absolute;top:0;left:0;right:0;bottom:0;background:var(--surface);z-index:50;overflow-y:auto;padding:16px;flex-direction:column;">
|
|
16802
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid var(--border);">
|
|
16803
|
+
<span style="font-weight:700;font-size:13px;">\u2699 Ignore List</span>
|
|
16804
|
+
<button id="settings-close" style="background:none;border:1px solid var(--border);color:var(--text-dim);cursor:pointer;font-size:12px;padding:3px 10px;font-family:inherit;">\u2715</button>
|
|
16805
|
+
</div>
|
|
16806
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-bottom:8px;">Default patterns</div>
|
|
16807
|
+
<div id="ignore-default-list"></div>
|
|
16808
|
+
<div style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--text-dim);margin-top:14px;margin-bottom:8px;">Custom patterns</div>
|
|
16809
|
+
<div id="ignore-custom-list"><div style="font-size:11px;color:var(--text-dim);padding:4px 0;">None added yet</div></div>
|
|
16810
|
+
<div style="display:flex;gap:8px;margin-top:10px;">
|
|
16811
|
+
<input id="ignore-input" placeholder="e.g. android/** or **/*.test.ts" style="flex:1;background:var(--surface2);border:1px solid var(--border);color:var(--text);font-family:inherit;font-size:12px;padding:7px 10px;outline:none;" />
|
|
16812
|
+
<button id="ignore-add-btn" style="background:rgba(107,138,255,0.15);border:1px solid rgba(107,138,255,0.3);color:#6B8AFF;cursor:pointer;font-family:inherit;font-size:11px;padding:7px 14px;">+ Add</button>
|
|
16426
16813
|
</div>
|
|
16427
|
-
<div
|
|
16428
|
-
<
|
|
16814
|
+
<div style="font-size:10px;color:var(--text-dim);margin-top:12px;padding:8px;border:1px solid var(--border);line-height:1.6;">
|
|
16815
|
+
Changes apply on next scan.<br>Run <code>mycelium scan</code> or re-run <code>mycelium init</code>.
|
|
16429
16816
|
</div>
|
|
16430
16817
|
</div>
|
|
16431
16818
|
</div>
|
|
@@ -16562,7 +16949,12 @@ function buildVis() {
|
|
|
16562
16949
|
// Filter nodes/links based on current view settings
|
|
16563
16950
|
visNodes = allNodes.filter(n => {
|
|
16564
16951
|
if (n.kind === 'file') return true;
|
|
16565
|
-
if (
|
|
16952
|
+
if (zoomLevel === 'symbols' && showFunctions) {
|
|
16953
|
+
if (n.kind === 'function' || n.kind === 'class' || n.kind === 'type' || n.kind === 'interface') {
|
|
16954
|
+
// Only show named, non-anonymous symbols
|
|
16955
|
+
return n.name && n.name.length > 1 && !n.name.includes('anonymous') && !n.name.startsWith('_');
|
|
16956
|
+
}
|
|
16957
|
+
}
|
|
16566
16958
|
return false;
|
|
16567
16959
|
});
|
|
16568
16960
|
|
|
@@ -16571,7 +16963,9 @@ function buildVis() {
|
|
|
16571
16963
|
if (!nodeIds.has(l.from) || !nodeIds.has(l.to)) return false;
|
|
16572
16964
|
if (l.kind === 'imports' && !showImports) return false;
|
|
16573
16965
|
if (l.kind === 'calls' && !showCalls) return false;
|
|
16574
|
-
|
|
16966
|
+
// Contains edges anchor symbols to their parent file
|
|
16967
|
+
// Critical: without these, symbols have no edges and scatter everywhere
|
|
16968
|
+
if (l.kind === 'contains') return zoomLevel === 'symbols' && showFunctions;
|
|
16575
16969
|
return true;
|
|
16576
16970
|
});
|
|
16577
16971
|
|
|
@@ -16620,7 +17014,11 @@ function buildVis() {
|
|
|
16620
17014
|
.id(d => d.id)
|
|
16621
17015
|
.distance(d => d.kind === 'contains' ? 40 : d.kind === 'calls' ? 80 : 100)
|
|
16622
17016
|
.strength(d => d.kind === 'contains' ? 0.8 : 0.3))
|
|
16623
|
-
.force('charge', d3.forceManyBody()
|
|
17017
|
+
.force('charge', d3.forceManyBody()
|
|
17018
|
+
// Symbols get very weak repulsion so they cluster near parent files via contains edges
|
|
17019
|
+
// Without this, 600+ unconnected symbols explode across the entire screen
|
|
17020
|
+
.strength(d => d.kind === 'file' ? -180 : -8)
|
|
17021
|
+
.distanceMax(300))
|
|
16624
17022
|
.force('center', d3.forceCenter(W / 2, H / 2))
|
|
16625
17023
|
.force('collision', d3.forceCollide().radius(d => nodeRadius(d) + 8))
|
|
16626
17024
|
.force('cluster', clusterForce(dirGroups, 0.08))
|
|
@@ -17016,6 +17414,12 @@ document.getElementById('toggle-clusters').addEventListener('click', function()
|
|
|
17016
17414
|
});
|
|
17017
17415
|
|
|
17018
17416
|
document.getElementById('zoom-files').addEventListener('click', function() {
|
|
17417
|
+
if (zoomLevel === 'symbols') {
|
|
17418
|
+
// Reset positions \u2014 symbols simulation spreads files far apart
|
|
17419
|
+
// Without this, files stay in their spread-out positions after toggling back
|
|
17420
|
+
allNodes.forEach(n => { n.x = undefined; n.y = undefined; n.vx = 0; n.vy = 0; });
|
|
17421
|
+
_initialFitDone = false;
|
|
17422
|
+
}
|
|
17019
17423
|
zoomLevel = 'files';
|
|
17020
17424
|
this.classList.add('active');
|
|
17021
17425
|
document.getElementById('zoom-symbols').classList.remove('active');
|
|
@@ -17190,6 +17594,202 @@ function hideLoading() {
|
|
|
17190
17594
|
initMinimap();
|
|
17191
17595
|
loadGraph();
|
|
17192
17596
|
|
|
17597
|
+
// \u2500\u2500 Tab switching \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
17598
|
+
function switchTab(name) {
|
|
17599
|
+
['graph','history','help'].forEach(t => {
|
|
17600
|
+
document.getElementById('tab-content-' + t).style.display = t === name ? 'flex' : 'none';
|
|
17601
|
+
const btn = document.getElementById('tab-' + t);
|
|
17602
|
+
if (btn) btn.classList.toggle('active', t === name);
|
|
17603
|
+
});
|
|
17604
|
+
if (name === 'history') loadHistory();
|
|
17605
|
+
}
|
|
17606
|
+
|
|
17607
|
+
// \u2500\u2500 History tab \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
17608
|
+
async function loadHistory() {
|
|
17609
|
+
const listEl = document.getElementById('history-list');
|
|
17610
|
+
const taskEl = document.getElementById('history-task');
|
|
17611
|
+
listEl.innerHTML = '<div style="padding:12px;font-size:11px;color:var(--text-dim)">Loading\u2026</div>';
|
|
17612
|
+
try {
|
|
17613
|
+
const res = await fetch(\`\${API_BASE}/history\`);
|
|
17614
|
+
const data = await res.json();
|
|
17615
|
+
taskEl.textContent = data.activeTask || 'General development';
|
|
17616
|
+
const changes = data.recentChanges || [];
|
|
17617
|
+
if (changes.length === 0) {
|
|
17618
|
+
listEl.innerHTML = '<div style="padding:12px;font-size:11px;color:var(--text-dim)">No changes yet.<br>Mycelium logs every file save during active sessions.</div>';
|
|
17619
|
+
return;
|
|
17620
|
+
}
|
|
17621
|
+
listEl.innerHTML = changes.slice(0, 50).map(c => {
|
|
17622
|
+
const time = new Date(c.timestamp || c.at || Date.now()).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'});
|
|
17623
|
+
const file = (c.file || c.path || '').split(/[\\/]/).pop();
|
|
17624
|
+
return \`<div class="history-item">
|
|
17625
|
+
<div class="history-file">\${file || c.file || '\u2014'}</div>
|
|
17626
|
+
<div class="history-meta">
|
|
17627
|
+
<span>\${time}</span>
|
|
17628
|
+
\${c.task ? '<span class="history-task">' + c.task + '</span>' : ''}
|
|
17629
|
+
\${c.agent ? '<span class="history-agent">' + c.agent + '</span>' : ''}
|
|
17630
|
+
</div>
|
|
17631
|
+
</div>\`;
|
|
17632
|
+
}).join('');
|
|
17633
|
+
} catch(e) {
|
|
17634
|
+
listEl.innerHTML = '<div style="padding:12px;font-size:11px;color:var(--text-dim)">Could not load history.<br>Is the server running?</div>';
|
|
17635
|
+
}
|
|
17636
|
+
}
|
|
17637
|
+
|
|
17638
|
+
// \u2500\u2500 Settings panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
17639
|
+
document.getElementById('settings-btn').addEventListener('click', () => {
|
|
17640
|
+
document.getElementById('settings-panel').style.display = 'flex';
|
|
17641
|
+
loadIgnoreConfig();
|
|
17642
|
+
});
|
|
17643
|
+
document.getElementById('settings-close').addEventListener('click', () => {
|
|
17644
|
+
document.getElementById('settings-panel').style.display = 'none';
|
|
17645
|
+
});
|
|
17646
|
+
|
|
17647
|
+
async function loadIgnoreConfig() {
|
|
17648
|
+
try {
|
|
17649
|
+
const res = await fetch(\`\${API_BASE}/config\`);
|
|
17650
|
+
const data = await res.json();
|
|
17651
|
+
renderIgnoreList(data);
|
|
17652
|
+
} catch(e) {
|
|
17653
|
+
document.getElementById('ignore-default-list').innerHTML =
|
|
17654
|
+
'<div style="font-size:11px;color:var(--text-dim);padding:4px 0">Server not reachable</div>';
|
|
17655
|
+
}
|
|
17656
|
+
}
|
|
17657
|
+
|
|
17658
|
+
function renderIgnoreList(data) {
|
|
17659
|
+
const { defaultIgnore = [], userIgnore = [], userUnignore = [] } = data;
|
|
17660
|
+
document.getElementById('ignore-default-list').innerHTML = defaultIgnore.map(p => {
|
|
17661
|
+
const removed = userUnignore.includes(p);
|
|
17662
|
+
return \`<div class="ignore-item">
|
|
17663
|
+
<span class="ignore-pattern \${removed ? 'removed' : ''}">\${p}</span>
|
|
17664
|
+
\${removed
|
|
17665
|
+
? \`<button class="ignore-btn" onclick="ignoreAction('restore','\${p}')">restore</button>\`
|
|
17666
|
+
: \`<button class="ignore-btn danger" onclick="ignoreAction('remove','\${p}')">remove</button>\`}
|
|
17667
|
+
</div>\`;
|
|
17668
|
+
}).join('');
|
|
17669
|
+
const customEl = document.getElementById('ignore-custom-list');
|
|
17670
|
+
customEl.innerHTML = userIgnore.length === 0
|
|
17671
|
+
? '<div style="font-size:11px;color:var(--text-dim);padding:4px 0;">None added yet</div>'
|
|
17672
|
+
: userIgnore.map(p => \`<div class="ignore-item">
|
|
17673
|
+
<span class="ignore-pattern custom">\${p}</span>
|
|
17674
|
+
<button class="ignore-btn danger" onclick="ignoreAction('remove','\${p}')">remove</button>
|
|
17675
|
+
</div>\`).join('');
|
|
17676
|
+
}
|
|
17677
|
+
|
|
17678
|
+
async function ignoreAction(action, pattern) {
|
|
17679
|
+
try {
|
|
17680
|
+
await fetch(\`\${API_BASE}/config\`, {
|
|
17681
|
+
method: 'POST',
|
|
17682
|
+
headers: {'Content-Type':'application/json'},
|
|
17683
|
+
body: JSON.stringify({action, pattern}),
|
|
17684
|
+
});
|
|
17685
|
+
await loadIgnoreConfig();
|
|
17686
|
+
} catch(e) { alert('Could not update config.'); }
|
|
17687
|
+
}
|
|
17688
|
+
|
|
17689
|
+
document.getElementById('ignore-add-btn').addEventListener('click', async () => {
|
|
17690
|
+
const input = document.getElementById('ignore-input');
|
|
17691
|
+
const pattern = input.value.trim();
|
|
17692
|
+
if (!pattern) return;
|
|
17693
|
+
await ignoreAction('add', pattern);
|
|
17694
|
+
input.value = '';
|
|
17695
|
+
});
|
|
17696
|
+
document.getElementById('ignore-input').addEventListener('keydown', e => {
|
|
17697
|
+
if (e.key === 'Enter') document.getElementById('ignore-add-btn').click();
|
|
17698
|
+
});
|
|
17699
|
+
|
|
17700
|
+
// \u2500\u2500 Settings / Ignore Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
17701
|
+
const settingsBtn = document.getElementById('settings-btn');
|
|
17702
|
+
const settingsPanel = document.getElementById('settings-panel');
|
|
17703
|
+
const settingsClose = document.getElementById('settings-close');
|
|
17704
|
+
|
|
17705
|
+
settingsBtn.addEventListener('click', () => {
|
|
17706
|
+
settingsPanel.classList.add('open');
|
|
17707
|
+
loadIgnoreConfig();
|
|
17708
|
+
});
|
|
17709
|
+
|
|
17710
|
+
settingsClose.addEventListener('click', () => {
|
|
17711
|
+
settingsPanel.classList.remove('open');
|
|
17712
|
+
});
|
|
17713
|
+
|
|
17714
|
+
// Hover effect for gear button
|
|
17715
|
+
settingsBtn.addEventListener('mouseenter', () => {
|
|
17716
|
+
settingsBtn.style.color = 'var(--text)';
|
|
17717
|
+
settingsBtn.style.borderColor = 'rgba(255,255,255,0.3)';
|
|
17718
|
+
});
|
|
17719
|
+
settingsBtn.addEventListener('mouseleave', () => {
|
|
17720
|
+
settingsBtn.style.color = 'var(--text-dim)';
|
|
17721
|
+
settingsBtn.style.borderColor = 'var(--border)';
|
|
17722
|
+
});
|
|
17723
|
+
|
|
17724
|
+
async function loadIgnoreConfig() {
|
|
17725
|
+
try {
|
|
17726
|
+
const res = await fetch(\`\${API_BASE}/config\`);
|
|
17727
|
+
const data = await res.json();
|
|
17728
|
+
renderIgnoreList(data);
|
|
17729
|
+
} catch (e) {
|
|
17730
|
+
document.getElementById('ignore-default-list').innerHTML =
|
|
17731
|
+
'<div style="font-family:var(--font-code);font-size:11px;color:var(--text-dim);padding:4px 0">Could not load config \u2014 is the server running?</div>';
|
|
17732
|
+
}
|
|
17733
|
+
}
|
|
17734
|
+
|
|
17735
|
+
function renderIgnoreList(data) {
|
|
17736
|
+
const { defaultIgnore, userIgnore, userUnignore } = data;
|
|
17737
|
+
|
|
17738
|
+
// Default patterns
|
|
17739
|
+
const defaultEl = document.getElementById('ignore-default-list');
|
|
17740
|
+
defaultEl.innerHTML = defaultIgnore.map(pattern => {
|
|
17741
|
+
const isRemoved = userUnignore.includes(pattern);
|
|
17742
|
+
return \`<div class="ignore-item">
|
|
17743
|
+
<span class="ignore-pattern \${isRemoved ? 'removed' : ''}">\${pattern}</span>
|
|
17744
|
+
\${isRemoved
|
|
17745
|
+
? \`<button class="ignore-btn" onclick="restorePattern('\${pattern}')">restore</button>\`
|
|
17746
|
+
: \`<button class="ignore-btn danger" onclick="removePattern('\${pattern}')">remove</button>\`
|
|
17747
|
+
}
|
|
17748
|
+
</div>\`;
|
|
17749
|
+
}).join('');
|
|
17750
|
+
|
|
17751
|
+
// Custom patterns
|
|
17752
|
+
const customEl = document.getElementById('ignore-custom-list');
|
|
17753
|
+
if (userIgnore.length === 0) {
|
|
17754
|
+
customEl.innerHTML = '<div style="font-family:var(--font-code);font-size:11px;color:var(--text-dim);padding:4px 0;">None added yet</div>';
|
|
17755
|
+
} else {
|
|
17756
|
+
customEl.innerHTML = userIgnore.map(pattern =>
|
|
17757
|
+
\`<div class="ignore-item">
|
|
17758
|
+
<span class="ignore-pattern custom">\${pattern}</span>
|
|
17759
|
+
<button class="ignore-btn danger" onclick="removePattern('\${pattern}')">remove</button>
|
|
17760
|
+
</div>\`
|
|
17761
|
+
).join('');
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
|
|
17765
|
+
async function postIgnoreAction(action, pattern) {
|
|
17766
|
+
try {
|
|
17767
|
+
await fetch(\`\${API_BASE}/config\`, {
|
|
17768
|
+
method: 'POST',
|
|
17769
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17770
|
+
body: JSON.stringify({ action, pattern }),
|
|
17771
|
+
});
|
|
17772
|
+
await loadIgnoreConfig();
|
|
17773
|
+
} catch (e) {
|
|
17774
|
+
alert('Could not update config. Is the server running?');
|
|
17775
|
+
}
|
|
17776
|
+
}
|
|
17777
|
+
|
|
17778
|
+
function removePattern(pattern) { postIgnoreAction('remove', pattern); }
|
|
17779
|
+
function restorePattern(pattern) { postIgnoreAction('restore', pattern); }
|
|
17780
|
+
|
|
17781
|
+
document.getElementById('ignore-add-btn').addEventListener('click', async () => {
|
|
17782
|
+
const input = document.getElementById('ignore-input');
|
|
17783
|
+
const pattern = input.value.trim();
|
|
17784
|
+
if (!pattern) return;
|
|
17785
|
+
await postIgnoreAction('add', pattern);
|
|
17786
|
+
input.value = '';
|
|
17787
|
+
});
|
|
17788
|
+
|
|
17789
|
+
document.getElementById('ignore-input').addEventListener('keydown', e => {
|
|
17790
|
+
if (e.key === 'Enter') document.getElementById('ignore-add-btn').click();
|
|
17791
|
+
});
|
|
17792
|
+
|
|
17193
17793
|
function updateDirLegend() {
|
|
17194
17794
|
const el = document.getElementById('dir-legend');
|
|
17195
17795
|
if (!el) return;
|
|
@@ -17220,8 +17820,62 @@ var RateLimiter = class {
|
|
|
17220
17820
|
return true;
|
|
17221
17821
|
}
|
|
17222
17822
|
};
|
|
17823
|
+
var DEFAULT_IGNORE_LIST = [
|
|
17824
|
+
"node_modules/**",
|
|
17825
|
+
"vendor/**",
|
|
17826
|
+
".pnp/**",
|
|
17827
|
+
"dist/**",
|
|
17828
|
+
"build/**",
|
|
17829
|
+
"out/**",
|
|
17830
|
+
".next/**",
|
|
17831
|
+
".nuxt/**",
|
|
17832
|
+
".output/**",
|
|
17833
|
+
"coverage/**",
|
|
17834
|
+
"android/**",
|
|
17835
|
+
"ios/**",
|
|
17836
|
+
".expo/**",
|
|
17837
|
+
"Pods/**",
|
|
17838
|
+
"assets/**",
|
|
17839
|
+
"public/**",
|
|
17840
|
+
"static/**",
|
|
17841
|
+
"**/*.png",
|
|
17842
|
+
"**/*.jpg",
|
|
17843
|
+
"**/*.jpeg",
|
|
17844
|
+
"**/*.gif",
|
|
17845
|
+
"**/*.svg",
|
|
17846
|
+
"**/*.ico",
|
|
17847
|
+
"**/*.woff",
|
|
17848
|
+
"**/*.woff2",
|
|
17849
|
+
"**/*.ttf",
|
|
17850
|
+
"**/*.mp4",
|
|
17851
|
+
"**/*.mp3",
|
|
17852
|
+
".mycelium/**",
|
|
17853
|
+
".graphmem/**",
|
|
17854
|
+
".cache/**",
|
|
17855
|
+
".turbo/**",
|
|
17856
|
+
"generated/**",
|
|
17857
|
+
"__generated__/**",
|
|
17858
|
+
"**/*.generated.ts",
|
|
17859
|
+
"**/*.generated.js",
|
|
17860
|
+
"**/*.d.ts",
|
|
17861
|
+
"**/*.min.js",
|
|
17862
|
+
"**/*.min.css",
|
|
17863
|
+
"**/*.map",
|
|
17864
|
+
"**/*.test.ts",
|
|
17865
|
+
"**/*.test.tsx",
|
|
17866
|
+
"**/*.spec.ts",
|
|
17867
|
+
"**/*.spec.tsx",
|
|
17868
|
+
"**/__tests__/**",
|
|
17869
|
+
"**/__mocks__/**",
|
|
17870
|
+
"**/*.stories.ts",
|
|
17871
|
+
"**/*.stories.tsx",
|
|
17872
|
+
"docs/**",
|
|
17873
|
+
"fixtures/**",
|
|
17874
|
+
"e2e/**"
|
|
17875
|
+
];
|
|
17223
17876
|
var McpServer = class {
|
|
17224
17877
|
constructor(store, changeLogger, config) {
|
|
17878
|
+
this.projectRoot = null;
|
|
17225
17879
|
this.rateLimiter = new RateLimiter();
|
|
17226
17880
|
this.server = null;
|
|
17227
17881
|
this.store = store;
|
|
@@ -17299,6 +17953,10 @@ var McpServer = class {
|
|
|
17299
17953
|
if (pathname === "/xref") {
|
|
17300
17954
|
return this.handleXref(url, res);
|
|
17301
17955
|
}
|
|
17956
|
+
if (pathname === "/config") {
|
|
17957
|
+
if (req.method === "GET") return this.handleConfigGet(res);
|
|
17958
|
+
if (req.method === "POST") return this.handleConfigPost(req, res);
|
|
17959
|
+
}
|
|
17302
17960
|
if (pathname === "/preflight") {
|
|
17303
17961
|
return this.handlePreflight(url, res);
|
|
17304
17962
|
}
|
|
@@ -17503,6 +18161,75 @@ var McpServer = class {
|
|
|
17503
18161
|
this.handleKeywordPreflight(task, teamName, lens, res);
|
|
17504
18162
|
}
|
|
17505
18163
|
}
|
|
18164
|
+
handleConfigGet(res) {
|
|
18165
|
+
const configPath = path4.join(this.projectRoot ?? process.cwd(), ".mycelium", "config.json");
|
|
18166
|
+
let saved = {};
|
|
18167
|
+
try {
|
|
18168
|
+
if (fs3.existsSync(configPath)) saved = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
18169
|
+
} catch {
|
|
18170
|
+
}
|
|
18171
|
+
const userIgnore = saved?.parser?.userIgnore ?? [];
|
|
18172
|
+
const userUnignore = saved?.parser?.userUnignore ?? [];
|
|
18173
|
+
const defaultIgnore = DEFAULT_IGNORE_LIST;
|
|
18174
|
+
const activeIgnore = [
|
|
18175
|
+
...defaultIgnore.filter((p) => !userUnignore.includes(p)),
|
|
18176
|
+
...userIgnore
|
|
18177
|
+
];
|
|
18178
|
+
res.writeHead(200, { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" });
|
|
18179
|
+
res.end(JSON.stringify({
|
|
18180
|
+
defaultIgnore,
|
|
18181
|
+
userIgnore,
|
|
18182
|
+
userUnignore,
|
|
18183
|
+
activeIgnore,
|
|
18184
|
+
totalActive: activeIgnore.length
|
|
18185
|
+
}));
|
|
18186
|
+
}
|
|
18187
|
+
handleConfigPost(req, res) {
|
|
18188
|
+
let body = "";
|
|
18189
|
+
req.on("data", (chunk) => {
|
|
18190
|
+
body += chunk.toString();
|
|
18191
|
+
});
|
|
18192
|
+
req.on("end", () => {
|
|
18193
|
+
try {
|
|
18194
|
+
const { action, pattern } = JSON.parse(body);
|
|
18195
|
+
const configPath = path4.join(this.projectRoot ?? process.cwd(), ".mycelium", "config.json");
|
|
18196
|
+
let saved = {};
|
|
18197
|
+
try {
|
|
18198
|
+
if (fs3.existsSync(configPath)) saved = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
18199
|
+
} catch {
|
|
18200
|
+
}
|
|
18201
|
+
if (!saved.parser) saved.parser = {};
|
|
18202
|
+
let userIgnore = saved.parser.userIgnore ?? [];
|
|
18203
|
+
let userUnignore = saved.parser.userUnignore ?? [];
|
|
18204
|
+
if (action === "add" && pattern) {
|
|
18205
|
+
if (!userIgnore.includes(pattern)) {
|
|
18206
|
+
userIgnore = [...userIgnore, pattern];
|
|
18207
|
+
userUnignore = userUnignore.filter((p) => p !== pattern);
|
|
18208
|
+
}
|
|
18209
|
+
} else if (action === "remove" && pattern) {
|
|
18210
|
+
const isDefault = DEFAULT_IGNORE_LIST.includes(pattern);
|
|
18211
|
+
if (isDefault) {
|
|
18212
|
+
if (!userUnignore.includes(pattern)) userUnignore = [...userUnignore, pattern];
|
|
18213
|
+
} else {
|
|
18214
|
+
userIgnore = userIgnore.filter((p) => p !== pattern);
|
|
18215
|
+
}
|
|
18216
|
+
} else if (action === "restore" && pattern) {
|
|
18217
|
+
userUnignore = userUnignore.filter((p) => p !== pattern);
|
|
18218
|
+
} else if (action === "reset") {
|
|
18219
|
+
userIgnore = [];
|
|
18220
|
+
userUnignore = [];
|
|
18221
|
+
}
|
|
18222
|
+
saved.parser.userIgnore = userIgnore;
|
|
18223
|
+
saved.parser.userUnignore = userUnignore;
|
|
18224
|
+
fs3.writeFileSync(configPath, JSON.stringify(saved, null, 2));
|
|
18225
|
+
res.writeHead(200, { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" });
|
|
18226
|
+
res.end(JSON.stringify({ ok: true, userIgnore, userUnignore }));
|
|
18227
|
+
} catch (e) {
|
|
18228
|
+
res.writeHead(400);
|
|
18229
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
18230
|
+
}
|
|
18231
|
+
});
|
|
18232
|
+
}
|
|
17506
18233
|
async handleXref(url, res) {
|
|
17507
18234
|
const file = (url.searchParams.get("file") || "").slice(0, 500);
|
|
17508
18235
|
const fn = (url.searchParams.get("fn") || "").slice(0, 500);
|
|
@@ -18073,6 +18800,68 @@ ${GRAPHMEM_SECTION_END}`;
|
|
|
18073
18800
|
}
|
|
18074
18801
|
|
|
18075
18802
|
// src/graph/schema.ts
|
|
18803
|
+
var DEFAULT_IGNORE = [
|
|
18804
|
+
// Package managers & deps
|
|
18805
|
+
"node_modules/**",
|
|
18806
|
+
"vendor/**",
|
|
18807
|
+
".pnp/**",
|
|
18808
|
+
// Build output
|
|
18809
|
+
"dist/**",
|
|
18810
|
+
"build/**",
|
|
18811
|
+
"out/**",
|
|
18812
|
+
".next/**",
|
|
18813
|
+
".nuxt/**",
|
|
18814
|
+
".output/**",
|
|
18815
|
+
"coverage/**",
|
|
18816
|
+
// Native mobile (usually auto-generated)
|
|
18817
|
+
"android/**",
|
|
18818
|
+
"ios/**",
|
|
18819
|
+
".expo/**",
|
|
18820
|
+
"Pods/**",
|
|
18821
|
+
// Assets & static
|
|
18822
|
+
"assets/**",
|
|
18823
|
+
"public/**",
|
|
18824
|
+
"static/**",
|
|
18825
|
+
"**/*.png",
|
|
18826
|
+
"**/*.jpg",
|
|
18827
|
+
"**/*.jpeg",
|
|
18828
|
+
"**/*.gif",
|
|
18829
|
+
"**/*.svg",
|
|
18830
|
+
"**/*.ico",
|
|
18831
|
+
"**/*.woff",
|
|
18832
|
+
"**/*.woff2",
|
|
18833
|
+
"**/*.ttf",
|
|
18834
|
+
"**/*.mp4",
|
|
18835
|
+
"**/*.mp3",
|
|
18836
|
+
// Generated & cache
|
|
18837
|
+
".mycelium/**",
|
|
18838
|
+
".graphmem/**",
|
|
18839
|
+
".cache/**",
|
|
18840
|
+
".turbo/**",
|
|
18841
|
+
"generated/**",
|
|
18842
|
+
"__generated__/**",
|
|
18843
|
+
"**/*.generated.ts",
|
|
18844
|
+
"**/*.generated.js",
|
|
18845
|
+
// Config & declaration files
|
|
18846
|
+
"**/*.d.ts",
|
|
18847
|
+
"**/*.min.js",
|
|
18848
|
+
"**/*.min.css",
|
|
18849
|
+
"**/*.map",
|
|
18850
|
+
// Tests (optional — users may want these)
|
|
18851
|
+
"**/*.test.ts",
|
|
18852
|
+
"**/*.test.tsx",
|
|
18853
|
+
"**/*.spec.ts",
|
|
18854
|
+
"**/*.spec.tsx",
|
|
18855
|
+
"**/__tests__/**",
|
|
18856
|
+
"**/__mocks__/**",
|
|
18857
|
+
// Stories
|
|
18858
|
+
"**/*.stories.ts",
|
|
18859
|
+
"**/*.stories.tsx",
|
|
18860
|
+
// Docs & fixtures
|
|
18861
|
+
"docs/**",
|
|
18862
|
+
"fixtures/**",
|
|
18863
|
+
"e2e/**"
|
|
18864
|
+
];
|
|
18076
18865
|
var DEFAULT_CONFIG = {
|
|
18077
18866
|
teams: {
|
|
18078
18867
|
core: { name: "core", includeTags: [], includeAll: true }
|
|
@@ -18082,8 +18871,9 @@ var DEFAULT_CONFIG = {
|
|
|
18082
18871
|
batchSize: 10
|
|
18083
18872
|
},
|
|
18084
18873
|
parser: {
|
|
18085
|
-
include: [
|
|
18086
|
-
|
|
18874
|
+
include: [],
|
|
18875
|
+
// empty = auto-detect from tsconfig or directory structure
|
|
18876
|
+
exclude: DEFAULT_IGNORE
|
|
18087
18877
|
},
|
|
18088
18878
|
mcp: {
|
|
18089
18879
|
port: 47821,
|
|
@@ -18123,13 +18913,41 @@ function resolveRoot(input) {
|
|
|
18123
18913
|
}
|
|
18124
18914
|
function loadProjectConfig(root) {
|
|
18125
18915
|
const cfgPath = path8.join(root, ".mycelium", "config.json");
|
|
18916
|
+
let saved = {};
|
|
18917
|
+
if (fs8.existsSync(cfgPath)) {
|
|
18918
|
+
try {
|
|
18919
|
+
saved = JSON.parse(fs8.readFileSync(cfgPath, "utf8"));
|
|
18920
|
+
} catch {
|
|
18921
|
+
}
|
|
18922
|
+
}
|
|
18923
|
+
const merged = { ...DEFAULT_CONFIG, ...saved };
|
|
18924
|
+
const userIgnore = saved.parser?.userIgnore ?? [];
|
|
18925
|
+
const userUnignore = saved.parser?.userUnignore ?? [];
|
|
18926
|
+
merged.parser = {
|
|
18927
|
+
...DEFAULT_CONFIG.parser,
|
|
18928
|
+
...saved.parser ?? {},
|
|
18929
|
+
exclude: [
|
|
18930
|
+
...DEFAULT_IGNORE.filter((p) => !userUnignore.includes(p)),
|
|
18931
|
+
...userIgnore
|
|
18932
|
+
],
|
|
18933
|
+
userIgnore,
|
|
18934
|
+
userUnignore
|
|
18935
|
+
};
|
|
18936
|
+
return merged;
|
|
18937
|
+
}
|
|
18938
|
+
function saveProjectConfig(root, update) {
|
|
18939
|
+
const cfgPath = path8.join(root, ".mycelium", "config.json");
|
|
18940
|
+
let existing = {};
|
|
18126
18941
|
if (fs8.existsSync(cfgPath)) {
|
|
18127
18942
|
try {
|
|
18128
|
-
|
|
18943
|
+
existing = JSON.parse(fs8.readFileSync(cfgPath, "utf8"));
|
|
18129
18944
|
} catch {
|
|
18130
18945
|
}
|
|
18131
18946
|
}
|
|
18132
|
-
|
|
18947
|
+
if (!existing.parser) existing.parser = {};
|
|
18948
|
+
if (update.userIgnore !== void 0) existing.parser.userIgnore = update.userIgnore;
|
|
18949
|
+
if (update.userUnignore !== void 0) existing.parser.userUnignore = update.userUnignore;
|
|
18950
|
+
fs8.writeFileSync(cfgPath, JSON.stringify(existing, null, 2));
|
|
18133
18951
|
}
|
|
18134
18952
|
function initProjectDir(root) {
|
|
18135
18953
|
const dir = path8.join(root, ".mycelium");
|
|
@@ -18307,7 +19125,7 @@ program2.command("init [path]").description("Scan codebase, build graph, start s
|
|
|
18307
19125
|
ok(".mcp.json written");
|
|
18308
19126
|
if (opts.serve) {
|
|
18309
19127
|
const server = new McpServer(store, logger, config);
|
|
18310
|
-
server.start();
|
|
19128
|
+
server.start(root);
|
|
18311
19129
|
log("");
|
|
18312
19130
|
log(` ${C.bold}Graph view${C.reset} ${C.cyan}http://localhost:${config.mcp.port}/ui${C.reset}`);
|
|
18313
19131
|
log(` ${C.bold}MCP server${C.reset} ${C.gray}http://localhost:${config.mcp.port}${C.reset}`);
|
|
@@ -18338,7 +19156,7 @@ program2.command("serve [path]").description("Start MCP server and file watcher
|
|
|
18338
19156
|
}
|
|
18339
19157
|
ok(`Loaded graph: ${stats.fileCount} files, ${stats.edgeCount} edges`);
|
|
18340
19158
|
const server = new McpServer(store, logger, config);
|
|
18341
|
-
server.start();
|
|
19159
|
+
server.start(root);
|
|
18342
19160
|
log("");
|
|
18343
19161
|
log(` ${C.bold}Graph view${C.reset} ${C.cyan}http://localhost:${config.mcp.port}/ui${C.reset}`);
|
|
18344
19162
|
log(` ${C.bold}MCP server${C.reset} ${C.gray}http://localhost:${config.mcp.port}${C.reset}`);
|
|
@@ -18529,6 +19347,72 @@ program2.command("embed [path]").description("Generate semantic embeddings for a
|
|
|
18529
19347
|
if (pruned > 0) ok(`${pruned} stale embeddings removed`);
|
|
18530
19348
|
ok(`Semantic search now active on /search and /preflight`);
|
|
18531
19349
|
});
|
|
19350
|
+
program2.command("ignore [path]").description("View and manage the scan ignore list").option("--add <pattern>", "Add a pattern to the ignore list").option("--remove <pattern>", "Remove a pattern (default or custom) from the ignore list").option("--reset", "Reset to default ignore list").action((targetPath, opts) => {
|
|
19351
|
+
header();
|
|
19352
|
+
const root = resolveRoot(targetPath);
|
|
19353
|
+
const config = loadProjectConfig(root);
|
|
19354
|
+
const userIgnore = config.parser.userIgnore ?? [];
|
|
19355
|
+
const userUnignore = config.parser.userUnignore ?? [];
|
|
19356
|
+
if (opts.reset) {
|
|
19357
|
+
saveProjectConfig(root, { userIgnore: [], userUnignore: [] });
|
|
19358
|
+
ok("Ignore list reset to defaults");
|
|
19359
|
+
return;
|
|
19360
|
+
}
|
|
19361
|
+
if (opts.add) {
|
|
19362
|
+
const pattern = opts.add.trim();
|
|
19363
|
+
if (userIgnore.includes(pattern)) {
|
|
19364
|
+
warn(`Already ignored: ${pattern}`);
|
|
19365
|
+
return;
|
|
19366
|
+
}
|
|
19367
|
+
const newUnignore = userUnignore.filter((p) => p !== pattern);
|
|
19368
|
+
saveProjectConfig(root, { userIgnore: [...userIgnore, pattern], userUnignore: newUnignore });
|
|
19369
|
+
ok(`Added to ignore list: ${C.gray}${pattern}${C.reset}`);
|
|
19370
|
+
ok("Run mycelium scan to apply changes");
|
|
19371
|
+
return;
|
|
19372
|
+
}
|
|
19373
|
+
if (opts.remove) {
|
|
19374
|
+
const pattern = opts.remove.trim();
|
|
19375
|
+
const isDefault = DEFAULT_IGNORE.includes(pattern);
|
|
19376
|
+
const isCustom = userIgnore.includes(pattern);
|
|
19377
|
+
if (!isDefault && !isCustom) {
|
|
19378
|
+
warn(`Pattern not found in ignore list: ${pattern}`);
|
|
19379
|
+
return;
|
|
19380
|
+
}
|
|
19381
|
+
if (isDefault) {
|
|
19382
|
+
if (!userUnignore.includes(pattern)) {
|
|
19383
|
+
saveProjectConfig(root, { userIgnore, userUnignore: [...userUnignore, pattern] });
|
|
19384
|
+
}
|
|
19385
|
+
} else {
|
|
19386
|
+
saveProjectConfig(root, { userIgnore: userIgnore.filter((p) => p !== pattern), userUnignore });
|
|
19387
|
+
}
|
|
19388
|
+
ok(`Removed from ignore list: ${C.gray}${pattern}${C.reset}`);
|
|
19389
|
+
ok("Run mycelium scan to apply changes");
|
|
19390
|
+
return;
|
|
19391
|
+
}
|
|
19392
|
+
const activeIgnore = [
|
|
19393
|
+
...DEFAULT_IGNORE.filter((p) => !userUnignore.includes(p)),
|
|
19394
|
+
...userIgnore
|
|
19395
|
+
];
|
|
19396
|
+
log(`
|
|
19397
|
+
${C.bold}Default patterns${C.reset} ${C.gray}(${DEFAULT_IGNORE.length - userUnignore.length} active)${C.reset}`);
|
|
19398
|
+
for (const p of DEFAULT_IGNORE) {
|
|
19399
|
+
const removed = userUnignore.includes(p);
|
|
19400
|
+
log(` ${removed ? C.gray + "\u2717 " : " "}${p}${removed ? " (removed)" : ""}${C.reset}`);
|
|
19401
|
+
}
|
|
19402
|
+
if (userIgnore.length > 0) {
|
|
19403
|
+
log(`
|
|
19404
|
+
${C.bold}Custom patterns${C.reset} ${C.gray}(${userIgnore.length})${C.reset}`);
|
|
19405
|
+
for (const p of userIgnore) {
|
|
19406
|
+
log(` ${C.cyan}+${C.reset} ${p}`);
|
|
19407
|
+
}
|
|
19408
|
+
}
|
|
19409
|
+
log(`
|
|
19410
|
+
${C.gray}Total active: ${activeIgnore.length} patterns${C.reset}`);
|
|
19411
|
+
log(` ${C.gray}Add: mycelium ignore --add "android/**"${C.reset}`);
|
|
19412
|
+
log(` ${C.gray}Remove: mycelium ignore --remove "**/*.test.ts"${C.reset}`);
|
|
19413
|
+
log(` ${C.gray}Reset: mycelium ignore --reset${C.reset}
|
|
19414
|
+
`);
|
|
19415
|
+
});
|
|
18532
19416
|
program2.parse(process.argv);
|
|
18533
19417
|
/*! Bundled license information:
|
|
18534
19418
|
|
package/package.json
CHANGED