@swarmvaultai/engine 0.7.24 → 0.7.26
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/chunk-MB7HPUTR.js +1364 -0
- package/dist/chunk-N56FAH4N.js +1404 -0
- package/dist/chunk-NAIERP4C.js +65 -0
- package/dist/chunk-ZQ5T64AR.js +1365 -0
- package/dist/index.d.ts +98 -5
- package/dist/index.js +1124 -134
- package/dist/registry-FKEREVDO.js +12 -0
- package/dist/registry-SYCRRA65.js +12 -0
- package/dist/registry-UA42LQUQ.js +12 -0
- package/dist/token-estimation-TTONKT4O.js +10 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,12 @@ import {
|
|
|
22
22
|
uniqueBy,
|
|
23
23
|
writeFileIfChanged,
|
|
24
24
|
writeJsonFile
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-N56FAH4N.js";
|
|
26
|
+
import {
|
|
27
|
+
estimatePageTokens,
|
|
28
|
+
estimateTokens,
|
|
29
|
+
trimToTokenBudget
|
|
30
|
+
} from "./chunk-NAIERP4C.js";
|
|
26
31
|
|
|
27
32
|
// src/agents.ts
|
|
28
33
|
import crypto from "crypto";
|
|
@@ -450,9 +455,47 @@ async function installConfiguredAgents(rootDir) {
|
|
|
450
455
|
);
|
|
451
456
|
}
|
|
452
457
|
|
|
458
|
+
// src/auto-commit.ts
|
|
459
|
+
import { execFile } from "child_process";
|
|
460
|
+
import { promisify } from "util";
|
|
461
|
+
var execFileAsync = promisify(execFile);
|
|
462
|
+
async function git(rootDir, ...args) {
|
|
463
|
+
const { stdout } = await execFileAsync("git", args, { cwd: rootDir });
|
|
464
|
+
return stdout.trim();
|
|
465
|
+
}
|
|
466
|
+
async function isGitRepo(rootDir) {
|
|
467
|
+
try {
|
|
468
|
+
await git(rootDir, "rev-parse", "--is-inside-work-tree");
|
|
469
|
+
return true;
|
|
470
|
+
} catch {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async function autoCommitWikiChanges(rootDir, operation, detail, options) {
|
|
475
|
+
const { config, paths } = await loadVaultConfig(rootDir);
|
|
476
|
+
if (!options?.force && !config.autoCommit) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
if (!await isGitRepo(rootDir)) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
const wikiRelative = paths.wikiDir.replace(`${rootDir}/`, "");
|
|
483
|
+
const stateRelative = paths.stateDir.replace(`${rootDir}/`, "");
|
|
484
|
+
await git(rootDir, "add", wikiRelative, stateRelative).catch(() => {
|
|
485
|
+
});
|
|
486
|
+
const status = await git(rootDir, "diff", "--cached", "--stat");
|
|
487
|
+
if (!status) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
const message = detail ? `vault ${operation}: ${detail}` : `vault ${operation}`;
|
|
491
|
+
await git(rootDir, "commit", "-m", message);
|
|
492
|
+
return message;
|
|
493
|
+
}
|
|
494
|
+
|
|
453
495
|
// src/graph-export.ts
|
|
454
496
|
import fs2 from "fs/promises";
|
|
455
497
|
import path2 from "path";
|
|
498
|
+
import matter from "gray-matter";
|
|
456
499
|
|
|
457
500
|
// src/graph-interchange.ts
|
|
458
501
|
function exportHyperedgeNodeId(hyperedge) {
|
|
@@ -587,6 +630,316 @@ function graphCounts(graph) {
|
|
|
587
630
|
};
|
|
588
631
|
}
|
|
589
632
|
|
|
633
|
+
// src/graph-report-html.ts
|
|
634
|
+
function htmlEscape(text) {
|
|
635
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
636
|
+
}
|
|
637
|
+
function nodeTypeColor(type) {
|
|
638
|
+
const colors = {
|
|
639
|
+
source: "#f59e0b",
|
|
640
|
+
module: "#fb7185",
|
|
641
|
+
symbol: "#8b5cf6",
|
|
642
|
+
rationale: "#14b8a6",
|
|
643
|
+
concept: "#0ea5e9",
|
|
644
|
+
entity: "#22c55e"
|
|
645
|
+
};
|
|
646
|
+
return colors[type] ?? "#94a3b8";
|
|
647
|
+
}
|
|
648
|
+
function renderGraphReportHtml(graph, report) {
|
|
649
|
+
const nodesByType = /* @__PURE__ */ new Map();
|
|
650
|
+
for (const node of graph.nodes) {
|
|
651
|
+
nodesByType.set(node.type, (nodesByType.get(node.type) ?? 0) + 1);
|
|
652
|
+
}
|
|
653
|
+
const edgesByRelation = /* @__PURE__ */ new Map();
|
|
654
|
+
for (const edge of graph.edges) {
|
|
655
|
+
edgesByRelation.set(edge.relation, (edgesByRelation.get(edge.relation) ?? 0) + 1);
|
|
656
|
+
}
|
|
657
|
+
const pagesByKind = /* @__PURE__ */ new Map();
|
|
658
|
+
for (const page of graph.pages) {
|
|
659
|
+
const list = pagesByKind.get(page.kind) ?? [];
|
|
660
|
+
list.push(page);
|
|
661
|
+
pagesByKind.set(page.kind, list);
|
|
662
|
+
}
|
|
663
|
+
const godNodes = (report?.godNodes ?? []).slice(0, 15);
|
|
664
|
+
const bridgeNodes = (report?.bridgeNodes ?? []).slice(0, 10);
|
|
665
|
+
const communities = graph.communities ?? [];
|
|
666
|
+
const warnings = report?.warnings ?? [];
|
|
667
|
+
const overview = report?.overview ?? {
|
|
668
|
+
nodes: graph.nodes.length,
|
|
669
|
+
edges: graph.edges.length,
|
|
670
|
+
pages: graph.pages.length,
|
|
671
|
+
communities: communities.length
|
|
672
|
+
};
|
|
673
|
+
const sortedEdgeRelations = [...edgesByRelation.entries()].sort((a, b) => b[1] - a[1]);
|
|
674
|
+
const sortedNodeTypes = [...nodesByType.entries()].sort((a, b) => b[1] - a[1]);
|
|
675
|
+
const sortedCommunities2 = [...communities].sort((a, b) => b.nodeIds.length - a.nodeIds.length).slice(0, 20);
|
|
676
|
+
return `<!DOCTYPE html>
|
|
677
|
+
<html lang="en">
|
|
678
|
+
<head>
|
|
679
|
+
<meta charset="UTF-8">
|
|
680
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
681
|
+
<title>SwarmVault Graph Report</title>
|
|
682
|
+
<style>
|
|
683
|
+
:root {
|
|
684
|
+
--bg: #0f172a;
|
|
685
|
+
--surface: #1e293b;
|
|
686
|
+
--surface2: #334155;
|
|
687
|
+
--text: #e2e8f0;
|
|
688
|
+
--muted: #94a3b8;
|
|
689
|
+
--accent: #0ea5e9;
|
|
690
|
+
--accent2: #8b5cf6;
|
|
691
|
+
--border: #475569;
|
|
692
|
+
--success: #22c55e;
|
|
693
|
+
--warning: #f59e0b;
|
|
694
|
+
--danger: #ef4444;
|
|
695
|
+
}
|
|
696
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
697
|
+
body {
|
|
698
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
699
|
+
background: var(--bg);
|
|
700
|
+
color: var(--text);
|
|
701
|
+
line-height: 1.6;
|
|
702
|
+
padding: 2rem;
|
|
703
|
+
max-width: 1200px;
|
|
704
|
+
margin: 0 auto;
|
|
705
|
+
}
|
|
706
|
+
h1 {
|
|
707
|
+
font-size: 1.75rem;
|
|
708
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
709
|
+
-webkit-background-clip: text;
|
|
710
|
+
-webkit-text-fill-color: transparent;
|
|
711
|
+
margin-bottom: 0.25rem;
|
|
712
|
+
}
|
|
713
|
+
.subtitle { color: var(--muted); font-size: 0.85rem; margin-bottom: 2rem; }
|
|
714
|
+
h2 {
|
|
715
|
+
font-size: 1.15rem;
|
|
716
|
+
color: var(--text);
|
|
717
|
+
border-bottom: 1px solid var(--border);
|
|
718
|
+
padding-bottom: 0.5rem;
|
|
719
|
+
margin: 2rem 0 1rem;
|
|
720
|
+
}
|
|
721
|
+
.stats-grid {
|
|
722
|
+
display: grid;
|
|
723
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
724
|
+
gap: 1rem;
|
|
725
|
+
margin-bottom: 1.5rem;
|
|
726
|
+
}
|
|
727
|
+
.stat-card {
|
|
728
|
+
background: var(--surface);
|
|
729
|
+
border: 1px solid var(--border);
|
|
730
|
+
border-radius: 8px;
|
|
731
|
+
padding: 1rem;
|
|
732
|
+
text-align: center;
|
|
733
|
+
}
|
|
734
|
+
.stat-card .value {
|
|
735
|
+
font-size: 1.75rem;
|
|
736
|
+
font-weight: 700;
|
|
737
|
+
color: var(--accent);
|
|
738
|
+
}
|
|
739
|
+
.stat-card .label {
|
|
740
|
+
font-size: 0.8rem;
|
|
741
|
+
color: var(--muted);
|
|
742
|
+
text-transform: uppercase;
|
|
743
|
+
letter-spacing: 0.05em;
|
|
744
|
+
}
|
|
745
|
+
.badge {
|
|
746
|
+
display: inline-block;
|
|
747
|
+
padding: 0.15rem 0.5rem;
|
|
748
|
+
border-radius: 9999px;
|
|
749
|
+
font-size: 0.7rem;
|
|
750
|
+
font-weight: 600;
|
|
751
|
+
color: #fff;
|
|
752
|
+
}
|
|
753
|
+
table {
|
|
754
|
+
width: 100%;
|
|
755
|
+
border-collapse: collapse;
|
|
756
|
+
margin-bottom: 1rem;
|
|
757
|
+
}
|
|
758
|
+
th, td {
|
|
759
|
+
text-align: left;
|
|
760
|
+
padding: 0.5rem 0.75rem;
|
|
761
|
+
border-bottom: 1px solid var(--border);
|
|
762
|
+
font-size: 0.85rem;
|
|
763
|
+
}
|
|
764
|
+
th {
|
|
765
|
+
color: var(--muted);
|
|
766
|
+
font-weight: 600;
|
|
767
|
+
font-size: 0.75rem;
|
|
768
|
+
text-transform: uppercase;
|
|
769
|
+
letter-spacing: 0.05em;
|
|
770
|
+
}
|
|
771
|
+
tr:hover { background: var(--surface); }
|
|
772
|
+
.bar-container {
|
|
773
|
+
display: flex;
|
|
774
|
+
align-items: center;
|
|
775
|
+
gap: 0.5rem;
|
|
776
|
+
}
|
|
777
|
+
.bar {
|
|
778
|
+
height: 8px;
|
|
779
|
+
border-radius: 4px;
|
|
780
|
+
background: var(--accent);
|
|
781
|
+
min-width: 2px;
|
|
782
|
+
}
|
|
783
|
+
.warning-list {
|
|
784
|
+
list-style: none;
|
|
785
|
+
padding: 0;
|
|
786
|
+
}
|
|
787
|
+
.warning-list li {
|
|
788
|
+
padding: 0.5rem 0.75rem;
|
|
789
|
+
margin-bottom: 0.5rem;
|
|
790
|
+
background: var(--surface);
|
|
791
|
+
border-left: 3px solid var(--warning);
|
|
792
|
+
border-radius: 0 4px 4px 0;
|
|
793
|
+
font-size: 0.85rem;
|
|
794
|
+
}
|
|
795
|
+
.page-group { margin-bottom: 1.5rem; }
|
|
796
|
+
.page-group-title {
|
|
797
|
+
font-size: 0.85rem;
|
|
798
|
+
font-weight: 600;
|
|
799
|
+
color: var(--accent);
|
|
800
|
+
text-transform: capitalize;
|
|
801
|
+
margin-bottom: 0.5rem;
|
|
802
|
+
}
|
|
803
|
+
.page-list {
|
|
804
|
+
display: grid;
|
|
805
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
806
|
+
gap: 0.5rem;
|
|
807
|
+
}
|
|
808
|
+
.page-item {
|
|
809
|
+
background: var(--surface);
|
|
810
|
+
border: 1px solid var(--border);
|
|
811
|
+
border-radius: 6px;
|
|
812
|
+
padding: 0.5rem 0.75rem;
|
|
813
|
+
font-size: 0.8rem;
|
|
814
|
+
overflow: hidden;
|
|
815
|
+
text-overflow: ellipsis;
|
|
816
|
+
white-space: nowrap;
|
|
817
|
+
}
|
|
818
|
+
.page-item .path { color: var(--muted); font-size: 0.7rem; }
|
|
819
|
+
.empty { color: var(--muted); font-style: italic; font-size: 0.85rem; }
|
|
820
|
+
input[type="text"] {
|
|
821
|
+
width: 100%;
|
|
822
|
+
padding: 0.5rem 0.75rem;
|
|
823
|
+
background: var(--surface);
|
|
824
|
+
border: 1px solid var(--border);
|
|
825
|
+
border-radius: 6px;
|
|
826
|
+
color: var(--text);
|
|
827
|
+
font-size: 0.85rem;
|
|
828
|
+
margin-bottom: 1rem;
|
|
829
|
+
outline: none;
|
|
830
|
+
}
|
|
831
|
+
input[type="text"]:focus { border-color: var(--accent); }
|
|
832
|
+
.section { margin-bottom: 1rem; }
|
|
833
|
+
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; text-align: center; }
|
|
834
|
+
</style>
|
|
835
|
+
</head>
|
|
836
|
+
<body>
|
|
837
|
+
<h1>SwarmVault Graph Report</h1>
|
|
838
|
+
<p class="subtitle">Generated ${htmlEscape(report?.generatedAt ?? graph.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString())}</p>
|
|
839
|
+
|
|
840
|
+
<div class="stats-grid">
|
|
841
|
+
<div class="stat-card"><div class="value">${overview.nodes}</div><div class="label">Nodes</div></div>
|
|
842
|
+
<div class="stat-card"><div class="value">${overview.edges}</div><div class="label">Edges</div></div>
|
|
843
|
+
<div class="stat-card"><div class="value">${overview.pages}</div><div class="label">Pages</div></div>
|
|
844
|
+
<div class="stat-card"><div class="value">${overview.communities}</div><div class="label">Communities</div></div>
|
|
845
|
+
<div class="stat-card"><div class="value">${graph.sources.length}</div><div class="label">Sources</div></div>
|
|
846
|
+
<div class="stat-card"><div class="value">${(graph.hyperedges ?? []).length}</div><div class="label">Hyperedges</div></div>
|
|
847
|
+
</div>
|
|
848
|
+
|
|
849
|
+
<h2>Node Types</h2>
|
|
850
|
+
<table>
|
|
851
|
+
<thead><tr><th>Type</th><th>Count</th><th></th></tr></thead>
|
|
852
|
+
<tbody>
|
|
853
|
+
${sortedNodeTypes.map(([type, count]) => {
|
|
854
|
+
const maxCount = sortedNodeTypes[0]?.[1] ?? 1;
|
|
855
|
+
const pct = Math.round(count / maxCount * 100);
|
|
856
|
+
return `<tr><td><span class="badge" style="background:${nodeTypeColor(type)}">${htmlEscape(type)}</span></td><td>${count}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:${nodeTypeColor(type)}"></div></div></td></tr>`;
|
|
857
|
+
}).join("\n")}
|
|
858
|
+
</tbody>
|
|
859
|
+
</table>
|
|
860
|
+
|
|
861
|
+
<h2>Edge Relations</h2>
|
|
862
|
+
<table>
|
|
863
|
+
<thead><tr><th>Relation</th><th>Count</th><th></th></tr></thead>
|
|
864
|
+
<tbody>
|
|
865
|
+
${sortedEdgeRelations.map(([relation, count]) => {
|
|
866
|
+
const maxCount = sortedEdgeRelations[0]?.[1] ?? 1;
|
|
867
|
+
const pct = Math.round(count / maxCount * 100);
|
|
868
|
+
return `<tr><td>${htmlEscape(relation)}</td><td>${count}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%"></div></div></td></tr>`;
|
|
869
|
+
}).join("\n")}
|
|
870
|
+
</tbody>
|
|
871
|
+
</table>
|
|
872
|
+
|
|
873
|
+
${godNodes.length ? `<h2>God Nodes (Highest Connectivity)</h2>
|
|
874
|
+
<table>
|
|
875
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th><th></th></tr></thead>
|
|
876
|
+
<tbody>
|
|
877
|
+
${godNodes.map((node) => {
|
|
878
|
+
const maxDegree = godNodes[0]?.degree ?? 1;
|
|
879
|
+
const pct = Math.round((node.degree ?? 0) / maxDegree * 100);
|
|
880
|
+
return `<tr><td>${htmlEscape(node.label)}</td><td>${node.degree ?? 0}</td><td>${(node.bridgeScore ?? 0).toFixed(2)}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:var(--accent2)"></div></div></td></tr>`;
|
|
881
|
+
}).join("\n")}
|
|
882
|
+
</tbody>
|
|
883
|
+
</table>` : ""}
|
|
884
|
+
|
|
885
|
+
${bridgeNodes.length ? `<h2>Bridge Nodes</h2>
|
|
886
|
+
<table>
|
|
887
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th></tr></thead>
|
|
888
|
+
<tbody>
|
|
889
|
+
${bridgeNodes.map((node) => `<tr><td>${htmlEscape(node.label)}</td><td>${node.degree ?? 0}</td><td>${(node.bridgeScore ?? 0).toFixed(2)}</td></tr>`).join("\n")}
|
|
890
|
+
</tbody>
|
|
891
|
+
</table>` : ""}
|
|
892
|
+
|
|
893
|
+
${sortedCommunities2.length ? `<h2>Communities</h2>
|
|
894
|
+
<table>
|
|
895
|
+
<thead><tr><th>Label</th><th>Nodes</th><th></th></tr></thead>
|
|
896
|
+
<tbody>
|
|
897
|
+
${sortedCommunities2.map((c) => {
|
|
898
|
+
const maxSize = sortedCommunities2[0]?.nodeIds.length ?? 1;
|
|
899
|
+
const pct = Math.round(c.nodeIds.length / maxSize * 100);
|
|
900
|
+
return `<tr><td>${htmlEscape(c.label)}</td><td>${c.nodeIds.length}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%;background:var(--success)"></div></div></td></tr>`;
|
|
901
|
+
}).join("\n")}
|
|
902
|
+
</tbody>
|
|
903
|
+
</table>` : ""}
|
|
904
|
+
|
|
905
|
+
${warnings.length ? `<h2>Warnings</h2>
|
|
906
|
+
<ul class="warning-list">
|
|
907
|
+
${warnings.map((w) => `<li>${htmlEscape(w)}</li>`).join("\n")}
|
|
908
|
+
</ul>` : ""}
|
|
909
|
+
|
|
910
|
+
<h2>Pages</h2>
|
|
911
|
+
<input type="text" id="page-filter" placeholder="Filter pages..." />
|
|
912
|
+
<div id="pages-container">
|
|
913
|
+
${[...pagesByKind.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(
|
|
914
|
+
([kind, pages]) => `<div class="page-group" data-kind="${htmlEscape(kind)}">
|
|
915
|
+
<div class="page-group-title">${htmlEscape(kind)} (${pages.length})</div>
|
|
916
|
+
<div class="page-list">
|
|
917
|
+
${pages.sort((a, b) => a.title.localeCompare(b.title)).map(
|
|
918
|
+
(p) => `<div class="page-item" data-title="${htmlEscape(p.title.toLowerCase())}"><strong>${htmlEscape(p.title)}</strong><div class="path">${htmlEscape(p.path)}</div></div>`
|
|
919
|
+
).join("\n ")}
|
|
920
|
+
</div>
|
|
921
|
+
</div>`
|
|
922
|
+
).join("\n")}
|
|
923
|
+
</div>
|
|
924
|
+
|
|
925
|
+
<footer>Generated by SwarmVault · ${graph.nodes.length} nodes · ${graph.edges.length} edges · ${graph.pages.length} pages</footer>
|
|
926
|
+
|
|
927
|
+
<script>
|
|
928
|
+
document.getElementById("page-filter").addEventListener("input", function(e) {
|
|
929
|
+
var query = e.target.value.toLowerCase();
|
|
930
|
+
document.querySelectorAll(".page-item").forEach(function(el) {
|
|
931
|
+
el.style.display = el.getAttribute("data-title").includes(query) ? "" : "none";
|
|
932
|
+
});
|
|
933
|
+
document.querySelectorAll(".page-group").forEach(function(group) {
|
|
934
|
+
var visible = group.querySelectorAll('.page-item[style=""], .page-item:not([style])').length;
|
|
935
|
+
group.style.display = visible > 0 || !query ? "" : "none";
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
</script>
|
|
939
|
+
</body>
|
|
940
|
+
</html>`;
|
|
941
|
+
}
|
|
942
|
+
|
|
590
943
|
// src/graph-export.ts
|
|
591
944
|
var NODE_COLORS = {
|
|
592
945
|
source: "#f59e0b",
|
|
@@ -1220,6 +1573,14 @@ async function exportGraphFormat(rootDir, format, outputPath) {
|
|
|
1220
1573
|
const resolvedPath = await writeGraphExport(outputPath, rendered);
|
|
1221
1574
|
return { format, outputPath: resolvedPath };
|
|
1222
1575
|
}
|
|
1576
|
+
async function exportGraphReportHtml(rootDir, outputPath) {
|
|
1577
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
1578
|
+
const graph = await loadGraph(rootDir);
|
|
1579
|
+
const report = await readJsonFile(path2.join(paths.wikiDir, "graph", "report.json"));
|
|
1580
|
+
const html = renderGraphReportHtml(graph, report);
|
|
1581
|
+
const resolvedPath = await writeGraphExport(outputPath, html);
|
|
1582
|
+
return { format: "report", outputPath: resolvedPath };
|
|
1583
|
+
}
|
|
1223
1584
|
function safeFileName(label) {
|
|
1224
1585
|
return label.replace(/[\\/*?:"<>|#^[\]]/g, "").replace(/\s+/g, " ").trim().slice(0, 200) || "unnamed";
|
|
1225
1586
|
}
|
|
@@ -1233,14 +1594,27 @@ function deduplicateFileName(baseName, used) {
|
|
|
1233
1594
|
used.add(name);
|
|
1234
1595
|
return name;
|
|
1235
1596
|
}
|
|
1236
|
-
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1597
|
+
function typePluralDir(nodeType) {
|
|
1598
|
+
const map = {
|
|
1599
|
+
source: "sources",
|
|
1600
|
+
module: "modules",
|
|
1601
|
+
symbol: "symbols",
|
|
1602
|
+
concept: "concepts",
|
|
1603
|
+
entity: "entities",
|
|
1604
|
+
rationale: "rationales"
|
|
1605
|
+
};
|
|
1606
|
+
return map[nodeType] ?? "other";
|
|
1607
|
+
}
|
|
1608
|
+
function obsidianNodeSlug(node, pageById2) {
|
|
1609
|
+
if (node.pageId) {
|
|
1610
|
+
const page = pageById2.get(node.pageId);
|
|
1611
|
+
if (page) return path2.basename(page.path, ".md");
|
|
1612
|
+
}
|
|
1613
|
+
return slugify(node.label);
|
|
1614
|
+
}
|
|
1615
|
+
function buildAdjacency(edges) {
|
|
1242
1616
|
const adjacency = /* @__PURE__ */ new Map();
|
|
1243
|
-
for (const edge of
|
|
1617
|
+
for (const edge of edges) {
|
|
1244
1618
|
if (!adjacency.has(edge.source)) adjacency.set(edge.source, []);
|
|
1245
1619
|
if (!adjacency.has(edge.target)) adjacency.set(edge.target, []);
|
|
1246
1620
|
adjacency.get(edge.source).push({
|
|
@@ -1258,44 +1632,164 @@ async function exportObsidianVault(rootDir, outputDir) {
|
|
|
1258
1632
|
direction: "in"
|
|
1259
1633
|
});
|
|
1260
1634
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1635
|
+
return adjacency;
|
|
1636
|
+
}
|
|
1637
|
+
async function listFilesRecursive2(dir, base = "") {
|
|
1638
|
+
const results = [];
|
|
1639
|
+
let entries;
|
|
1640
|
+
try {
|
|
1641
|
+
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
1642
|
+
} catch {
|
|
1643
|
+
return results;
|
|
1266
1644
|
}
|
|
1267
|
-
|
|
1645
|
+
for (const entry of entries) {
|
|
1646
|
+
const rel = base ? `${base}/${entry.name}` : entry.name;
|
|
1647
|
+
if (entry.isDirectory()) {
|
|
1648
|
+
results.push(...await listFilesRecursive2(path2.join(dir, entry.name), rel));
|
|
1649
|
+
} else {
|
|
1650
|
+
results.push(rel);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return results;
|
|
1654
|
+
}
|
|
1655
|
+
function connectionsSection(nodeIds, adjacency, nodesById, wikilinkTarget) {
|
|
1656
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1657
|
+
const lines = [];
|
|
1658
|
+
for (const nodeId of nodeIds) {
|
|
1659
|
+
for (const entry of adjacency.get(nodeId) ?? []) {
|
|
1660
|
+
const key = `${entry.neighborId}:${entry.relation}`;
|
|
1661
|
+
if (seen.has(key)) continue;
|
|
1662
|
+
seen.add(key);
|
|
1663
|
+
const neighbor = nodesById.get(entry.neighborId);
|
|
1664
|
+
if (!neighbor) continue;
|
|
1665
|
+
const target = wikilinkTarget.get(entry.neighborId);
|
|
1666
|
+
if (!target) continue;
|
|
1667
|
+
lines.push(`- [[${target}|${neighbor.label}]] \u2014 ${entry.relation} (${entry.evidenceClass}, ${entry.confidence.toFixed(2)})`);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return lines;
|
|
1671
|
+
}
|
|
1672
|
+
async function exportObsidianVault(rootDir, outputDir) {
|
|
1673
|
+
const graph = await loadGraph(rootDir);
|
|
1674
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
1675
|
+
const resolvedOutputDir = path2.resolve(outputDir);
|
|
1676
|
+
await ensureDir(resolvedOutputDir);
|
|
1677
|
+
const nodesById = graphNodeById(graph);
|
|
1678
|
+
const pageById2 = graphPageById(graph);
|
|
1679
|
+
const communities = sortedCommunities(graph);
|
|
1680
|
+
const adjacency = buildAdjacency(graph.edges);
|
|
1681
|
+
const nodesByPageId = /* @__PURE__ */ new Map();
|
|
1268
1682
|
for (const node of graph.nodes) {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
if (
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
const neighborFile = nodeFileName.get(neighbor.neighborId) ?? safeFileName(neighborNode.label);
|
|
1290
|
-
lines.push(`- [[${neighborFile}]] \u2014 ${neighbor.relation} (${neighbor.evidenceClass}, ${neighbor.confidence.toFixed(2)})`);
|
|
1683
|
+
if (node.pageId && pageById2.has(node.pageId)) {
|
|
1684
|
+
const list = nodesByPageId.get(node.pageId) ?? [];
|
|
1685
|
+
list.push(node);
|
|
1686
|
+
nodesByPageId.set(node.pageId, list);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
const orphanNodes = graph.nodes.filter((node) => !node.pageId || !pageById2.has(node.pageId));
|
|
1690
|
+
const usedOrphanSlugs = /* @__PURE__ */ new Set();
|
|
1691
|
+
const orphanFilePath = /* @__PURE__ */ new Map();
|
|
1692
|
+
for (const node of [...orphanNodes].sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id))) {
|
|
1693
|
+
const slug = deduplicateFileName(obsidianNodeSlug(node, pageById2), usedOrphanSlugs);
|
|
1694
|
+
orphanFilePath.set(node.id, `graph/nodes/${typePluralDir(node.type)}/${slug}.md`);
|
|
1695
|
+
}
|
|
1696
|
+
const wikilinkTarget = /* @__PURE__ */ new Map();
|
|
1697
|
+
for (const node of graph.nodes) {
|
|
1698
|
+
if (node.pageId) {
|
|
1699
|
+
const page = pageById2.get(node.pageId);
|
|
1700
|
+
if (page) {
|
|
1701
|
+
wikilinkTarget.set(node.id, page.path.replace(/\.md$/, ""));
|
|
1702
|
+
continue;
|
|
1291
1703
|
}
|
|
1292
|
-
lines.push("");
|
|
1293
1704
|
}
|
|
1294
|
-
|
|
1705
|
+
const orphanPath = orphanFilePath.get(node.id);
|
|
1706
|
+
if (orphanPath) {
|
|
1707
|
+
wikilinkTarget.set(node.id, orphanPath.replace(/\.md$/, ""));
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
let fileCount = 0;
|
|
1711
|
+
const wikiFiles = await listFilesRecursive2(paths.wikiDir);
|
|
1712
|
+
const pageByPath = new Map(graph.pages.map((p) => [p.path, p]));
|
|
1713
|
+
for (const relPath of wikiFiles) {
|
|
1714
|
+
if (!relPath.endsWith(".md")) continue;
|
|
1715
|
+
const srcFile = path2.join(paths.wikiDir, relPath);
|
|
1716
|
+
const destFile = path2.join(resolvedOutputDir, relPath);
|
|
1717
|
+
await ensureDir(path2.dirname(destFile));
|
|
1718
|
+
let rawContent;
|
|
1719
|
+
try {
|
|
1720
|
+
rawContent = await fs2.readFile(srcFile, "utf8");
|
|
1721
|
+
} catch {
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
const matchingPage = pageByPath.get(relPath);
|
|
1725
|
+
const pageNodes = matchingPage ? nodesByPageId.get(matchingPage.id) ?? [] : [];
|
|
1726
|
+
const parsed = matter(rawContent);
|
|
1727
|
+
const data = parsed.data;
|
|
1728
|
+
if (pageNodes.length > 0) {
|
|
1729
|
+
const primaryNode = pageNodes[0];
|
|
1730
|
+
if (primaryNode.communityId) {
|
|
1731
|
+
data.graph_community = primaryNode.communityId;
|
|
1732
|
+
}
|
|
1733
|
+
const title = data.title ?? "";
|
|
1734
|
+
const nodeAliases = pageNodes.map((n) => n.label).filter((label) => label.toLowerCase() !== title.toLowerCase());
|
|
1735
|
+
const existingAliases = Array.isArray(data.aliases) ? data.aliases : [];
|
|
1736
|
+
const mergedAliases = [.../* @__PURE__ */ new Set([...existingAliases, ...nodeAliases])];
|
|
1737
|
+
if (mergedAliases.length > 0) {
|
|
1738
|
+
data.aliases = mergedAliases;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
let outputContent = matter.stringify(parsed.content, data);
|
|
1742
|
+
if (pageNodes.length > 0) {
|
|
1743
|
+
const connLines = connectionsSection(
|
|
1744
|
+
pageNodes.map((n) => n.id),
|
|
1745
|
+
adjacency,
|
|
1746
|
+
nodesById,
|
|
1747
|
+
wikilinkTarget
|
|
1748
|
+
);
|
|
1749
|
+
if (connLines.length > 0) {
|
|
1750
|
+
outputContent = `${outputContent.trimEnd()}
|
|
1751
|
+
|
|
1752
|
+
## Graph Connections
|
|
1753
|
+
|
|
1754
|
+
${connLines.join("\n")}
|
|
1755
|
+
`;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
await fs2.writeFile(destFile, outputContent, "utf8");
|
|
1759
|
+
fileCount++;
|
|
1760
|
+
}
|
|
1761
|
+
for (const node of orphanNodes) {
|
|
1762
|
+
const relPath = orphanFilePath.get(node.id);
|
|
1763
|
+
const destFile = path2.join(resolvedOutputDir, relPath);
|
|
1764
|
+
await ensureDir(path2.dirname(destFile));
|
|
1765
|
+
const slug = path2.basename(relPath, ".md");
|
|
1766
|
+
const aliases = node.label !== slug ? [node.label] : [];
|
|
1767
|
+
const frontmatter = {
|
|
1768
|
+
id: node.id,
|
|
1769
|
+
type: node.type,
|
|
1770
|
+
community: node.communityId ?? null,
|
|
1771
|
+
confidence: node.confidence ?? null,
|
|
1772
|
+
source_class: node.sourceClass ?? null,
|
|
1773
|
+
tags: node.tags ?? []
|
|
1774
|
+
};
|
|
1775
|
+
if (aliases.length > 0) {
|
|
1776
|
+
frontmatter.aliases = aliases;
|
|
1777
|
+
}
|
|
1778
|
+
const lines = [`# ${node.label}`, ""];
|
|
1779
|
+
const connLines = connectionsSection([node.id], adjacency, nodesById, wikilinkTarget);
|
|
1780
|
+
if (connLines.length > 0) {
|
|
1781
|
+
lines.push("## Connections", "", ...connLines, "");
|
|
1782
|
+
}
|
|
1783
|
+
const content = matter.stringify(lines.join("\n"), frontmatter);
|
|
1784
|
+
await fs2.writeFile(destFile, content, "utf8");
|
|
1295
1785
|
fileCount++;
|
|
1296
1786
|
}
|
|
1297
1787
|
const usedCommunityFileNames = /* @__PURE__ */ new Set();
|
|
1298
1788
|
for (const community of communities) {
|
|
1789
|
+
const wikiCommunityPage = graph.pages.find(
|
|
1790
|
+
(p) => p.kind === "community_summary" && p.nodeIds.some((nid) => community.nodeIds.includes(nid))
|
|
1791
|
+
);
|
|
1792
|
+
if (wikiCommunityPage) continue;
|
|
1299
1793
|
const memberNodes = community.nodeIds.map((id) => nodesById.get(id)).filter((n2) => Boolean(n2));
|
|
1300
1794
|
const memberIdSet = new Set(community.nodeIds);
|
|
1301
1795
|
let internalEdges = 0;
|
|
@@ -1314,35 +1808,102 @@ async function exportObsidianVault(rootDir, outputDir) {
|
|
|
1314
1808
|
return nbNode && nbNode.communityId !== community.id;
|
|
1315
1809
|
});
|
|
1316
1810
|
});
|
|
1317
|
-
const
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
`node_count: ${memberNodes.length}`,
|
|
1322
|
-
`cohesion: ${cohesion.toFixed(4)}`,
|
|
1323
|
-
"---",
|
|
1324
|
-
"",
|
|
1325
|
-
`# ${community.label}`,
|
|
1326
|
-
"",
|
|
1327
|
-
"## Members",
|
|
1328
|
-
""
|
|
1329
|
-
];
|
|
1811
|
+
const communitySlug = deduplicateFileName(safeFileName(community.label), usedCommunityFileNames);
|
|
1812
|
+
const destFile = path2.join(resolvedOutputDir, "graph", "communities", `${communitySlug}.md`);
|
|
1813
|
+
await ensureDir(path2.dirname(destFile));
|
|
1814
|
+
const lines = [`# ${community.label}`, "", "## Members", ""];
|
|
1330
1815
|
for (const member of memberNodes) {
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1816
|
+
const target = wikilinkTarget.get(member.id);
|
|
1817
|
+
if (target) {
|
|
1818
|
+
lines.push(`- [[${target}|${member.label}]]`);
|
|
1819
|
+
} else {
|
|
1820
|
+
lines.push(`- ${member.label}`);
|
|
1821
|
+
}
|
|
1333
1822
|
}
|
|
1334
1823
|
lines.push("");
|
|
1335
1824
|
if (bridgeNodes.length > 0) {
|
|
1336
1825
|
lines.push("## Bridge Nodes", "");
|
|
1337
1826
|
for (const bridge of bridgeNodes) {
|
|
1338
|
-
const
|
|
1339
|
-
|
|
1827
|
+
const target = wikilinkTarget.get(bridge.id);
|
|
1828
|
+
if (target) {
|
|
1829
|
+
lines.push(`- [[${target}|${bridge.label}]]`);
|
|
1830
|
+
} else {
|
|
1831
|
+
lines.push(`- ${bridge.label}`);
|
|
1832
|
+
}
|
|
1340
1833
|
}
|
|
1341
1834
|
lines.push("");
|
|
1342
1835
|
}
|
|
1343
|
-
|
|
1836
|
+
const frontmatter = {
|
|
1837
|
+
id: community.id,
|
|
1838
|
+
node_count: memberNodes.length,
|
|
1839
|
+
cohesion: Number(cohesion.toFixed(4))
|
|
1840
|
+
};
|
|
1841
|
+
const content = matter.stringify(lines.join("\n"), frontmatter);
|
|
1842
|
+
await fs2.writeFile(destFile, content, "utf8");
|
|
1344
1843
|
fileCount++;
|
|
1345
1844
|
}
|
|
1845
|
+
const outputsAssetsDir = path2.join(paths.wikiDir, "outputs", "assets");
|
|
1846
|
+
try {
|
|
1847
|
+
const assetFiles = await listFilesRecursive2(outputsAssetsDir);
|
|
1848
|
+
for (const relAsset of assetFiles) {
|
|
1849
|
+
const src = path2.join(outputsAssetsDir, relAsset);
|
|
1850
|
+
const dest = path2.join(resolvedOutputDir, "outputs", "assets", relAsset);
|
|
1851
|
+
await ensureDir(path2.dirname(dest));
|
|
1852
|
+
await fs2.copyFile(src, dest);
|
|
1853
|
+
fileCount++;
|
|
1854
|
+
}
|
|
1855
|
+
} catch {
|
|
1856
|
+
}
|
|
1857
|
+
try {
|
|
1858
|
+
const rawAssetFiles = await listFilesRecursive2(paths.rawAssetsDir);
|
|
1859
|
+
for (const relAsset of rawAssetFiles) {
|
|
1860
|
+
const src = path2.join(paths.rawAssetsDir, relAsset);
|
|
1861
|
+
const dest = path2.join(resolvedOutputDir, "raw", "assets", relAsset);
|
|
1862
|
+
await ensureDir(path2.dirname(dest));
|
|
1863
|
+
await fs2.copyFile(src, dest);
|
|
1864
|
+
fileCount++;
|
|
1865
|
+
}
|
|
1866
|
+
} catch {
|
|
1867
|
+
}
|
|
1868
|
+
const obsidianDir = path2.join(resolvedOutputDir, ".obsidian");
|
|
1869
|
+
await ensureDir(obsidianDir);
|
|
1870
|
+
const projectIds = Object.keys(
|
|
1871
|
+
graph.pages.reduce(
|
|
1872
|
+
(acc, page) => {
|
|
1873
|
+
for (const pid of page.projectIds) acc[pid] = true;
|
|
1874
|
+
return acc;
|
|
1875
|
+
},
|
|
1876
|
+
{}
|
|
1877
|
+
)
|
|
1878
|
+
);
|
|
1879
|
+
const colorGroups = projectIds.map((pid, index) => ({
|
|
1880
|
+
query: `tag:#project/${pid}`,
|
|
1881
|
+
color: ["#0ea5e9", "#22c55e", "#f59e0b", "#8b5cf6", "#fb7185", "#14b8a6"][index % 6]
|
|
1882
|
+
}));
|
|
1883
|
+
await fs2.writeFile(
|
|
1884
|
+
path2.join(obsidianDir, "app.json"),
|
|
1885
|
+
JSON.stringify(
|
|
1886
|
+
{ newFileLocation: "folder", newFileFolderPath: "outputs", attachmentFolderPath: "raw/assets", useMarkdownLinks: false },
|
|
1887
|
+
null,
|
|
1888
|
+
2
|
|
1889
|
+
),
|
|
1890
|
+
"utf8"
|
|
1891
|
+
);
|
|
1892
|
+
await fs2.writeFile(
|
|
1893
|
+
path2.join(obsidianDir, "core-plugins.json"),
|
|
1894
|
+
JSON.stringify(["file-explorer", "global-search", "graph", "backlink", "tag-pane", "page-preview", "outline"], null, 2),
|
|
1895
|
+
"utf8"
|
|
1896
|
+
);
|
|
1897
|
+
await fs2.writeFile(
|
|
1898
|
+
path2.join(obsidianDir, "graph.json"),
|
|
1899
|
+
JSON.stringify(
|
|
1900
|
+
{ colorGroups, "collapse-filter": false, search: "", showTags: true, showAttachments: false, showOrphans: true },
|
|
1901
|
+
null,
|
|
1902
|
+
2
|
|
1903
|
+
),
|
|
1904
|
+
"utf8"
|
|
1905
|
+
);
|
|
1906
|
+
fileCount += 3;
|
|
1346
1907
|
return { format: "obsidian", outputPath: resolvedOutputDir, fileCount };
|
|
1347
1908
|
}
|
|
1348
1909
|
async function exportObsidianCanvas(rootDir, outputPath) {
|
|
@@ -1463,7 +2024,7 @@ function nodeMap(graph) {
|
|
|
1463
2024
|
function pageMap(graph) {
|
|
1464
2025
|
return new Map(graph.pages.map((page) => [page.id, page]));
|
|
1465
2026
|
}
|
|
1466
|
-
function
|
|
2027
|
+
function estimateTokens2(text) {
|
|
1467
2028
|
return Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));
|
|
1468
2029
|
}
|
|
1469
2030
|
function estimateCorpusWords(texts) {
|
|
@@ -1500,7 +2061,7 @@ function benchmarkQueryTokens(graph, queryResult, pageContentsById) {
|
|
|
1500
2061
|
const target = nodesById.get(edge.target)?.label ?? edge.target;
|
|
1501
2062
|
lines.push(`EDGE ${source} --${edge.relation}/${edge.evidenceClass}/${edge.confidence.toFixed(2)}--> ${target}`);
|
|
1502
2063
|
}
|
|
1503
|
-
const queryTokens =
|
|
2064
|
+
const queryTokens = estimateTokens2(lines.join("\n"));
|
|
1504
2065
|
return {
|
|
1505
2066
|
question: queryResult.question,
|
|
1506
2067
|
queryTokens,
|
|
@@ -2217,6 +2778,67 @@ function graphDiff(oldGraph, newGraph) {
|
|
|
2217
2778
|
const summary = parts.length ? parts.join("; ") : "No changes";
|
|
2218
2779
|
return { addedNodes, removedNodes, addedEdges, removedEdges, addedPages, removedPages, summary };
|
|
2219
2780
|
}
|
|
2781
|
+
function blastRadius(graph, target, options) {
|
|
2782
|
+
const maxDepth = Math.max(1, Math.min(options?.maxDepth ?? 3, 10));
|
|
2783
|
+
const resolved = resolveNode(graph, target);
|
|
2784
|
+
const moduleNode = resolved?.type === "module" ? resolved : resolved?.moduleId ? graph.nodes.find((n) => n.id === resolved.moduleId) : void 0;
|
|
2785
|
+
if (!moduleNode) {
|
|
2786
|
+
const normalizedTarget = normalizeTarget(target);
|
|
2787
|
+
const candidate = graph.nodes.filter((n) => n.type === "module").find((n) => normalizeTarget(n.label).includes(normalizedTarget) || normalizeTarget(n.id).includes(normalizedTarget));
|
|
2788
|
+
if (!candidate) {
|
|
2789
|
+
return {
|
|
2790
|
+
target,
|
|
2791
|
+
totalAffected: 0,
|
|
2792
|
+
maxDepth,
|
|
2793
|
+
affectedModules: [],
|
|
2794
|
+
summary: `No module found matching "${target}".`
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
return blastRadius(graph, candidate.id, options);
|
|
2798
|
+
}
|
|
2799
|
+
const reverseImports = /* @__PURE__ */ new Map();
|
|
2800
|
+
for (const edge of graph.edges) {
|
|
2801
|
+
if (edge.relation === "imports") {
|
|
2802
|
+
const dependents = reverseImports.get(edge.target) ?? [];
|
|
2803
|
+
dependents.push(edge.source);
|
|
2804
|
+
reverseImports.set(edge.target, dependents);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
const affected = [];
|
|
2808
|
+
const seen = /* @__PURE__ */ new Set([moduleNode.id]);
|
|
2809
|
+
const frontier = [{ id: moduleNode.id, depth: 0 }];
|
|
2810
|
+
const nodes = nodeById(graph);
|
|
2811
|
+
while (frontier.length > 0) {
|
|
2812
|
+
const current = frontier.shift();
|
|
2813
|
+
if (current.depth >= maxDepth) {
|
|
2814
|
+
continue;
|
|
2815
|
+
}
|
|
2816
|
+
for (const dependentId of reverseImports.get(current.id) ?? []) {
|
|
2817
|
+
if (seen.has(dependentId)) {
|
|
2818
|
+
continue;
|
|
2819
|
+
}
|
|
2820
|
+
seen.add(dependentId);
|
|
2821
|
+
const dependentNode = nodes.get(dependentId);
|
|
2822
|
+
const nextDepth = current.depth + 1;
|
|
2823
|
+
affected.push({
|
|
2824
|
+
moduleId: dependentId,
|
|
2825
|
+
label: dependentNode?.label ?? dependentId,
|
|
2826
|
+
depth: nextDepth
|
|
2827
|
+
});
|
|
2828
|
+
frontier.push({ id: dependentId, depth: nextDepth });
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
affected.sort((a, b) => a.depth - b.depth || a.label.localeCompare(b.label));
|
|
2832
|
+
const summary = affected.length ? `Changing "${moduleNode.label}" affects ${affected.length} module${affected.length === 1 ? "" : "s"} (max depth ${maxDepth}).` : `No modules depend on "${moduleNode.label}".`;
|
|
2833
|
+
return {
|
|
2834
|
+
target,
|
|
2835
|
+
resolvedModuleId: moduleNode.id,
|
|
2836
|
+
affectedModules: affected,
|
|
2837
|
+
totalAffected: affected.length,
|
|
2838
|
+
maxDepth,
|
|
2839
|
+
summary
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2220
2842
|
|
|
2221
2843
|
// src/hooks.ts
|
|
2222
2844
|
import fs4 from "fs/promises";
|
|
@@ -2361,7 +2983,7 @@ import fs11 from "fs/promises";
|
|
|
2361
2983
|
import path12 from "path";
|
|
2362
2984
|
import { pathToFileURL } from "url";
|
|
2363
2985
|
import { Readability } from "@mozilla/readability";
|
|
2364
|
-
import
|
|
2986
|
+
import matter4 from "gray-matter";
|
|
2365
2987
|
import ignore from "ignore";
|
|
2366
2988
|
import { isText } from "istextorbinary";
|
|
2367
2989
|
import { JSDOM as JSDOM2 } from "jsdom";
|
|
@@ -5851,7 +6473,7 @@ function htmlCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
5851
6473
|
}
|
|
5852
6474
|
continue;
|
|
5853
6475
|
}
|
|
5854
|
-
if (tagName
|
|
6476
|
+
if (tagName?.includes("-")) {
|
|
5855
6477
|
if (!seenSymbolNames.has(tagName)) {
|
|
5856
6478
|
seenSymbolNames.add(tagName);
|
|
5857
6479
|
draftSymbols.push({
|
|
@@ -7618,6 +8240,7 @@ import { parse as parseCsvSync } from "csv-parse/sync";
|
|
|
7618
8240
|
import { strFromU8, unzipSync } from "fflate";
|
|
7619
8241
|
import { JSDOM } from "jsdom";
|
|
7620
8242
|
import TurndownService from "turndown";
|
|
8243
|
+
import { fetchTranscript } from "youtube-transcript-plus";
|
|
7621
8244
|
import { z } from "zod";
|
|
7622
8245
|
|
|
7623
8246
|
// src/markdown-ast.ts
|
|
@@ -7816,6 +8439,107 @@ async function extractImageWithVision(rootDir, input) {
|
|
|
7816
8439
|
await attachment.cleanup();
|
|
7817
8440
|
}
|
|
7818
8441
|
}
|
|
8442
|
+
async function extractAudioTranscription(rootDir, input) {
|
|
8443
|
+
let provider;
|
|
8444
|
+
try {
|
|
8445
|
+
provider = await getProviderForTask(rootDir, "audioProvider");
|
|
8446
|
+
} catch (error) {
|
|
8447
|
+
return {
|
|
8448
|
+
artifact: {
|
|
8449
|
+
...extractionMetadata("audio", input.mimeType, "audio_transcription"),
|
|
8450
|
+
warnings: [`Audio transcription unavailable: ${error instanceof Error ? error.message : "provider not configured"}`]
|
|
8451
|
+
}
|
|
8452
|
+
};
|
|
8453
|
+
}
|
|
8454
|
+
if (!provider.capabilities.has("audio") || !provider.transcribeAudio) {
|
|
8455
|
+
return {
|
|
8456
|
+
artifact: {
|
|
8457
|
+
...extractionMetadata("audio", input.mimeType, "audio_transcription"),
|
|
8458
|
+
warnings: [`Audio transcription unavailable for provider ${provider.id}. Configure a provider with audio capability.`]
|
|
8459
|
+
}
|
|
8460
|
+
};
|
|
8461
|
+
}
|
|
8462
|
+
try {
|
|
8463
|
+
const result = await provider.transcribeAudio({
|
|
8464
|
+
mimeType: input.mimeType,
|
|
8465
|
+
bytes: input.bytes,
|
|
8466
|
+
fileName: input.fileName
|
|
8467
|
+
});
|
|
8468
|
+
const metadata = {};
|
|
8469
|
+
if (result.duration !== void 0) {
|
|
8470
|
+
metadata.duration = String(result.duration);
|
|
8471
|
+
}
|
|
8472
|
+
if (result.language) {
|
|
8473
|
+
metadata.language = result.language;
|
|
8474
|
+
}
|
|
8475
|
+
return {
|
|
8476
|
+
extractedText: result.text || void 0,
|
|
8477
|
+
artifact: {
|
|
8478
|
+
...extractionMetadata("audio", input.mimeType, "audio_transcription"),
|
|
8479
|
+
providerId: provider.id,
|
|
8480
|
+
providerModel: provider.model,
|
|
8481
|
+
metadata: Object.keys(metadata).length ? metadata : void 0
|
|
8482
|
+
}
|
|
8483
|
+
};
|
|
8484
|
+
} catch (error) {
|
|
8485
|
+
return {
|
|
8486
|
+
artifact: {
|
|
8487
|
+
...extractionMetadata("audio", input.mimeType, "audio_transcription"),
|
|
8488
|
+
providerId: provider.id,
|
|
8489
|
+
providerModel: provider.model,
|
|
8490
|
+
warnings: [`Audio transcription failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
|
|
8491
|
+
}
|
|
8492
|
+
};
|
|
8493
|
+
}
|
|
8494
|
+
}
|
|
8495
|
+
async function extractYoutubeTranscript(input) {
|
|
8496
|
+
try {
|
|
8497
|
+
const result = await fetchTranscript(input.videoId, { videoDetails: true });
|
|
8498
|
+
const details = result.videoDetails;
|
|
8499
|
+
const title = details?.title ?? `YouTube ${input.videoId}`;
|
|
8500
|
+
const transcriptText = result.segments?.map((part) => part.text).join(" ") ?? "";
|
|
8501
|
+
const sections = [`# ${title}`];
|
|
8502
|
+
const metaLines = [];
|
|
8503
|
+
if (details?.author) metaLines.push(`**Author:** ${details.author}`);
|
|
8504
|
+
if (details?.lengthSeconds) {
|
|
8505
|
+
const seconds = details.lengthSeconds;
|
|
8506
|
+
const minutes = Math.floor(seconds / 60);
|
|
8507
|
+
const secs = seconds % 60;
|
|
8508
|
+
metaLines.push(`**Duration:** ${minutes}:${String(secs).padStart(2, "0")}`);
|
|
8509
|
+
}
|
|
8510
|
+
if (details?.viewCount) metaLines.push(`**Views:** ${Number(details.viewCount).toLocaleString()}`);
|
|
8511
|
+
metaLines.push(`**URL:** ${input.url}`);
|
|
8512
|
+
if (metaLines.length) {
|
|
8513
|
+
sections.push(metaLines.join("\n"));
|
|
8514
|
+
}
|
|
8515
|
+
if (transcriptText.trim()) {
|
|
8516
|
+
sections.push(`## Transcript
|
|
8517
|
+
|
|
8518
|
+
${transcriptText.trim()}`);
|
|
8519
|
+
}
|
|
8520
|
+
const extractedText = sections.join("\n\n");
|
|
8521
|
+
const metadata = {};
|
|
8522
|
+
if (details?.title) metadata.title = details.title;
|
|
8523
|
+
if (details?.author) metadata.author = details.author;
|
|
8524
|
+
if (details?.lengthSeconds) metadata.duration = String(details.lengthSeconds);
|
|
8525
|
+
if (details?.viewCount) metadata.viewCount = String(details.viewCount);
|
|
8526
|
+
return {
|
|
8527
|
+
title,
|
|
8528
|
+
extractedText: extractedText || void 0,
|
|
8529
|
+
artifact: {
|
|
8530
|
+
...extractionMetadata("youtube", "text/html", "youtube_transcript"),
|
|
8531
|
+
metadata: Object.keys(metadata).length ? metadata : void 0
|
|
8532
|
+
}
|
|
8533
|
+
};
|
|
8534
|
+
} catch (error) {
|
|
8535
|
+
return {
|
|
8536
|
+
artifact: {
|
|
8537
|
+
...extractionMetadata("youtube", "text/html", "youtube_transcript"),
|
|
8538
|
+
warnings: [`YouTube transcript extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
|
|
8539
|
+
}
|
|
8540
|
+
};
|
|
8541
|
+
}
|
|
8542
|
+
}
|
|
7819
8543
|
function normalizePdfMetadata(raw) {
|
|
7820
8544
|
if (!raw || typeof raw !== "object") {
|
|
7821
8545
|
return void 0;
|
|
@@ -9613,7 +10337,7 @@ async function extractSlackExportDirectory(directoryPath) {
|
|
|
9613
10337
|
// src/logs.ts
|
|
9614
10338
|
import fs8 from "fs/promises";
|
|
9615
10339
|
import path8 from "path";
|
|
9616
|
-
import
|
|
10340
|
+
import matter2 from "gray-matter";
|
|
9617
10341
|
async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
|
|
9618
10342
|
const { paths } = await initWorkspace(rootDir);
|
|
9619
10343
|
await ensureDir(paths.sessionsDir);
|
|
@@ -9666,7 +10390,7 @@ async function recordSession(rootDir, input) {
|
|
|
9666
10390
|
token_usage: input.tokenUsage
|
|
9667
10391
|
}).filter(([, value]) => value !== void 0)
|
|
9668
10392
|
);
|
|
9669
|
-
const content =
|
|
10393
|
+
const content = matter2.stringify(
|
|
9670
10394
|
[
|
|
9671
10395
|
`# ${input.operation[0]?.toUpperCase() ?? ""}${input.operation.slice(1)} Session`,
|
|
9672
10396
|
"",
|
|
@@ -9887,7 +10611,7 @@ async function managedSourceWorkingDir(rootDir, sourceId) {
|
|
|
9887
10611
|
// src/watch-state.ts
|
|
9888
10612
|
import fs10 from "fs/promises";
|
|
9889
10613
|
import path11 from "path";
|
|
9890
|
-
import
|
|
10614
|
+
import matter3 from "gray-matter";
|
|
9891
10615
|
function pendingEntryKey(entry) {
|
|
9892
10616
|
return entry.path;
|
|
9893
10617
|
}
|
|
@@ -9999,13 +10723,13 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
9999
10723
|
continue;
|
|
10000
10724
|
}
|
|
10001
10725
|
const raw = await fs10.readFile(absolutePath, "utf8");
|
|
10002
|
-
const parsed =
|
|
10726
|
+
const parsed = matter3(raw);
|
|
10003
10727
|
if (parsed.data.freshness === "stale") {
|
|
10004
10728
|
continue;
|
|
10005
10729
|
}
|
|
10006
10730
|
parsed.data.freshness = "stale";
|
|
10007
10731
|
parsed.data.updated_at = now;
|
|
10008
|
-
await writeFileIfChanged(absolutePath,
|
|
10732
|
+
await writeFileIfChanged(absolutePath, matter3.stringify(parsed.content, parsed.data));
|
|
10009
10733
|
}
|
|
10010
10734
|
return affectedPagePaths;
|
|
10011
10735
|
}
|
|
@@ -10101,6 +10825,9 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
10101
10825
|
if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation" || mimeType === "application/vnd.ms-powerpoint.presentation.macroenabled.12" || mimeType === "application/vnd.ms-powerpoint.template.macroenabled.12" || mimeType === "application/vnd.openxmlformats-officedocument.presentationml.template" || filePath.toLowerCase().endsWith(".pptx") || filePath.toLowerCase().endsWith(".pptm") || filePath.toLowerCase().endsWith(".potx") || filePath.toLowerCase().endsWith(".potm")) {
|
|
10102
10826
|
return "pptx";
|
|
10103
10827
|
}
|
|
10828
|
+
if (mimeType.startsWith("audio/") || /\.(mp3|wav|m4a|ogg|flac|webm|aac|wma)$/i.test(filePath)) {
|
|
10829
|
+
return "audio";
|
|
10830
|
+
}
|
|
10104
10831
|
if (mimeType.startsWith("image/") || isImagePath(filePath)) {
|
|
10105
10832
|
return "image";
|
|
10106
10833
|
}
|
|
@@ -10125,6 +10852,10 @@ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
10125
10852
|
function isImagePath(filePath) {
|
|
10126
10853
|
return IMAGE_EXTENSIONS.has(path12.extname(filePath).toLowerCase());
|
|
10127
10854
|
}
|
|
10855
|
+
var YOUTUBE_URL_PATTERN = /(?:youtube\.com\/watch\?.*v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([\w-]{11})/i;
|
|
10856
|
+
function parseYoutubeVideoId(url) {
|
|
10857
|
+
return url.match(YOUTUBE_URL_PATTERN)?.[1];
|
|
10858
|
+
}
|
|
10128
10859
|
function isStructuredTextMime(mimeType) {
|
|
10129
10860
|
switch (mimeType) {
|
|
10130
10861
|
case "application/json":
|
|
@@ -10392,7 +11123,7 @@ function normalizeSemanticMarkdownList(value) {
|
|
|
10392
11123
|
return items.length ? items : void 0;
|
|
10393
11124
|
}
|
|
10394
11125
|
function semanticMarkdownTitle(fallback, content, filePath) {
|
|
10395
|
-
const parsed =
|
|
11126
|
+
const parsed = matter4(content);
|
|
10396
11127
|
const frontmatterTitle = normalizeSemanticMarkdownScalar(parsed.data.title);
|
|
10397
11128
|
if (frontmatterTitle) {
|
|
10398
11129
|
return frontmatterTitle;
|
|
@@ -10400,7 +11131,7 @@ function semanticMarkdownTitle(fallback, content, filePath) {
|
|
|
10400
11131
|
return titleFromText(fallback, parsed.content, filePath);
|
|
10401
11132
|
}
|
|
10402
11133
|
function semanticMarkdownContent(content) {
|
|
10403
|
-
const parsed =
|
|
11134
|
+
const parsed = matter4(content);
|
|
10404
11135
|
const body = parsed.content.replace(/\r\n?/g, "\n").trim();
|
|
10405
11136
|
const semanticFrontmatter = Object.fromEntries(
|
|
10406
11137
|
MARKDOWN_SEMANTIC_FRONTMATTER_KEYS.flatMap((key) => {
|
|
@@ -10604,7 +11335,7 @@ function markdownFrontmatter(value) {
|
|
|
10604
11335
|
([, rawValue]) => Array.isArray(rawValue) ? rawValue.length > 0 : Boolean(typeof rawValue === "string" ? rawValue.trim() : rawValue)
|
|
10605
11336
|
)
|
|
10606
11337
|
);
|
|
10607
|
-
return
|
|
11338
|
+
return matter4.stringify("", normalized).trimEnd().split("\n").concat([""]);
|
|
10608
11339
|
}
|
|
10609
11340
|
function prepareCapturedMarkdownInput(input) {
|
|
10610
11341
|
return finalizePreparedInput({
|
|
@@ -11422,7 +12153,7 @@ function preparedMatchesManifest(manifest, prepared, contentHash) {
|
|
|
11422
12153
|
return manifest.contentHash === contentHash && manifest.extractionHash === (prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact)) && manifest.semanticHash === (prepared.semanticHash ?? contentHash) && manifest.title === prepared.title && manifest.sourceKind === prepared.sourceKind && manifest.sourceType === prepared.sourceType && manifest.sourceClass === prepared.sourceClass && manifest.language === prepared.language && manifest.mimeType === prepared.mimeType && manifest.repoRelativePath === prepared.repoRelativePath && manifest.sourceGroupId === prepared.sourceGroupId && manifest.sourceGroupTitle === prepared.sourceGroupTitle && manifest.sourcePartKey === prepared.sourcePartKey && manifest.partIndex === prepared.partIndex && manifest.partCount === prepared.partCount && manifest.partTitle === prepared.partTitle && JSON.stringify(manifest.details ?? {}) === JSON.stringify(prepared.details ?? {});
|
|
11423
12154
|
}
|
|
11424
12155
|
function shouldDeferWatchSemanticRefresh(sourceKind) {
|
|
11425
|
-
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "docx" || sourceKind === "epub" || sourceKind === "csv" || sourceKind === "xlsx" || sourceKind === "pptx" || sourceKind === "transcript" || sourceKind === "chat_export" || sourceKind === "email" || sourceKind === "calendar" || sourceKind === "image";
|
|
12156
|
+
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "docx" || sourceKind === "epub" || sourceKind === "csv" || sourceKind === "xlsx" || sourceKind === "pptx" || sourceKind === "transcript" || sourceKind === "chat_export" || sourceKind === "email" || sourceKind === "calendar" || sourceKind === "image" || sourceKind === "audio";
|
|
11426
12157
|
}
|
|
11427
12158
|
function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
|
|
11428
12159
|
return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
|
|
@@ -11913,6 +12644,15 @@ async function prepareFileInputs(rootDir, absoluteInput, repoRoot, sourceClass)
|
|
|
11913
12644
|
title = extracted.title?.trim() || title;
|
|
11914
12645
|
extractedText = extracted.extractedText;
|
|
11915
12646
|
extractionArtifact = extracted.artifact;
|
|
12647
|
+
} else if (sourceKind === "audio") {
|
|
12648
|
+
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
12649
|
+
const extracted = await extractAudioTranscription(rootDir, {
|
|
12650
|
+
mimeType,
|
|
12651
|
+
bytes: payloadBytes,
|
|
12652
|
+
fileName: absoluteInput
|
|
12653
|
+
});
|
|
12654
|
+
extractedText = extracted.extractedText;
|
|
12655
|
+
extractionArtifact = extracted.artifact;
|
|
11916
12656
|
} else {
|
|
11917
12657
|
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
11918
12658
|
}
|
|
@@ -11944,6 +12684,28 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
|
11944
12684
|
}
|
|
11945
12685
|
async function prepareUrlInputs(rootDir, input, options) {
|
|
11946
12686
|
await validateUrlSafety(input);
|
|
12687
|
+
const youtubeVideoId = parseYoutubeVideoId(input);
|
|
12688
|
+
if (youtubeVideoId) {
|
|
12689
|
+
const extracted = await extractYoutubeTranscript({ videoId: youtubeVideoId, url: input });
|
|
12690
|
+
const title2 = extracted.title ?? `YouTube ${youtubeVideoId}`;
|
|
12691
|
+
const extractedText2 = extracted.extractedText;
|
|
12692
|
+
const payloadBytes2 = Buffer.from(extractedText2 ?? "", "utf8");
|
|
12693
|
+
return [
|
|
12694
|
+
finalizePreparedInput({
|
|
12695
|
+
title: title2,
|
|
12696
|
+
originType: "url",
|
|
12697
|
+
sourceKind: "youtube",
|
|
12698
|
+
url: normalizeOriginUrl(input),
|
|
12699
|
+
mimeType: "text/html",
|
|
12700
|
+
storedExtension: ".md",
|
|
12701
|
+
payloadBytes: payloadBytes2,
|
|
12702
|
+
extractedText: extractedText2,
|
|
12703
|
+
extractionArtifact: extracted.artifact,
|
|
12704
|
+
extractionHash: buildExtractionHash(extractedText2, extracted.artifact),
|
|
12705
|
+
details: extracted.artifact.metadata
|
|
12706
|
+
})
|
|
12707
|
+
];
|
|
12708
|
+
}
|
|
11947
12709
|
const response = await fetch(input);
|
|
11948
12710
|
if (!response.ok) {
|
|
11949
12711
|
throw new Error(`Failed to fetch ${input}: ${response.status} ${response.statusText}`);
|
|
@@ -12163,6 +12925,14 @@ async function prepareUrlInputs(rootDir, input, options) {
|
|
|
12163
12925
|
title = extracted.title?.trim() || title;
|
|
12164
12926
|
extractedText = extracted.extractedText;
|
|
12165
12927
|
extractionArtifact = extracted.artifact;
|
|
12928
|
+
} else if (sourceKind === "audio") {
|
|
12929
|
+
const extracted = await extractAudioTranscription(rootDir, {
|
|
12930
|
+
mimeType,
|
|
12931
|
+
bytes: payloadBytes,
|
|
12932
|
+
fileName: inputUrl.pathname
|
|
12933
|
+
});
|
|
12934
|
+
extractedText = extracted.extractedText;
|
|
12935
|
+
extractionArtifact = extracted.artifact;
|
|
12166
12936
|
}
|
|
12167
12937
|
}
|
|
12168
12938
|
return [
|
|
@@ -12750,7 +13520,7 @@ import fs19 from "fs/promises";
|
|
|
12750
13520
|
import path23 from "path";
|
|
12751
13521
|
import Graph from "graphology";
|
|
12752
13522
|
import louvain from "graphology-communities-louvain";
|
|
12753
|
-
import
|
|
13523
|
+
import matter10 from "gray-matter";
|
|
12754
13524
|
import { z as z7 } from "zod";
|
|
12755
13525
|
|
|
12756
13526
|
// src/analysis.ts
|
|
@@ -13186,7 +13956,7 @@ function conflictConfidence(claimA, claimB) {
|
|
|
13186
13956
|
// src/deep-lint.ts
|
|
13187
13957
|
import fs13 from "fs/promises";
|
|
13188
13958
|
import path17 from "path";
|
|
13189
|
-
import
|
|
13959
|
+
import matter5 from "gray-matter";
|
|
13190
13960
|
import { z as z5 } from "zod";
|
|
13191
13961
|
|
|
13192
13962
|
// src/findings.ts
|
|
@@ -13560,7 +14330,7 @@ async function loadContextPages(rootDir, graph) {
|
|
|
13560
14330
|
contextPages.slice(0, 18).map(async (page) => {
|
|
13561
14331
|
const absolutePath = path17.join(paths.wikiDir, page.path);
|
|
13562
14332
|
const raw = await fs13.readFile(absolutePath, "utf8").catch(() => "");
|
|
13563
|
-
const parsed =
|
|
14333
|
+
const parsed = matter5(raw);
|
|
13564
14334
|
return {
|
|
13565
14335
|
id: page.id,
|
|
13566
14336
|
title: page.title,
|
|
@@ -14024,6 +14794,33 @@ async function semanticGraphMatches(rootDir, graph, question, limit = 12) {
|
|
|
14024
14794
|
score: Math.max(0, Number((cosineSimilarity(queryVector, vectors.get(`${item.kind}:${item.id}`) ?? []) * 100).toFixed(2)))
|
|
14025
14795
|
})).filter((match) => match.score >= 18).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, limit);
|
|
14026
14796
|
}
|
|
14797
|
+
async function semanticPageSearch(rootDir, graph, query, limit = 10) {
|
|
14798
|
+
const items = await buildEmbeddableItems(rootDir, graph);
|
|
14799
|
+
const pageItems = items.filter((item) => item.kind === "page");
|
|
14800
|
+
if (!pageItems.length) {
|
|
14801
|
+
return [];
|
|
14802
|
+
}
|
|
14803
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
14804
|
+
if (!provider) {
|
|
14805
|
+
return [];
|
|
14806
|
+
}
|
|
14807
|
+
const [queryVector] = await provider.embedTexts([query]);
|
|
14808
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) {
|
|
14809
|
+
return [];
|
|
14810
|
+
}
|
|
14811
|
+
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
14812
|
+
return pageItems.map((item) => {
|
|
14813
|
+
const page = pageMap2.get(item.id);
|
|
14814
|
+
return {
|
|
14815
|
+
pageId: item.id,
|
|
14816
|
+
path: page?.path ?? "",
|
|
14817
|
+
title: item.label,
|
|
14818
|
+
kind: page?.kind ?? "",
|
|
14819
|
+
status: page?.status ?? "",
|
|
14820
|
+
score: cosineSimilarity(queryVector, vectors.get(`page:${item.id}`) ?? [])
|
|
14821
|
+
};
|
|
14822
|
+
}).filter((result) => result.score >= 0.25 && result.path).sort((left, right) => right.score - left.score).slice(0, limit);
|
|
14823
|
+
}
|
|
14027
14824
|
function distinctScope(left, right) {
|
|
14028
14825
|
const leftSources = new Set(left.sourceIds);
|
|
14029
14826
|
const rightSources = new Set(right.sourceIds);
|
|
@@ -14469,7 +15266,7 @@ function enrichGraph(graph, manifests, analyses, extraSimilarityEdges = []) {
|
|
|
14469
15266
|
}
|
|
14470
15267
|
|
|
14471
15268
|
// src/markdown.ts
|
|
14472
|
-
import
|
|
15269
|
+
import matter6 from "gray-matter";
|
|
14473
15270
|
function uniqueStrings2(values) {
|
|
14474
15271
|
return uniqueBy(values.filter(Boolean), (value) => value);
|
|
14475
15272
|
}
|
|
@@ -14724,7 +15521,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
14724
15521
|
compiledFrom: metadata.compiledFrom,
|
|
14725
15522
|
managedBy: metadata.managedBy
|
|
14726
15523
|
},
|
|
14727
|
-
content:
|
|
15524
|
+
content: matter6.stringify(body, safeFrontmatter(frontmatter))
|
|
14728
15525
|
};
|
|
14729
15526
|
}
|
|
14730
15527
|
function buildModulePage(input) {
|
|
@@ -14874,7 +15671,7 @@ function buildModulePage(input) {
|
|
|
14874
15671
|
compiledFrom: metadata.compiledFrom,
|
|
14875
15672
|
managedBy: metadata.managedBy
|
|
14876
15673
|
},
|
|
14877
|
-
content:
|
|
15674
|
+
content: matter6.stringify(body, frontmatter)
|
|
14878
15675
|
};
|
|
14879
15676
|
}
|
|
14880
15677
|
function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations, existingContent) {
|
|
@@ -14951,7 +15748,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
|
|
|
14951
15748
|
compiledFrom: metadata.compiledFrom,
|
|
14952
15749
|
managedBy: metadata.managedBy
|
|
14953
15750
|
},
|
|
14954
|
-
content:
|
|
15751
|
+
content: matter6.stringify(body, frontmatter)
|
|
14955
15752
|
};
|
|
14956
15753
|
}
|
|
14957
15754
|
function buildIndexPage(pages, schemaHash, metadata, projectPages = []) {
|
|
@@ -15035,7 +15832,7 @@ function buildIndexPage(pages, schemaHash, metadata, projectPages = []) {
|
|
|
15035
15832
|
}
|
|
15036
15833
|
function buildSectionIndex(kind, pages, schemaHash, metadata, projectIds = []) {
|
|
15037
15834
|
const title = kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
15038
|
-
return
|
|
15835
|
+
return matter6.stringify(
|
|
15039
15836
|
[`# ${title}`, "", ...pages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`), ""].join("\n"),
|
|
15040
15837
|
{
|
|
15041
15838
|
page_id: `${kind}:index`,
|
|
@@ -15547,7 +16344,7 @@ function buildGraphReportPage(input) {
|
|
|
15547
16344
|
compiledFrom: input.metadata.compiledFrom,
|
|
15548
16345
|
managedBy: input.metadata.managedBy
|
|
15549
16346
|
},
|
|
15550
|
-
content:
|
|
16347
|
+
content: matter6.stringify(body, frontmatter)
|
|
15551
16348
|
};
|
|
15552
16349
|
}
|
|
15553
16350
|
function buildCommunitySummaryPage(input) {
|
|
@@ -15631,11 +16428,11 @@ function buildCommunitySummaryPage(input) {
|
|
|
15631
16428
|
compiledFrom: input.metadata.compiledFrom,
|
|
15632
16429
|
managedBy: input.metadata.managedBy
|
|
15633
16430
|
},
|
|
15634
|
-
content:
|
|
16431
|
+
content: matter6.stringify(body, frontmatter)
|
|
15635
16432
|
};
|
|
15636
16433
|
}
|
|
15637
16434
|
function buildProjectsIndex(projectPages, schemaHash, metadata) {
|
|
15638
|
-
return
|
|
16435
|
+
return matter6.stringify(
|
|
15639
16436
|
[
|
|
15640
16437
|
"# Projects",
|
|
15641
16438
|
"",
|
|
@@ -15666,7 +16463,7 @@ function buildProjectsIndex(projectPages, schemaHash, metadata) {
|
|
|
15666
16463
|
}
|
|
15667
16464
|
function buildProjectIndex(input) {
|
|
15668
16465
|
const title = `Project: ${input.projectId}`;
|
|
15669
|
-
return
|
|
16466
|
+
return matter6.stringify(
|
|
15670
16467
|
[
|
|
15671
16468
|
`# ${title}`,
|
|
15672
16469
|
"",
|
|
@@ -15783,7 +16580,7 @@ function buildOutputPage(input) {
|
|
|
15783
16580
|
outputFormat: input.outputFormat,
|
|
15784
16581
|
outputAssets
|
|
15785
16582
|
},
|
|
15786
|
-
content:
|
|
16583
|
+
content: matter6.stringify(
|
|
15787
16584
|
(input.outputFormat === "slides" ? [
|
|
15788
16585
|
input.answer,
|
|
15789
16586
|
"",
|
|
@@ -15911,7 +16708,7 @@ function buildExploreHubPage(input) {
|
|
|
15911
16708
|
outputFormat: input.outputFormat,
|
|
15912
16709
|
outputAssets
|
|
15913
16710
|
},
|
|
15914
|
-
content:
|
|
16711
|
+
content: matter6.stringify(
|
|
15915
16712
|
(input.outputFormat === "slides" ? [
|
|
15916
16713
|
`# ${title}`,
|
|
15917
16714
|
"",
|
|
@@ -16177,12 +16974,12 @@ function buildOutputAssetManifest(input) {
|
|
|
16177
16974
|
// src/outputs.ts
|
|
16178
16975
|
import fs16 from "fs/promises";
|
|
16179
16976
|
import path20 from "path";
|
|
16180
|
-
import
|
|
16977
|
+
import matter8 from "gray-matter";
|
|
16181
16978
|
|
|
16182
16979
|
// src/pages.ts
|
|
16183
16980
|
import fs15 from "fs/promises";
|
|
16184
16981
|
import path19 from "path";
|
|
16185
|
-
import
|
|
16982
|
+
import matter7 from "gray-matter";
|
|
16186
16983
|
function normalizeStringArray(value) {
|
|
16187
16984
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16188
16985
|
}
|
|
@@ -16263,7 +17060,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
16263
17060
|
};
|
|
16264
17061
|
}
|
|
16265
17062
|
const content = await fs15.readFile(absolutePath, "utf8");
|
|
16266
|
-
const parsed =
|
|
17063
|
+
const parsed = matter7(content);
|
|
16267
17064
|
return {
|
|
16268
17065
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
16269
17066
|
managedBy: normalizePageManager(parsed.data.managed_by, defaults.managedBy ?? "system"),
|
|
@@ -16297,7 +17094,7 @@ function inferPageKind(relativePath, explicitKind = void 0) {
|
|
|
16297
17094
|
return "index";
|
|
16298
17095
|
}
|
|
16299
17096
|
function parseStoredPage(relativePath, content, defaults = {}) {
|
|
16300
|
-
const parsed =
|
|
17097
|
+
const parsed = matter7(content);
|
|
16301
17098
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16302
17099
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
16303
17100
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
@@ -16352,7 +17149,7 @@ async function loadInsightPages(wikiDir) {
|
|
|
16352
17149
|
for (const absolutePath of files) {
|
|
16353
17150
|
const relativePath = toPosix(path19.relative(wikiDir, absolutePath));
|
|
16354
17151
|
const content = await fs15.readFile(absolutePath, "utf8");
|
|
16355
|
-
const parsed =
|
|
17152
|
+
const parsed = matter7(content);
|
|
16356
17153
|
const stats = await fs15.stat(absolutePath);
|
|
16357
17154
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(absolutePath, ".md");
|
|
16358
17155
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
@@ -16453,7 +17250,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
16453
17250
|
const relativePath = path20.posix.join(current.relativeDir, entry.name);
|
|
16454
17251
|
const absolutePath = path20.join(current.absoluteDir, entry.name);
|
|
16455
17252
|
const content = await fs16.readFile(absolutePath, "utf8");
|
|
16456
|
-
const parsed =
|
|
17253
|
+
const parsed = matter8(content);
|
|
16457
17254
|
const slug = relativePath.replace(/^outputs\//, "").replace(/\.md$/, "");
|
|
16458
17255
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(slug);
|
|
16459
17256
|
const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
|
|
@@ -16507,7 +17304,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
16507
17304
|
// src/search.ts
|
|
16508
17305
|
import fs17 from "fs/promises";
|
|
16509
17306
|
import path21 from "path";
|
|
16510
|
-
import
|
|
17307
|
+
import matter9 from "gray-matter";
|
|
16511
17308
|
function warningMessage(warning) {
|
|
16512
17309
|
return warning instanceof Error ? warning.message : String(warning);
|
|
16513
17310
|
}
|
|
@@ -16595,7 +17392,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
16595
17392
|
for (const page of pages) {
|
|
16596
17393
|
const absolutePath = path21.join(wikiDir, page.path);
|
|
16597
17394
|
const content = await fs17.readFile(absolutePath, "utf8");
|
|
16598
|
-
const parsed =
|
|
17395
|
+
const parsed = matter9(content);
|
|
16599
17396
|
let body = parsed.content;
|
|
16600
17397
|
const primarySourceId = Array.isArray(parsed.data.source_ids) && typeof parsed.data.source_ids[0] === "string" ? parsed.data.source_ids[0] : page.sourceIds[0];
|
|
16601
17398
|
if ((page.kind === "source" || page.kind === "module") && primarySourceId) {
|
|
@@ -16633,6 +17430,38 @@ ${excerpt.trim()}`.trim();
|
|
|
16633
17430
|
db.exec("INSERT INTO page_search (rowid, title, body) SELECT rowid, title, body FROM pages;");
|
|
16634
17431
|
db.close();
|
|
16635
17432
|
}
|
|
17433
|
+
function mergeSearchResults(ftsResults, semanticHits, limit) {
|
|
17434
|
+
const k = 60;
|
|
17435
|
+
const scores = /* @__PURE__ */ new Map();
|
|
17436
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
17437
|
+
for (let i = 0; i < ftsResults.length; i++) {
|
|
17438
|
+
const r = ftsResults[i];
|
|
17439
|
+
scores.set(r.pageId, (scores.get(r.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17440
|
+
resultMap.set(r.pageId, r);
|
|
17441
|
+
}
|
|
17442
|
+
for (let i = 0; i < semanticHits.length; i++) {
|
|
17443
|
+
const hit = semanticHits[i];
|
|
17444
|
+
scores.set(hit.pageId, (scores.get(hit.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17445
|
+
if (!resultMap.has(hit.pageId)) {
|
|
17446
|
+
resultMap.set(hit.pageId, {
|
|
17447
|
+
pageId: hit.pageId,
|
|
17448
|
+
path: hit.path,
|
|
17449
|
+
title: hit.title,
|
|
17450
|
+
snippet: "",
|
|
17451
|
+
rank: -hit.score,
|
|
17452
|
+
kind: hit.kind,
|
|
17453
|
+
status: hit.status,
|
|
17454
|
+
projectIds: [],
|
|
17455
|
+
sourceType: void 0,
|
|
17456
|
+
sourceClass: void 0
|
|
17457
|
+
});
|
|
17458
|
+
}
|
|
17459
|
+
}
|
|
17460
|
+
return [...scores.entries()].sort(([, a], [, b]) => b - a).slice(0, limit).map(([pageId, rrfScore]) => {
|
|
17461
|
+
const result = resultMap.get(pageId);
|
|
17462
|
+
return { ...result, rank: -rrfScore };
|
|
17463
|
+
});
|
|
17464
|
+
}
|
|
16636
17465
|
function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
16637
17466
|
const options = typeof limitOrOptions === "number" ? { limit: limitOrOptions } : limitOrOptions;
|
|
16638
17467
|
const ftsQuery = toFtsQuery(query);
|
|
@@ -16974,7 +17803,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
16974
17803
|
if (!providerConfig) {
|
|
16975
17804
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
16976
17805
|
}
|
|
16977
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
17806
|
+
const { createProvider: createProvider2 } = await import("./registry-SYCRRA65.js");
|
|
16978
17807
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
16979
17808
|
}
|
|
16980
17809
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -17493,7 +18322,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17493
18322
|
{
|
|
17494
18323
|
relativePath: "dashboards/index.md",
|
|
17495
18324
|
title: "Dashboards",
|
|
17496
|
-
content: (metadata) =>
|
|
18325
|
+
content: (metadata) => matter10.stringify(
|
|
17497
18326
|
[
|
|
17498
18327
|
"# Dashboards",
|
|
17499
18328
|
"",
|
|
@@ -17545,7 +18374,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17545
18374
|
{
|
|
17546
18375
|
relativePath: "dashboards/recent-sources.md",
|
|
17547
18376
|
title: "Recent Sources",
|
|
17548
|
-
content: (metadata) =>
|
|
18377
|
+
content: (metadata) => matter10.stringify(
|
|
17549
18378
|
[
|
|
17550
18379
|
"# Recent Sources",
|
|
17551
18380
|
"",
|
|
@@ -17588,7 +18417,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17588
18417
|
{
|
|
17589
18418
|
relativePath: "dashboards/reading-log.md",
|
|
17590
18419
|
title: "Reading Log",
|
|
17591
|
-
content: (metadata) =>
|
|
18420
|
+
content: (metadata) => matter10.stringify(
|
|
17592
18421
|
[
|
|
17593
18422
|
"# Reading Log",
|
|
17594
18423
|
"",
|
|
@@ -17648,7 +18477,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17648
18477
|
{
|
|
17649
18478
|
relativePath: "dashboards/timeline.md",
|
|
17650
18479
|
title: "Timeline",
|
|
17651
|
-
content: (metadata) =>
|
|
18480
|
+
content: (metadata) => matter10.stringify(
|
|
17652
18481
|
[
|
|
17653
18482
|
"# Timeline",
|
|
17654
18483
|
"",
|
|
@@ -17694,7 +18523,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17694
18523
|
{
|
|
17695
18524
|
relativePath: "dashboards/source-sessions.md",
|
|
17696
18525
|
title: "Source Sessions",
|
|
17697
|
-
content: (metadata) =>
|
|
18526
|
+
content: (metadata) => matter10.stringify(
|
|
17698
18527
|
[
|
|
17699
18528
|
"# Source Sessions",
|
|
17700
18529
|
"",
|
|
@@ -17751,7 +18580,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17751
18580
|
{
|
|
17752
18581
|
relativePath: "dashboards/source-guides.md",
|
|
17753
18582
|
title: "Source Guides",
|
|
17754
|
-
content: (metadata) =>
|
|
18583
|
+
content: (metadata) => matter10.stringify(
|
|
17755
18584
|
[
|
|
17756
18585
|
"# Source Guides",
|
|
17757
18586
|
"",
|
|
@@ -17804,7 +18633,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17804
18633
|
{
|
|
17805
18634
|
relativePath: "dashboards/research-map.md",
|
|
17806
18635
|
title: "Research Map",
|
|
17807
|
-
content: (metadata) =>
|
|
18636
|
+
content: (metadata) => matter10.stringify(
|
|
17808
18637
|
[
|
|
17809
18638
|
"# Research Map",
|
|
17810
18639
|
"",
|
|
@@ -17868,7 +18697,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17868
18697
|
{
|
|
17869
18698
|
relativePath: "dashboards/contradictions.md",
|
|
17870
18699
|
title: "Contradictions",
|
|
17871
|
-
content: (metadata) =>
|
|
18700
|
+
content: (metadata) => matter10.stringify(
|
|
17872
18701
|
[
|
|
17873
18702
|
"# Contradictions",
|
|
17874
18703
|
"",
|
|
@@ -17927,7 +18756,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17927
18756
|
{
|
|
17928
18757
|
relativePath: "dashboards/open-questions.md",
|
|
17929
18758
|
title: "Open Questions",
|
|
17930
|
-
content: (metadata) =>
|
|
18759
|
+
content: (metadata) => matter10.stringify(
|
|
17931
18760
|
[
|
|
17932
18761
|
"# Open Questions",
|
|
17933
18762
|
"",
|
|
@@ -18003,7 +18832,12 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
18003
18832
|
function indexCompiledFrom(pages) {
|
|
18004
18833
|
return uniqueStrings3(pages.flatMap((page) => page.sourceIds));
|
|
18005
18834
|
}
|
|
18006
|
-
function
|
|
18835
|
+
function autoResolution(nodeCount, edgeCount) {
|
|
18836
|
+
if (nodeCount <= 20) return 0.5;
|
|
18837
|
+
if (edgeCount / Math.max(1, nodeCount) < 2) return 0.8;
|
|
18838
|
+
return 1;
|
|
18839
|
+
}
|
|
18840
|
+
function deriveGraphMetrics(nodes, edges, options) {
|
|
18007
18841
|
const adjacency = /* @__PURE__ */ new Map();
|
|
18008
18842
|
const connect = (left, right) => {
|
|
18009
18843
|
if (!adjacency.has(left)) {
|
|
@@ -18040,7 +18874,8 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
18040
18874
|
}
|
|
18041
18875
|
}
|
|
18042
18876
|
}
|
|
18043
|
-
const
|
|
18877
|
+
const effectiveResolution = options?.resolution ?? autoResolution(louvainGraph.order, louvainGraph.size);
|
|
18878
|
+
const louvainMapping = louvainGraph.size > 0 ? louvain(louvainGraph, { resolution: effectiveResolution }) : {};
|
|
18044
18879
|
const groupByCommunity = /* @__PURE__ */ new Map();
|
|
18045
18880
|
let nextIsolated = -1;
|
|
18046
18881
|
for (const node of nonSourceNodes) {
|
|
@@ -18186,7 +19021,7 @@ function detectContradictions(analyses) {
|
|
|
18186
19021
|
}
|
|
18187
19022
|
return contradictions;
|
|
18188
19023
|
}
|
|
18189
|
-
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
19024
|
+
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex, options) {
|
|
18190
19025
|
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
18191
19026
|
const goPackageSymbolLookups = buildGoPackageSymbolLookups(analyses, manifestsById);
|
|
18192
19027
|
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
@@ -18546,7 +19381,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
18546
19381
|
manifests,
|
|
18547
19382
|
analyses
|
|
18548
19383
|
);
|
|
18549
|
-
const metrics = deriveGraphMetrics(graphNodes, enriched.edges);
|
|
19384
|
+
const metrics = deriveGraphMetrics(graphNodes, enriched.edges, { resolution: options?.communityResolution });
|
|
18550
19385
|
return {
|
|
18551
19386
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18552
19387
|
nodes: metrics.nodes,
|
|
@@ -19013,7 +19848,9 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
19013
19848
|
}
|
|
19014
19849
|
const compiledPages = records.map((record) => record.page);
|
|
19015
19850
|
const basePages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
19016
|
-
const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex
|
|
19851
|
+
const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex, {
|
|
19852
|
+
communityResolution: config.graph?.communityResolution
|
|
19853
|
+
});
|
|
19017
19854
|
const contradictions = detectContradictions(input.analyses);
|
|
19018
19855
|
for (const contradiction of contradictions) {
|
|
19019
19856
|
const edgeId = `contradiction:${contradiction.sourceIdA}->${contradiction.sourceIdB}`;
|
|
@@ -19035,7 +19872,9 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
19035
19872
|
const edges = uniqueBy([...structuralGraph.edges, ...embeddingEdges], (edge) => edge.id).sort(
|
|
19036
19873
|
(left, right) => left.id.localeCompare(right.id)
|
|
19037
19874
|
);
|
|
19038
|
-
const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges
|
|
19875
|
+
const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges, {
|
|
19876
|
+
resolution: config.graph?.communityResolution
|
|
19877
|
+
});
|
|
19039
19878
|
return {
|
|
19040
19879
|
...structuralGraph,
|
|
19041
19880
|
nodes: metrics.nodes,
|
|
@@ -19606,7 +20445,7 @@ async function executeQuery(rootDir, question, format) {
|
|
|
19606
20445
|
const absolutePath = path23.join(paths.wikiDir, result.path);
|
|
19607
20446
|
try {
|
|
19608
20447
|
const content = await fs19.readFile(absolutePath, "utf8");
|
|
19609
|
-
const parsed =
|
|
20448
|
+
const parsed = matter10(content);
|
|
19610
20449
|
return `# ${result.title}
|
|
19611
20450
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
19612
20451
|
} catch {
|
|
@@ -19819,8 +20658,8 @@ function computeChangeSummary(current, staged, changeType) {
|
|
|
19819
20658
|
if (changeType === "delete") return "Removed page";
|
|
19820
20659
|
if (changeType === "promote") return "Promoted from candidate";
|
|
19821
20660
|
if (!current || !staged) return "Updated page";
|
|
19822
|
-
const currentParsed =
|
|
19823
|
-
const stagedParsed =
|
|
20661
|
+
const currentParsed = matter10(current);
|
|
20662
|
+
const stagedParsed = matter10(staged);
|
|
19824
20663
|
const changes = [];
|
|
19825
20664
|
const currentTags = currentParsed.data.tags ?? [];
|
|
19826
20665
|
const stagedTags = stagedParsed.data.tags ?? [];
|
|
@@ -20021,9 +20860,9 @@ async function promoteCandidate(rootDir, target) {
|
|
|
20021
20860
|
const graph = await readJsonFile(paths.graphPath);
|
|
20022
20861
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
20023
20862
|
const raw = await fs19.readFile(path23.join(paths.wikiDir, candidate.path), "utf8");
|
|
20024
|
-
const parsed =
|
|
20863
|
+
const parsed = matter10(raw);
|
|
20025
20864
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20026
|
-
const nextContent =
|
|
20865
|
+
const nextContent = matter10.stringify(parsed.content, {
|
|
20027
20866
|
...parsed.data,
|
|
20028
20867
|
status: "active",
|
|
20029
20868
|
updated_at: nextUpdatedAt,
|
|
@@ -20174,7 +21013,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
20174
21013
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20175
21014
|
await writeFileIfChanged(
|
|
20176
21015
|
insightsIndexPath,
|
|
20177
|
-
|
|
21016
|
+
matter10.stringify(
|
|
20178
21017
|
(isResearchProfile ? [
|
|
20179
21018
|
"# Insights",
|
|
20180
21019
|
"",
|
|
@@ -20221,7 +21060,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
20221
21060
|
);
|
|
20222
21061
|
await writeFileIfChanged(
|
|
20223
21062
|
path23.join(paths.wikiDir, "projects", "index.md"),
|
|
20224
|
-
|
|
21063
|
+
matter10.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
20225
21064
|
page_id: "projects:index",
|
|
20226
21065
|
kind: "index",
|
|
20227
21066
|
title: "Projects",
|
|
@@ -20244,7 +21083,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
20244
21083
|
);
|
|
20245
21084
|
await writeFileIfChanged(
|
|
20246
21085
|
path23.join(paths.wikiDir, "candidates", "index.md"),
|
|
20247
|
-
|
|
21086
|
+
matter10.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
20248
21087
|
page_id: "candidates:index",
|
|
20249
21088
|
kind: "index",
|
|
20250
21089
|
title: "Candidates",
|
|
@@ -20271,7 +21110,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
20271
21110
|
if (isResearchProfile) {
|
|
20272
21111
|
await writeFileIfChanged(
|
|
20273
21112
|
path23.join(paths.wikiDir, "insights", "research-playbook.md"),
|
|
20274
|
-
|
|
21113
|
+
matter10.stringify(
|
|
20275
21114
|
[
|
|
20276
21115
|
`# ${requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook"}`,
|
|
20277
21116
|
"",
|
|
@@ -20553,6 +21392,45 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20553
21392
|
`benchmark=${benchmark.ok ? "ok" : `error:${benchmark.error}`}`
|
|
20554
21393
|
]
|
|
20555
21394
|
});
|
|
21395
|
+
let tokenStats;
|
|
21396
|
+
if (options.maxTokens && options.maxTokens > 0) {
|
|
21397
|
+
const { estimatePageTokens: estimatePageTokens2, trimToTokenBudget: trimToTokenBudget2 } = await import("./token-estimation-TTONKT4O.js");
|
|
21398
|
+
const nodeDegreeLookup = /* @__PURE__ */ new Map();
|
|
21399
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21400
|
+
if (graph) {
|
|
21401
|
+
for (const node of graph.nodes) {
|
|
21402
|
+
if (node.pageId && node.degree) {
|
|
21403
|
+
const existing = nodeDegreeLookup.get(node.pageId) ?? 0;
|
|
21404
|
+
nodeDegreeLookup.set(node.pageId, Math.max(existing, node.degree));
|
|
21405
|
+
}
|
|
21406
|
+
}
|
|
21407
|
+
}
|
|
21408
|
+
const estimates = await Promise.all(
|
|
21409
|
+
sync.allPages.map(async (page) => {
|
|
21410
|
+
const fullPath = path23.join(paths.wikiDir, page.path);
|
|
21411
|
+
let content = "";
|
|
21412
|
+
try {
|
|
21413
|
+
content = await fs19.readFile(fullPath, "utf8");
|
|
21414
|
+
} catch {
|
|
21415
|
+
}
|
|
21416
|
+
return estimatePageTokens2(page.id, page.path, page.kind, content, nodeDegreeLookup.get(page.id), page.confidence);
|
|
21417
|
+
})
|
|
21418
|
+
);
|
|
21419
|
+
const budgetResult = trimToTokenBudget2(estimates, options.maxTokens);
|
|
21420
|
+
for (const dropped of budgetResult.dropped) {
|
|
21421
|
+
const fullPath = path23.join(paths.wikiDir, dropped.path);
|
|
21422
|
+
try {
|
|
21423
|
+
await fs19.unlink(fullPath);
|
|
21424
|
+
} catch {
|
|
21425
|
+
}
|
|
21426
|
+
}
|
|
21427
|
+
tokenStats = {
|
|
21428
|
+
estimatedTokens: budgetResult.totalTokens,
|
|
21429
|
+
maxTokens: options.maxTokens,
|
|
21430
|
+
pagesKept: budgetResult.kept.length,
|
|
21431
|
+
pagesDropped: budgetResult.dropped.length
|
|
21432
|
+
};
|
|
21433
|
+
}
|
|
20556
21434
|
return {
|
|
20557
21435
|
graphPath: paths.graphPath,
|
|
20558
21436
|
pageCount: sync.allPages.length,
|
|
@@ -20564,7 +21442,8 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20564
21442
|
postPassApprovalId,
|
|
20565
21443
|
postPassApprovalDir,
|
|
20566
21444
|
promotedPageIds: sync.promotedPageIds,
|
|
20567
|
-
candidatePageCount: sync.candidatePageCount
|
|
21445
|
+
candidatePageCount: sync.candidatePageCount,
|
|
21446
|
+
tokenStats
|
|
20568
21447
|
};
|
|
20569
21448
|
}
|
|
20570
21449
|
async function queryVault(rootDir, options) {
|
|
@@ -20921,11 +21800,59 @@ ${orchestrationNotes.join("\n")}
|
|
|
20921
21800
|
};
|
|
20922
21801
|
}
|
|
20923
21802
|
async function searchVault(rootDir, query, limit = 5) {
|
|
20924
|
-
const { paths } = await loadVaultConfig(rootDir);
|
|
21803
|
+
const { paths, config } = await loadVaultConfig(rootDir);
|
|
20925
21804
|
if (!await fileExists(paths.searchDbPath)) {
|
|
20926
21805
|
await compileVault(rootDir, {});
|
|
20927
21806
|
}
|
|
20928
|
-
|
|
21807
|
+
const hybrid = config.search?.hybrid !== false;
|
|
21808
|
+
const ftsResults = searchPages(paths.searchDbPath, query, hybrid ? limit * 3 : limit);
|
|
21809
|
+
if (!hybrid || !await fileExists(paths.graphPath)) {
|
|
21810
|
+
return ftsResults.slice(0, limit);
|
|
21811
|
+
}
|
|
21812
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21813
|
+
if (!graph) {
|
|
21814
|
+
return ftsResults.slice(0, limit);
|
|
21815
|
+
}
|
|
21816
|
+
const semanticHits = await semanticPageSearch(rootDir, graph, query, limit * 3).catch(() => []);
|
|
21817
|
+
if (!semanticHits.length) {
|
|
21818
|
+
return ftsResults.slice(0, limit);
|
|
21819
|
+
}
|
|
21820
|
+
const merged = mergeSearchResults(ftsResults, semanticHits, limit);
|
|
21821
|
+
if (config.search?.rerank && merged.length > 1) {
|
|
21822
|
+
return rerankSearchResults(rootDir, query, merged, limit);
|
|
21823
|
+
}
|
|
21824
|
+
return merged;
|
|
21825
|
+
}
|
|
21826
|
+
async function rerankSearchResults(rootDir, query, results, limit) {
|
|
21827
|
+
const provider = await getProviderForTask(rootDir, "queryProvider");
|
|
21828
|
+
const candidates = results.slice(0, Math.min(results.length, 20)).map((r, i) => `[${i}] ${r.title} \u2014 ${r.snippet || r.path}`).join("\n");
|
|
21829
|
+
const prompt = `Given the search query: "${query}"
|
|
21830
|
+
|
|
21831
|
+
Rank these results by relevance (most relevant first).
|
|
21832
|
+
|
|
21833
|
+
${candidates}`;
|
|
21834
|
+
try {
|
|
21835
|
+
const indices = await provider.generateStructured(
|
|
21836
|
+
{ prompt, system: "You are a search result ranker." },
|
|
21837
|
+
z7.array(z7.number().int().nonnegative())
|
|
21838
|
+
);
|
|
21839
|
+
const reranked = [];
|
|
21840
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21841
|
+
for (const idx of indices) {
|
|
21842
|
+
if (idx >= 0 && idx < results.length && !seen.has(idx)) {
|
|
21843
|
+
seen.add(idx);
|
|
21844
|
+
reranked.push(results[idx]);
|
|
21845
|
+
}
|
|
21846
|
+
}
|
|
21847
|
+
for (let i = 0; i < results.length && reranked.length < limit; i++) {
|
|
21848
|
+
if (!seen.has(i)) {
|
|
21849
|
+
reranked.push(results[i]);
|
|
21850
|
+
}
|
|
21851
|
+
}
|
|
21852
|
+
return reranked.slice(0, limit);
|
|
21853
|
+
} catch {
|
|
21854
|
+
return results.slice(0, limit);
|
|
21855
|
+
}
|
|
20929
21856
|
}
|
|
20930
21857
|
async function ensureCompiledGraph(rootDir) {
|
|
20931
21858
|
const { paths } = await loadVaultConfig(rootDir);
|
|
@@ -20968,7 +21895,7 @@ async function benchmarkVault(rootDir, options = {}) {
|
|
|
20968
21895
|
if (!await fileExists(absolutePath)) {
|
|
20969
21896
|
continue;
|
|
20970
21897
|
}
|
|
20971
|
-
const parsed =
|
|
21898
|
+
const parsed = matter10(await fs19.readFile(absolutePath, "utf8"));
|
|
20972
21899
|
pageContentsById.set(page.id, parsed.content);
|
|
20973
21900
|
}
|
|
20974
21901
|
const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
@@ -21029,6 +21956,10 @@ async function listGodNodes(rootDir, limit = 10) {
|
|
|
21029
21956
|
const graph = await ensureCompiledGraph(rootDir);
|
|
21030
21957
|
return topGodNodes(graph, limit);
|
|
21031
21958
|
}
|
|
21959
|
+
async function blastRadiusVault(rootDir, target, options) {
|
|
21960
|
+
const graph = await ensureCompiledGraph(rootDir);
|
|
21961
|
+
return blastRadius(graph, target, options);
|
|
21962
|
+
}
|
|
21032
21963
|
async function listPages(rootDir) {
|
|
21033
21964
|
const { paths } = await loadVaultConfig(rootDir);
|
|
21034
21965
|
const graph = await readJsonFile(paths.graphPath);
|
|
@@ -21048,7 +21979,7 @@ async function readPage(rootDir, relativePath) {
|
|
|
21048
21979
|
return null;
|
|
21049
21980
|
}
|
|
21050
21981
|
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
21051
|
-
const parsed =
|
|
21982
|
+
const parsed = matter10(raw);
|
|
21052
21983
|
return {
|
|
21053
21984
|
path: relativePath,
|
|
21054
21985
|
title: typeof parsed.data.title === "string" ? parsed.data.title : path23.basename(relativePath, path23.extname(relativePath)),
|
|
@@ -21263,7 +22194,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
21263
22194
|
}
|
|
21264
22195
|
|
|
21265
22196
|
// src/mcp.ts
|
|
21266
|
-
var SERVER_VERSION = "0.7.
|
|
22197
|
+
var SERVER_VERSION = "0.7.26";
|
|
21267
22198
|
async function createMcpServer(rootDir) {
|
|
21268
22199
|
const server = new McpServer({
|
|
21269
22200
|
name: "swarmvault",
|
|
@@ -21413,6 +22344,19 @@ async function createMcpServer(rootDir) {
|
|
|
21413
22344
|
return asToolText(await listGodNodes(rootDir, limit ?? 10));
|
|
21414
22345
|
})
|
|
21415
22346
|
);
|
|
22347
|
+
server.registerTool(
|
|
22348
|
+
"blast_radius",
|
|
22349
|
+
{
|
|
22350
|
+
description: "Analyze the impact of changing a file or module by tracing reverse import edges.",
|
|
22351
|
+
inputSchema: {
|
|
22352
|
+
target: z8.string().min(1).describe("File path, module label, or module id"),
|
|
22353
|
+
maxDepth: z8.number().int().min(1).max(10).optional().describe("Maximum traversal depth (default 3)")
|
|
22354
|
+
}
|
|
22355
|
+
},
|
|
22356
|
+
safeHandler(async ({ target, maxDepth }) => {
|
|
22357
|
+
return asToolText(await blastRadiusVault(rootDir, target, { maxDepth: maxDepth ?? 3 }));
|
|
22358
|
+
})
|
|
22359
|
+
);
|
|
21416
22360
|
server.registerTool(
|
|
21417
22361
|
"query_vault",
|
|
21418
22362
|
{
|
|
@@ -21450,11 +22394,12 @@ async function createMcpServer(rootDir) {
|
|
|
21450
22394
|
{
|
|
21451
22395
|
description: "Compile source manifests into wiki pages, graph data, and search index.",
|
|
21452
22396
|
inputSchema: {
|
|
21453
|
-
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
|
|
22397
|
+
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes"),
|
|
22398
|
+
maxTokens: z8.number().int().min(1e3).optional().describe("Maximum token budget for wiki output")
|
|
21454
22399
|
}
|
|
21455
22400
|
},
|
|
21456
|
-
safeHandler(async ({ approve }) => {
|
|
21457
|
-
const result = await compileVault(rootDir, { approve: approve ?? false });
|
|
22401
|
+
safeHandler(async ({ approve, maxTokens }) => {
|
|
22402
|
+
const result = await compileVault(rootDir, { approve: approve ?? false, maxTokens });
|
|
21458
22403
|
return asToolText(result);
|
|
21459
22404
|
})
|
|
21460
22405
|
);
|
|
@@ -21934,7 +22879,7 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
21934
22879
|
import { spawn as spawn2 } from "child_process";
|
|
21935
22880
|
import fs22 from "fs/promises";
|
|
21936
22881
|
import path26 from "path";
|
|
21937
|
-
import
|
|
22882
|
+
import matter11 from "gray-matter";
|
|
21938
22883
|
import { JSDOM as JSDOM3 } from "jsdom";
|
|
21939
22884
|
var DEFAULT_CRAWL_MAX_PAGES = 12;
|
|
21940
22885
|
var DEFAULT_CRAWL_MAX_DEPTH = 2;
|
|
@@ -23277,7 +24222,7 @@ async function buildGuidedUpdatePages(rootDir, scope, session) {
|
|
|
23277
24222
|
const relativePath = useCanonicalTargets && targetPage ? targetPage.path : targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
|
|
23278
24223
|
const absolutePath = path26.join(paths.wikiDir, relativePath);
|
|
23279
24224
|
const existingContent = await fileExists(absolutePath) ? await fs22.readFile(absolutePath, "utf8") : "";
|
|
23280
|
-
const parsed = existingContent ?
|
|
24225
|
+
const parsed = existingContent ? matter11(existingContent) : { data: {}, content: "" };
|
|
23281
24226
|
const existingData = parsed.data;
|
|
23282
24227
|
const existingSourceIds = Array.isArray(existingData.source_ids) ? existingData.source_ids.filter((value) => typeof value === "string") : [];
|
|
23283
24228
|
const existingProjectIds = Array.isArray(existingData.project_ids) ? existingData.project_ids.filter((value) => typeof value === "string") : [];
|
|
@@ -23338,7 +24283,7 @@ async function buildGuidedUpdatePages(rootDir, scope, session) {
|
|
|
23338
24283
|
""
|
|
23339
24284
|
].join("\n");
|
|
23340
24285
|
const nextBody = replaceMarkedSection(baseBody, scope.id, updateBlock);
|
|
23341
|
-
const content =
|
|
24286
|
+
const content = matter11.stringify(
|
|
23342
24287
|
`${nextBody.trimEnd()}
|
|
23343
24288
|
`,
|
|
23344
24289
|
JSON.parse(
|
|
@@ -23719,12 +24664,12 @@ async function deleteManagedSource(rootDir, id) {
|
|
|
23719
24664
|
}
|
|
23720
24665
|
|
|
23721
24666
|
// src/viewer.ts
|
|
23722
|
-
import { execFile } from "child_process";
|
|
24667
|
+
import { execFile as execFile2 } from "child_process";
|
|
23723
24668
|
import fs23 from "fs/promises";
|
|
23724
24669
|
import http from "http";
|
|
23725
24670
|
import path28 from "path";
|
|
23726
|
-
import { promisify } from "util";
|
|
23727
|
-
import
|
|
24671
|
+
import { promisify as promisify2 } from "util";
|
|
24672
|
+
import matter12 from "gray-matter";
|
|
23728
24673
|
import mime2 from "mime-types";
|
|
23729
24674
|
|
|
23730
24675
|
// src/graph-presentation.ts
|
|
@@ -24337,7 +25282,7 @@ async function getWatchStatus(rootDir) {
|
|
|
24337
25282
|
}
|
|
24338
25283
|
|
|
24339
25284
|
// src/viewer.ts
|
|
24340
|
-
var
|
|
25285
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
24341
25286
|
async function isReadableFile(absolutePath) {
|
|
24342
25287
|
try {
|
|
24343
25288
|
const stats = await fs23.stat(absolutePath);
|
|
@@ -24356,7 +25301,7 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
24356
25301
|
return null;
|
|
24357
25302
|
}
|
|
24358
25303
|
const raw = await fs23.readFile(absolutePath, "utf8");
|
|
24359
|
-
const parsed =
|
|
25304
|
+
const parsed = matter12(raw);
|
|
24360
25305
|
return {
|
|
24361
25306
|
path: relativePath,
|
|
24362
25307
|
title: typeof parsed.data.title === "string" ? parsed.data.title : path28.basename(relativePath, path28.extname(relativePath)),
|
|
@@ -24404,7 +25349,7 @@ async function ensureViewerDist(viewerDistDir) {
|
|
|
24404
25349
|
}
|
|
24405
25350
|
const viewerProjectDir = path28.dirname(viewerDistDir);
|
|
24406
25351
|
if (await fileExists(path28.join(viewerProjectDir, "package.json"))) {
|
|
24407
|
-
await
|
|
25352
|
+
await execFileAsync2("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
24408
25353
|
}
|
|
24409
25354
|
}
|
|
24410
25355
|
async function startGraphServer(rootDir, port, options = {}) {
|
|
@@ -24584,6 +25529,44 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
24584
25529
|
response.end(JSON.stringify(result));
|
|
24585
25530
|
return;
|
|
24586
25531
|
}
|
|
25532
|
+
if (url.pathname === "/api/clip" && request.method === "POST") {
|
|
25533
|
+
const body = await readJsonBody(request);
|
|
25534
|
+
const clipUrl = typeof body.url === "string" ? body.url.trim() : "";
|
|
25535
|
+
if (!clipUrl) {
|
|
25536
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
25537
|
+
response.end(JSON.stringify({ error: "Missing url field." }));
|
|
25538
|
+
return;
|
|
25539
|
+
}
|
|
25540
|
+
const manifest = await ingestInput(rootDir, clipUrl);
|
|
25541
|
+
response.writeHead(200, { "content-type": "application/json", "access-control-allow-origin": "*" });
|
|
25542
|
+
response.end(JSON.stringify({ ok: true, sourceId: manifest.sourceId, title: manifest.title }));
|
|
25543
|
+
return;
|
|
25544
|
+
}
|
|
25545
|
+
if (url.pathname === "/api/clip" && request.method === "OPTIONS") {
|
|
25546
|
+
response.writeHead(204, {
|
|
25547
|
+
"access-control-allow-origin": "*",
|
|
25548
|
+
"access-control-allow-methods": "POST, OPTIONS",
|
|
25549
|
+
"access-control-allow-headers": "content-type"
|
|
25550
|
+
});
|
|
25551
|
+
response.end();
|
|
25552
|
+
return;
|
|
25553
|
+
}
|
|
25554
|
+
if (url.pathname === "/api/bookmarklet") {
|
|
25555
|
+
const script = `javascript:void(fetch('http://localhost:${effectivePort}/api/clip',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:location.href})}).then(r=>r.json()).then(d=>alert('Clipped: '+(d.title||d.sourceId))).catch(e=>alert('Clip failed: '+e.message)))`;
|
|
25556
|
+
response.writeHead(200, { "content-type": "text/html" });
|
|
25557
|
+
response.end(
|
|
25558
|
+
[
|
|
25559
|
+
"<!doctype html><html><head><title>SwarmVault Clipper</title></head><body>",
|
|
25560
|
+
"<h1>SwarmVault Clipper</h1>",
|
|
25561
|
+
`<p>Drag this link to your bookmarks bar:</p>`,
|
|
25562
|
+
`<p style="font-size:1.5em"><a href="${script.replace(/&/g, "&").replace(/"/g, """)}">Clip to SwarmVault</a></p>`,
|
|
25563
|
+
`<p>When clicked on any page, it sends the URL to your running SwarmVault instance for ingestion.</p>`,
|
|
25564
|
+
`<p>Server: <code>http://localhost:${effectivePort}</code></p>`,
|
|
25565
|
+
"</body></html>"
|
|
25566
|
+
].join("\n")
|
|
25567
|
+
);
|
|
25568
|
+
return;
|
|
25569
|
+
}
|
|
24587
25570
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
24588
25571
|
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
24589
25572
|
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
@@ -24702,7 +25685,10 @@ export {
|
|
|
24702
25685
|
addManagedSource,
|
|
24703
25686
|
archiveCandidate,
|
|
24704
25687
|
assertProviderCapability,
|
|
25688
|
+
autoCommitWikiChanges,
|
|
24705
25689
|
benchmarkVault,
|
|
25690
|
+
blastRadius,
|
|
25691
|
+
blastRadiusVault,
|
|
24706
25692
|
bootstrapDemo,
|
|
24707
25693
|
compileVault,
|
|
24708
25694
|
createMcpServer,
|
|
@@ -24711,10 +25697,13 @@ export {
|
|
|
24711
25697
|
defaultVaultConfig,
|
|
24712
25698
|
defaultVaultSchema,
|
|
24713
25699
|
deleteManagedSource,
|
|
25700
|
+
estimatePageTokens,
|
|
25701
|
+
estimateTokens,
|
|
24714
25702
|
explainGraphVault,
|
|
24715
25703
|
exploreVault,
|
|
24716
25704
|
exportGraphFormat,
|
|
24717
25705
|
exportGraphHtml,
|
|
25706
|
+
exportGraphReportHtml,
|
|
24718
25707
|
exportObsidianCanvas,
|
|
24719
25708
|
exportObsidianVault,
|
|
24720
25709
|
getGitHookStatus,
|
|
@@ -24771,6 +25760,7 @@ export {
|
|
|
24771
25760
|
startMcpServer,
|
|
24772
25761
|
syncTrackedRepos,
|
|
24773
25762
|
syncTrackedReposForWatch,
|
|
25763
|
+
trimToTokenBudget,
|
|
24774
25764
|
uninstallGitHooks,
|
|
24775
25765
|
watchVault
|
|
24776
25766
|
};
|