@swarmvaultai/engine 0.7.23 → 0.7.25
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-NAIERP4C.js +65 -0
- package/dist/hooks/claude.js +0 -0
- package/dist/hooks/copilot.js +0 -0
- package/dist/hooks/gemini.js +0 -0
- package/dist/index.d.ts +80 -4
- package/dist/index.js +662 -20
- package/dist/registry-UA42LQUQ.js +12 -0
- package/dist/token-estimation-TTONKT4O.js +10 -0
- package/package.json +9 -8
- package/LICENSE +0 -21
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-MB7HPUTR.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,6 +455,43 @@ 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";
|
|
@@ -587,6 +629,316 @@ function graphCounts(graph) {
|
|
|
587
629
|
};
|
|
588
630
|
}
|
|
589
631
|
|
|
632
|
+
// src/graph-report-html.ts
|
|
633
|
+
function htmlEscape(text) {
|
|
634
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
635
|
+
}
|
|
636
|
+
function nodeTypeColor(type) {
|
|
637
|
+
const colors = {
|
|
638
|
+
source: "#f59e0b",
|
|
639
|
+
module: "#fb7185",
|
|
640
|
+
symbol: "#8b5cf6",
|
|
641
|
+
rationale: "#14b8a6",
|
|
642
|
+
concept: "#0ea5e9",
|
|
643
|
+
entity: "#22c55e"
|
|
644
|
+
};
|
|
645
|
+
return colors[type] ?? "#94a3b8";
|
|
646
|
+
}
|
|
647
|
+
function renderGraphReportHtml(graph, report) {
|
|
648
|
+
const nodesByType = /* @__PURE__ */ new Map();
|
|
649
|
+
for (const node of graph.nodes) {
|
|
650
|
+
nodesByType.set(node.type, (nodesByType.get(node.type) ?? 0) + 1);
|
|
651
|
+
}
|
|
652
|
+
const edgesByRelation = /* @__PURE__ */ new Map();
|
|
653
|
+
for (const edge of graph.edges) {
|
|
654
|
+
edgesByRelation.set(edge.relation, (edgesByRelation.get(edge.relation) ?? 0) + 1);
|
|
655
|
+
}
|
|
656
|
+
const pagesByKind = /* @__PURE__ */ new Map();
|
|
657
|
+
for (const page of graph.pages) {
|
|
658
|
+
const list = pagesByKind.get(page.kind) ?? [];
|
|
659
|
+
list.push(page);
|
|
660
|
+
pagesByKind.set(page.kind, list);
|
|
661
|
+
}
|
|
662
|
+
const godNodes = (report?.godNodes ?? []).slice(0, 15);
|
|
663
|
+
const bridgeNodes = (report?.bridgeNodes ?? []).slice(0, 10);
|
|
664
|
+
const communities = graph.communities ?? [];
|
|
665
|
+
const warnings = report?.warnings ?? [];
|
|
666
|
+
const overview = report?.overview ?? {
|
|
667
|
+
nodes: graph.nodes.length,
|
|
668
|
+
edges: graph.edges.length,
|
|
669
|
+
pages: graph.pages.length,
|
|
670
|
+
communities: communities.length
|
|
671
|
+
};
|
|
672
|
+
const sortedEdgeRelations = [...edgesByRelation.entries()].sort((a, b) => b[1] - a[1]);
|
|
673
|
+
const sortedNodeTypes = [...nodesByType.entries()].sort((a, b) => b[1] - a[1]);
|
|
674
|
+
const sortedCommunities2 = [...communities].sort((a, b) => b.nodeIds.length - a.nodeIds.length).slice(0, 20);
|
|
675
|
+
return `<!DOCTYPE html>
|
|
676
|
+
<html lang="en">
|
|
677
|
+
<head>
|
|
678
|
+
<meta charset="UTF-8">
|
|
679
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
680
|
+
<title>SwarmVault Graph Report</title>
|
|
681
|
+
<style>
|
|
682
|
+
:root {
|
|
683
|
+
--bg: #0f172a;
|
|
684
|
+
--surface: #1e293b;
|
|
685
|
+
--surface2: #334155;
|
|
686
|
+
--text: #e2e8f0;
|
|
687
|
+
--muted: #94a3b8;
|
|
688
|
+
--accent: #0ea5e9;
|
|
689
|
+
--accent2: #8b5cf6;
|
|
690
|
+
--border: #475569;
|
|
691
|
+
--success: #22c55e;
|
|
692
|
+
--warning: #f59e0b;
|
|
693
|
+
--danger: #ef4444;
|
|
694
|
+
}
|
|
695
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
696
|
+
body {
|
|
697
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
698
|
+
background: var(--bg);
|
|
699
|
+
color: var(--text);
|
|
700
|
+
line-height: 1.6;
|
|
701
|
+
padding: 2rem;
|
|
702
|
+
max-width: 1200px;
|
|
703
|
+
margin: 0 auto;
|
|
704
|
+
}
|
|
705
|
+
h1 {
|
|
706
|
+
font-size: 1.75rem;
|
|
707
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
708
|
+
-webkit-background-clip: text;
|
|
709
|
+
-webkit-text-fill-color: transparent;
|
|
710
|
+
margin-bottom: 0.25rem;
|
|
711
|
+
}
|
|
712
|
+
.subtitle { color: var(--muted); font-size: 0.85rem; margin-bottom: 2rem; }
|
|
713
|
+
h2 {
|
|
714
|
+
font-size: 1.15rem;
|
|
715
|
+
color: var(--text);
|
|
716
|
+
border-bottom: 1px solid var(--border);
|
|
717
|
+
padding-bottom: 0.5rem;
|
|
718
|
+
margin: 2rem 0 1rem;
|
|
719
|
+
}
|
|
720
|
+
.stats-grid {
|
|
721
|
+
display: grid;
|
|
722
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
723
|
+
gap: 1rem;
|
|
724
|
+
margin-bottom: 1.5rem;
|
|
725
|
+
}
|
|
726
|
+
.stat-card {
|
|
727
|
+
background: var(--surface);
|
|
728
|
+
border: 1px solid var(--border);
|
|
729
|
+
border-radius: 8px;
|
|
730
|
+
padding: 1rem;
|
|
731
|
+
text-align: center;
|
|
732
|
+
}
|
|
733
|
+
.stat-card .value {
|
|
734
|
+
font-size: 1.75rem;
|
|
735
|
+
font-weight: 700;
|
|
736
|
+
color: var(--accent);
|
|
737
|
+
}
|
|
738
|
+
.stat-card .label {
|
|
739
|
+
font-size: 0.8rem;
|
|
740
|
+
color: var(--muted);
|
|
741
|
+
text-transform: uppercase;
|
|
742
|
+
letter-spacing: 0.05em;
|
|
743
|
+
}
|
|
744
|
+
.badge {
|
|
745
|
+
display: inline-block;
|
|
746
|
+
padding: 0.15rem 0.5rem;
|
|
747
|
+
border-radius: 9999px;
|
|
748
|
+
font-size: 0.7rem;
|
|
749
|
+
font-weight: 600;
|
|
750
|
+
color: #fff;
|
|
751
|
+
}
|
|
752
|
+
table {
|
|
753
|
+
width: 100%;
|
|
754
|
+
border-collapse: collapse;
|
|
755
|
+
margin-bottom: 1rem;
|
|
756
|
+
}
|
|
757
|
+
th, td {
|
|
758
|
+
text-align: left;
|
|
759
|
+
padding: 0.5rem 0.75rem;
|
|
760
|
+
border-bottom: 1px solid var(--border);
|
|
761
|
+
font-size: 0.85rem;
|
|
762
|
+
}
|
|
763
|
+
th {
|
|
764
|
+
color: var(--muted);
|
|
765
|
+
font-weight: 600;
|
|
766
|
+
font-size: 0.75rem;
|
|
767
|
+
text-transform: uppercase;
|
|
768
|
+
letter-spacing: 0.05em;
|
|
769
|
+
}
|
|
770
|
+
tr:hover { background: var(--surface); }
|
|
771
|
+
.bar-container {
|
|
772
|
+
display: flex;
|
|
773
|
+
align-items: center;
|
|
774
|
+
gap: 0.5rem;
|
|
775
|
+
}
|
|
776
|
+
.bar {
|
|
777
|
+
height: 8px;
|
|
778
|
+
border-radius: 4px;
|
|
779
|
+
background: var(--accent);
|
|
780
|
+
min-width: 2px;
|
|
781
|
+
}
|
|
782
|
+
.warning-list {
|
|
783
|
+
list-style: none;
|
|
784
|
+
padding: 0;
|
|
785
|
+
}
|
|
786
|
+
.warning-list li {
|
|
787
|
+
padding: 0.5rem 0.75rem;
|
|
788
|
+
margin-bottom: 0.5rem;
|
|
789
|
+
background: var(--surface);
|
|
790
|
+
border-left: 3px solid var(--warning);
|
|
791
|
+
border-radius: 0 4px 4px 0;
|
|
792
|
+
font-size: 0.85rem;
|
|
793
|
+
}
|
|
794
|
+
.page-group { margin-bottom: 1.5rem; }
|
|
795
|
+
.page-group-title {
|
|
796
|
+
font-size: 0.85rem;
|
|
797
|
+
font-weight: 600;
|
|
798
|
+
color: var(--accent);
|
|
799
|
+
text-transform: capitalize;
|
|
800
|
+
margin-bottom: 0.5rem;
|
|
801
|
+
}
|
|
802
|
+
.page-list {
|
|
803
|
+
display: grid;
|
|
804
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
805
|
+
gap: 0.5rem;
|
|
806
|
+
}
|
|
807
|
+
.page-item {
|
|
808
|
+
background: var(--surface);
|
|
809
|
+
border: 1px solid var(--border);
|
|
810
|
+
border-radius: 6px;
|
|
811
|
+
padding: 0.5rem 0.75rem;
|
|
812
|
+
font-size: 0.8rem;
|
|
813
|
+
overflow: hidden;
|
|
814
|
+
text-overflow: ellipsis;
|
|
815
|
+
white-space: nowrap;
|
|
816
|
+
}
|
|
817
|
+
.page-item .path { color: var(--muted); font-size: 0.7rem; }
|
|
818
|
+
.empty { color: var(--muted); font-style: italic; font-size: 0.85rem; }
|
|
819
|
+
input[type="text"] {
|
|
820
|
+
width: 100%;
|
|
821
|
+
padding: 0.5rem 0.75rem;
|
|
822
|
+
background: var(--surface);
|
|
823
|
+
border: 1px solid var(--border);
|
|
824
|
+
border-radius: 6px;
|
|
825
|
+
color: var(--text);
|
|
826
|
+
font-size: 0.85rem;
|
|
827
|
+
margin-bottom: 1rem;
|
|
828
|
+
outline: none;
|
|
829
|
+
}
|
|
830
|
+
input[type="text"]:focus { border-color: var(--accent); }
|
|
831
|
+
.section { margin-bottom: 1rem; }
|
|
832
|
+
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; text-align: center; }
|
|
833
|
+
</style>
|
|
834
|
+
</head>
|
|
835
|
+
<body>
|
|
836
|
+
<h1>SwarmVault Graph Report</h1>
|
|
837
|
+
<p class="subtitle">Generated ${htmlEscape(report?.generatedAt ?? graph.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString())}</p>
|
|
838
|
+
|
|
839
|
+
<div class="stats-grid">
|
|
840
|
+
<div class="stat-card"><div class="value">${overview.nodes}</div><div class="label">Nodes</div></div>
|
|
841
|
+
<div class="stat-card"><div class="value">${overview.edges}</div><div class="label">Edges</div></div>
|
|
842
|
+
<div class="stat-card"><div class="value">${overview.pages}</div><div class="label">Pages</div></div>
|
|
843
|
+
<div class="stat-card"><div class="value">${overview.communities}</div><div class="label">Communities</div></div>
|
|
844
|
+
<div class="stat-card"><div class="value">${graph.sources.length}</div><div class="label">Sources</div></div>
|
|
845
|
+
<div class="stat-card"><div class="value">${(graph.hyperedges ?? []).length}</div><div class="label">Hyperedges</div></div>
|
|
846
|
+
</div>
|
|
847
|
+
|
|
848
|
+
<h2>Node Types</h2>
|
|
849
|
+
<table>
|
|
850
|
+
<thead><tr><th>Type</th><th>Count</th><th></th></tr></thead>
|
|
851
|
+
<tbody>
|
|
852
|
+
${sortedNodeTypes.map(([type, count]) => {
|
|
853
|
+
const maxCount = sortedNodeTypes[0]?.[1] ?? 1;
|
|
854
|
+
const pct = Math.round(count / maxCount * 100);
|
|
855
|
+
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>`;
|
|
856
|
+
}).join("\n")}
|
|
857
|
+
</tbody>
|
|
858
|
+
</table>
|
|
859
|
+
|
|
860
|
+
<h2>Edge Relations</h2>
|
|
861
|
+
<table>
|
|
862
|
+
<thead><tr><th>Relation</th><th>Count</th><th></th></tr></thead>
|
|
863
|
+
<tbody>
|
|
864
|
+
${sortedEdgeRelations.map(([relation, count]) => {
|
|
865
|
+
const maxCount = sortedEdgeRelations[0]?.[1] ?? 1;
|
|
866
|
+
const pct = Math.round(count / maxCount * 100);
|
|
867
|
+
return `<tr><td>${htmlEscape(relation)}</td><td>${count}</td><td><div class="bar-container"><div class="bar" style="width:${pct}%"></div></div></td></tr>`;
|
|
868
|
+
}).join("\n")}
|
|
869
|
+
</tbody>
|
|
870
|
+
</table>
|
|
871
|
+
|
|
872
|
+
${godNodes.length ? `<h2>God Nodes (Highest Connectivity)</h2>
|
|
873
|
+
<table>
|
|
874
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th><th></th></tr></thead>
|
|
875
|
+
<tbody>
|
|
876
|
+
${godNodes.map((node) => {
|
|
877
|
+
const maxDegree = godNodes[0]?.degree ?? 1;
|
|
878
|
+
const pct = Math.round((node.degree ?? 0) / maxDegree * 100);
|
|
879
|
+
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>`;
|
|
880
|
+
}).join("\n")}
|
|
881
|
+
</tbody>
|
|
882
|
+
</table>` : ""}
|
|
883
|
+
|
|
884
|
+
${bridgeNodes.length ? `<h2>Bridge Nodes</h2>
|
|
885
|
+
<table>
|
|
886
|
+
<thead><tr><th>Label</th><th>Degree</th><th>Bridge Score</th></tr></thead>
|
|
887
|
+
<tbody>
|
|
888
|
+
${bridgeNodes.map((node) => `<tr><td>${htmlEscape(node.label)}</td><td>${node.degree ?? 0}</td><td>${(node.bridgeScore ?? 0).toFixed(2)}</td></tr>`).join("\n")}
|
|
889
|
+
</tbody>
|
|
890
|
+
</table>` : ""}
|
|
891
|
+
|
|
892
|
+
${sortedCommunities2.length ? `<h2>Communities</h2>
|
|
893
|
+
<table>
|
|
894
|
+
<thead><tr><th>Label</th><th>Nodes</th><th></th></tr></thead>
|
|
895
|
+
<tbody>
|
|
896
|
+
${sortedCommunities2.map((c) => {
|
|
897
|
+
const maxSize = sortedCommunities2[0]?.nodeIds.length ?? 1;
|
|
898
|
+
const pct = Math.round(c.nodeIds.length / maxSize * 100);
|
|
899
|
+
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>`;
|
|
900
|
+
}).join("\n")}
|
|
901
|
+
</tbody>
|
|
902
|
+
</table>` : ""}
|
|
903
|
+
|
|
904
|
+
${warnings.length ? `<h2>Warnings</h2>
|
|
905
|
+
<ul class="warning-list">
|
|
906
|
+
${warnings.map((w) => `<li>${htmlEscape(w)}</li>`).join("\n")}
|
|
907
|
+
</ul>` : ""}
|
|
908
|
+
|
|
909
|
+
<h2>Pages</h2>
|
|
910
|
+
<input type="text" id="page-filter" placeholder="Filter pages..." />
|
|
911
|
+
<div id="pages-container">
|
|
912
|
+
${[...pagesByKind.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(
|
|
913
|
+
([kind, pages]) => `<div class="page-group" data-kind="${htmlEscape(kind)}">
|
|
914
|
+
<div class="page-group-title">${htmlEscape(kind)} (${pages.length})</div>
|
|
915
|
+
<div class="page-list">
|
|
916
|
+
${pages.sort((a, b) => a.title.localeCompare(b.title)).map(
|
|
917
|
+
(p) => `<div class="page-item" data-title="${htmlEscape(p.title.toLowerCase())}"><strong>${htmlEscape(p.title)}</strong><div class="path">${htmlEscape(p.path)}</div></div>`
|
|
918
|
+
).join("\n ")}
|
|
919
|
+
</div>
|
|
920
|
+
</div>`
|
|
921
|
+
).join("\n")}
|
|
922
|
+
</div>
|
|
923
|
+
|
|
924
|
+
<footer>Generated by SwarmVault · ${graph.nodes.length} nodes · ${graph.edges.length} edges · ${graph.pages.length} pages</footer>
|
|
925
|
+
|
|
926
|
+
<script>
|
|
927
|
+
document.getElementById("page-filter").addEventListener("input", function(e) {
|
|
928
|
+
var query = e.target.value.toLowerCase();
|
|
929
|
+
document.querySelectorAll(".page-item").forEach(function(el) {
|
|
930
|
+
el.style.display = el.getAttribute("data-title").includes(query) ? "" : "none";
|
|
931
|
+
});
|
|
932
|
+
document.querySelectorAll(".page-group").forEach(function(group) {
|
|
933
|
+
var visible = group.querySelectorAll('.page-item[style=""], .page-item:not([style])').length;
|
|
934
|
+
group.style.display = visible > 0 || !query ? "" : "none";
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
</script>
|
|
938
|
+
</body>
|
|
939
|
+
</html>`;
|
|
940
|
+
}
|
|
941
|
+
|
|
590
942
|
// src/graph-export.ts
|
|
591
943
|
var NODE_COLORS = {
|
|
592
944
|
source: "#f59e0b",
|
|
@@ -1220,6 +1572,14 @@ async function exportGraphFormat(rootDir, format, outputPath) {
|
|
|
1220
1572
|
const resolvedPath = await writeGraphExport(outputPath, rendered);
|
|
1221
1573
|
return { format, outputPath: resolvedPath };
|
|
1222
1574
|
}
|
|
1575
|
+
async function exportGraphReportHtml(rootDir, outputPath) {
|
|
1576
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
1577
|
+
const graph = await loadGraph(rootDir);
|
|
1578
|
+
const report = await readJsonFile(path2.join(paths.wikiDir, "graph", "report.json"));
|
|
1579
|
+
const html = renderGraphReportHtml(graph, report);
|
|
1580
|
+
const resolvedPath = await writeGraphExport(outputPath, html);
|
|
1581
|
+
return { format: "report", outputPath: resolvedPath };
|
|
1582
|
+
}
|
|
1223
1583
|
function safeFileName(label) {
|
|
1224
1584
|
return label.replace(/[\\/*?:"<>|#^[\]]/g, "").replace(/\s+/g, " ").trim().slice(0, 200) || "unnamed";
|
|
1225
1585
|
}
|
|
@@ -1463,7 +1823,7 @@ function nodeMap(graph) {
|
|
|
1463
1823
|
function pageMap(graph) {
|
|
1464
1824
|
return new Map(graph.pages.map((page) => [page.id, page]));
|
|
1465
1825
|
}
|
|
1466
|
-
function
|
|
1826
|
+
function estimateTokens2(text) {
|
|
1467
1827
|
return Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));
|
|
1468
1828
|
}
|
|
1469
1829
|
function estimateCorpusWords(texts) {
|
|
@@ -1500,7 +1860,7 @@ function benchmarkQueryTokens(graph, queryResult, pageContentsById) {
|
|
|
1500
1860
|
const target = nodesById.get(edge.target)?.label ?? edge.target;
|
|
1501
1861
|
lines.push(`EDGE ${source} --${edge.relation}/${edge.evidenceClass}/${edge.confidence.toFixed(2)}--> ${target}`);
|
|
1502
1862
|
}
|
|
1503
|
-
const queryTokens =
|
|
1863
|
+
const queryTokens = estimateTokens2(lines.join("\n"));
|
|
1504
1864
|
return {
|
|
1505
1865
|
question: queryResult.question,
|
|
1506
1866
|
queryTokens,
|
|
@@ -2217,6 +2577,67 @@ function graphDiff(oldGraph, newGraph) {
|
|
|
2217
2577
|
const summary = parts.length ? parts.join("; ") : "No changes";
|
|
2218
2578
|
return { addedNodes, removedNodes, addedEdges, removedEdges, addedPages, removedPages, summary };
|
|
2219
2579
|
}
|
|
2580
|
+
function blastRadius(graph, target, options) {
|
|
2581
|
+
const maxDepth = Math.max(1, Math.min(options?.maxDepth ?? 3, 10));
|
|
2582
|
+
const resolved = resolveNode(graph, target);
|
|
2583
|
+
const moduleNode = resolved?.type === "module" ? resolved : resolved?.moduleId ? graph.nodes.find((n) => n.id === resolved.moduleId) : void 0;
|
|
2584
|
+
if (!moduleNode) {
|
|
2585
|
+
const normalizedTarget = normalizeTarget(target);
|
|
2586
|
+
const candidate = graph.nodes.filter((n) => n.type === "module").find((n) => normalizeTarget(n.label).includes(normalizedTarget) || normalizeTarget(n.id).includes(normalizedTarget));
|
|
2587
|
+
if (!candidate) {
|
|
2588
|
+
return {
|
|
2589
|
+
target,
|
|
2590
|
+
totalAffected: 0,
|
|
2591
|
+
maxDepth,
|
|
2592
|
+
affectedModules: [],
|
|
2593
|
+
summary: `No module found matching "${target}".`
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
return blastRadius(graph, candidate.id, options);
|
|
2597
|
+
}
|
|
2598
|
+
const reverseImports = /* @__PURE__ */ new Map();
|
|
2599
|
+
for (const edge of graph.edges) {
|
|
2600
|
+
if (edge.relation === "imports") {
|
|
2601
|
+
const dependents = reverseImports.get(edge.target) ?? [];
|
|
2602
|
+
dependents.push(edge.source);
|
|
2603
|
+
reverseImports.set(edge.target, dependents);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
const affected = [];
|
|
2607
|
+
const seen = /* @__PURE__ */ new Set([moduleNode.id]);
|
|
2608
|
+
const frontier = [{ id: moduleNode.id, depth: 0 }];
|
|
2609
|
+
const nodes = nodeById(graph);
|
|
2610
|
+
while (frontier.length > 0) {
|
|
2611
|
+
const current = frontier.shift();
|
|
2612
|
+
if (current.depth >= maxDepth) {
|
|
2613
|
+
continue;
|
|
2614
|
+
}
|
|
2615
|
+
for (const dependentId of reverseImports.get(current.id) ?? []) {
|
|
2616
|
+
if (seen.has(dependentId)) {
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
seen.add(dependentId);
|
|
2620
|
+
const dependentNode = nodes.get(dependentId);
|
|
2621
|
+
const nextDepth = current.depth + 1;
|
|
2622
|
+
affected.push({
|
|
2623
|
+
moduleId: dependentId,
|
|
2624
|
+
label: dependentNode?.label ?? dependentId,
|
|
2625
|
+
depth: nextDepth
|
|
2626
|
+
});
|
|
2627
|
+
frontier.push({ id: dependentId, depth: nextDepth });
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
affected.sort((a, b) => a.depth - b.depth || a.label.localeCompare(b.label));
|
|
2631
|
+
const summary = affected.length ? `Changing "${moduleNode.label}" affects ${affected.length} module${affected.length === 1 ? "" : "s"} (max depth ${maxDepth}).` : `No modules depend on "${moduleNode.label}".`;
|
|
2632
|
+
return {
|
|
2633
|
+
target,
|
|
2634
|
+
resolvedModuleId: moduleNode.id,
|
|
2635
|
+
affectedModules: affected,
|
|
2636
|
+
totalAffected: affected.length,
|
|
2637
|
+
maxDepth,
|
|
2638
|
+
summary
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2220
2641
|
|
|
2221
2642
|
// src/hooks.ts
|
|
2222
2643
|
import fs4 from "fs/promises";
|
|
@@ -14024,6 +14445,33 @@ async function semanticGraphMatches(rootDir, graph, question, limit = 12) {
|
|
|
14024
14445
|
score: Math.max(0, Number((cosineSimilarity(queryVector, vectors.get(`${item.kind}:${item.id}`) ?? []) * 100).toFixed(2)))
|
|
14025
14446
|
})).filter((match) => match.score >= 18).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, limit);
|
|
14026
14447
|
}
|
|
14448
|
+
async function semanticPageSearch(rootDir, graph, query, limit = 10) {
|
|
14449
|
+
const items = await buildEmbeddableItems(rootDir, graph);
|
|
14450
|
+
const pageItems = items.filter((item) => item.kind === "page");
|
|
14451
|
+
if (!pageItems.length) {
|
|
14452
|
+
return [];
|
|
14453
|
+
}
|
|
14454
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
14455
|
+
if (!provider) {
|
|
14456
|
+
return [];
|
|
14457
|
+
}
|
|
14458
|
+
const [queryVector] = await provider.embedTexts([query]);
|
|
14459
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) {
|
|
14460
|
+
return [];
|
|
14461
|
+
}
|
|
14462
|
+
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
14463
|
+
return pageItems.map((item) => {
|
|
14464
|
+
const page = pageMap2.get(item.id);
|
|
14465
|
+
return {
|
|
14466
|
+
pageId: item.id,
|
|
14467
|
+
path: page?.path ?? "",
|
|
14468
|
+
title: item.label,
|
|
14469
|
+
kind: page?.kind ?? "",
|
|
14470
|
+
status: page?.status ?? "",
|
|
14471
|
+
score: cosineSimilarity(queryVector, vectors.get(`page:${item.id}`) ?? [])
|
|
14472
|
+
};
|
|
14473
|
+
}).filter((result) => result.score >= 0.25 && result.path).sort((left, right) => right.score - left.score).slice(0, limit);
|
|
14474
|
+
}
|
|
14027
14475
|
function distinctScope(left, right) {
|
|
14028
14476
|
const leftSources = new Set(left.sourceIds);
|
|
14029
14477
|
const rightSources = new Set(right.sourceIds);
|
|
@@ -16633,6 +17081,38 @@ ${excerpt.trim()}`.trim();
|
|
|
16633
17081
|
db.exec("INSERT INTO page_search (rowid, title, body) SELECT rowid, title, body FROM pages;");
|
|
16634
17082
|
db.close();
|
|
16635
17083
|
}
|
|
17084
|
+
function mergeSearchResults(ftsResults, semanticHits, limit) {
|
|
17085
|
+
const k = 60;
|
|
17086
|
+
const scores = /* @__PURE__ */ new Map();
|
|
17087
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
17088
|
+
for (let i = 0; i < ftsResults.length; i++) {
|
|
17089
|
+
const r = ftsResults[i];
|
|
17090
|
+
scores.set(r.pageId, (scores.get(r.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17091
|
+
resultMap.set(r.pageId, r);
|
|
17092
|
+
}
|
|
17093
|
+
for (let i = 0; i < semanticHits.length; i++) {
|
|
17094
|
+
const hit = semanticHits[i];
|
|
17095
|
+
scores.set(hit.pageId, (scores.get(hit.pageId) ?? 0) + 1 / (k + i + 1));
|
|
17096
|
+
if (!resultMap.has(hit.pageId)) {
|
|
17097
|
+
resultMap.set(hit.pageId, {
|
|
17098
|
+
pageId: hit.pageId,
|
|
17099
|
+
path: hit.path,
|
|
17100
|
+
title: hit.title,
|
|
17101
|
+
snippet: "",
|
|
17102
|
+
rank: -hit.score,
|
|
17103
|
+
kind: hit.kind,
|
|
17104
|
+
status: hit.status,
|
|
17105
|
+
projectIds: [],
|
|
17106
|
+
sourceType: void 0,
|
|
17107
|
+
sourceClass: void 0
|
|
17108
|
+
});
|
|
17109
|
+
}
|
|
17110
|
+
}
|
|
17111
|
+
return [...scores.entries()].sort(([, a], [, b]) => b - a).slice(0, limit).map(([pageId, rrfScore]) => {
|
|
17112
|
+
const result = resultMap.get(pageId);
|
|
17113
|
+
return { ...result, rank: -rrfScore };
|
|
17114
|
+
});
|
|
17115
|
+
}
|
|
16636
17116
|
function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
16637
17117
|
const options = typeof limitOrOptions === "number" ? { limit: limitOrOptions } : limitOrOptions;
|
|
16638
17118
|
const ftsQuery = toFtsQuery(query);
|
|
@@ -16974,7 +17454,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
16974
17454
|
if (!providerConfig) {
|
|
16975
17455
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
16976
17456
|
}
|
|
16977
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
17457
|
+
const { createProvider: createProvider2 } = await import("./registry-UA42LQUQ.js");
|
|
16978
17458
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
16979
17459
|
}
|
|
16980
17460
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -17485,8 +17965,8 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
|
|
|
17485
17965
|
).slice(0, 20);
|
|
17486
17966
|
const sourceSessions = await listGuidedSourceSessions(paths.rootDir);
|
|
17487
17967
|
const stagedGuideBundles = (await Promise.all(
|
|
17488
|
-
(await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await
|
|
17489
|
-
)).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "
|
|
17968
|
+
(await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await readApprovalManifest(paths, entry.name).catch(() => null))
|
|
17969
|
+
)).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "guided-source" || manifest.bundleType === "guided-session").sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, 12);
|
|
17490
17970
|
const readerFocusPages = uniqueBy([...guidePages, ...briefPages, ...conceptPages, ...entityPages], (page) => page.id).slice(0, 8);
|
|
17491
17971
|
const diligenceSessions = sourceSessions.filter((session) => session.status === "staged" || session.status === "awaiting_input").slice(0, 8);
|
|
17492
17972
|
const dashboards = [
|
|
@@ -18713,11 +19193,22 @@ function approvalManifestPath(paths, approvalId) {
|
|
|
18713
19193
|
function approvalGraphPath(paths, approvalId) {
|
|
18714
19194
|
return path23.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
18715
19195
|
}
|
|
19196
|
+
function normalizeApprovalBundleType(raw) {
|
|
19197
|
+
if (!raw) return void 0;
|
|
19198
|
+
const legacy = {
|
|
19199
|
+
generated_output: "generated-output",
|
|
19200
|
+
source_review: "source-review",
|
|
19201
|
+
guided_source: "guided-source",
|
|
19202
|
+
guided_session: "guided-session"
|
|
19203
|
+
};
|
|
19204
|
+
return legacy[raw] ?? raw;
|
|
19205
|
+
}
|
|
18716
19206
|
async function readApprovalManifest(paths, approvalId) {
|
|
18717
19207
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
18718
19208
|
if (!manifest) {
|
|
18719
19209
|
throw new Error(`Approval bundle not found: ${approvalId}`);
|
|
18720
19210
|
}
|
|
19211
|
+
manifest.bundleType = normalizeApprovalBundleType(manifest.bundleType);
|
|
18721
19212
|
return manifest;
|
|
18722
19213
|
}
|
|
18723
19214
|
async function writeApprovalManifest(paths, manifest) {
|
|
@@ -19560,7 +20051,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages, options = {}) {
|
|
|
19560
20051
|
await writeApprovalManifest(paths, {
|
|
19561
20052
|
approvalId,
|
|
19562
20053
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19563
|
-
bundleType: options.bundleType ?? "
|
|
20054
|
+
bundleType: options.bundleType ?? "generated-output",
|
|
19564
20055
|
title: options.title,
|
|
19565
20056
|
sourceSessionId: options.sourceSessionId,
|
|
19566
20057
|
entries: await buildApprovalEntries(
|
|
@@ -20542,6 +21033,45 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20542
21033
|
`benchmark=${benchmark.ok ? "ok" : `error:${benchmark.error}`}`
|
|
20543
21034
|
]
|
|
20544
21035
|
});
|
|
21036
|
+
let tokenStats;
|
|
21037
|
+
if (options.maxTokens && options.maxTokens > 0) {
|
|
21038
|
+
const { estimatePageTokens: estimatePageTokens2, trimToTokenBudget: trimToTokenBudget2 } = await import("./token-estimation-TTONKT4O.js");
|
|
21039
|
+
const nodeDegreeLookup = /* @__PURE__ */ new Map();
|
|
21040
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21041
|
+
if (graph) {
|
|
21042
|
+
for (const node of graph.nodes) {
|
|
21043
|
+
if (node.pageId && node.degree) {
|
|
21044
|
+
const existing = nodeDegreeLookup.get(node.pageId) ?? 0;
|
|
21045
|
+
nodeDegreeLookup.set(node.pageId, Math.max(existing, node.degree));
|
|
21046
|
+
}
|
|
21047
|
+
}
|
|
21048
|
+
}
|
|
21049
|
+
const estimates = await Promise.all(
|
|
21050
|
+
sync.allPages.map(async (page) => {
|
|
21051
|
+
const fullPath = path23.join(paths.wikiDir, page.path);
|
|
21052
|
+
let content = "";
|
|
21053
|
+
try {
|
|
21054
|
+
content = await fs19.readFile(fullPath, "utf8");
|
|
21055
|
+
} catch {
|
|
21056
|
+
}
|
|
21057
|
+
return estimatePageTokens2(page.id, page.path, page.kind, content, nodeDegreeLookup.get(page.id), page.confidence);
|
|
21058
|
+
})
|
|
21059
|
+
);
|
|
21060
|
+
const budgetResult = trimToTokenBudget2(estimates, options.maxTokens);
|
|
21061
|
+
for (const dropped of budgetResult.dropped) {
|
|
21062
|
+
const fullPath = path23.join(paths.wikiDir, dropped.path);
|
|
21063
|
+
try {
|
|
21064
|
+
await fs19.unlink(fullPath);
|
|
21065
|
+
} catch {
|
|
21066
|
+
}
|
|
21067
|
+
}
|
|
21068
|
+
tokenStats = {
|
|
21069
|
+
estimatedTokens: budgetResult.totalTokens,
|
|
21070
|
+
maxTokens: options.maxTokens,
|
|
21071
|
+
pagesKept: budgetResult.kept.length,
|
|
21072
|
+
pagesDropped: budgetResult.dropped.length
|
|
21073
|
+
};
|
|
21074
|
+
}
|
|
20545
21075
|
return {
|
|
20546
21076
|
graphPath: paths.graphPath,
|
|
20547
21077
|
pageCount: sync.allPages.length,
|
|
@@ -20553,7 +21083,8 @@ async function compileVault(rootDir, options = {}) {
|
|
|
20553
21083
|
postPassApprovalId,
|
|
20554
21084
|
postPassApprovalDir,
|
|
20555
21085
|
promotedPageIds: sync.promotedPageIds,
|
|
20556
|
-
candidatePageCount: sync.candidatePageCount
|
|
21086
|
+
candidatePageCount: sync.candidatePageCount,
|
|
21087
|
+
tokenStats
|
|
20557
21088
|
};
|
|
20558
21089
|
}
|
|
20559
21090
|
async function queryVault(rootDir, options) {
|
|
@@ -20910,11 +21441,59 @@ ${orchestrationNotes.join("\n")}
|
|
|
20910
21441
|
};
|
|
20911
21442
|
}
|
|
20912
21443
|
async function searchVault(rootDir, query, limit = 5) {
|
|
20913
|
-
const { paths } = await loadVaultConfig(rootDir);
|
|
21444
|
+
const { paths, config } = await loadVaultConfig(rootDir);
|
|
20914
21445
|
if (!await fileExists(paths.searchDbPath)) {
|
|
20915
21446
|
await compileVault(rootDir, {});
|
|
20916
21447
|
}
|
|
20917
|
-
|
|
21448
|
+
const hybrid = config.search?.hybrid !== false;
|
|
21449
|
+
const ftsResults = searchPages(paths.searchDbPath, query, hybrid ? limit * 3 : limit);
|
|
21450
|
+
if (!hybrid || !await fileExists(paths.graphPath)) {
|
|
21451
|
+
return ftsResults.slice(0, limit);
|
|
21452
|
+
}
|
|
21453
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21454
|
+
if (!graph) {
|
|
21455
|
+
return ftsResults.slice(0, limit);
|
|
21456
|
+
}
|
|
21457
|
+
const semanticHits = await semanticPageSearch(rootDir, graph, query, limit * 3).catch(() => []);
|
|
21458
|
+
if (!semanticHits.length) {
|
|
21459
|
+
return ftsResults.slice(0, limit);
|
|
21460
|
+
}
|
|
21461
|
+
const merged = mergeSearchResults(ftsResults, semanticHits, limit);
|
|
21462
|
+
if (config.search?.rerank && merged.length > 1) {
|
|
21463
|
+
return rerankSearchResults(rootDir, query, merged, limit);
|
|
21464
|
+
}
|
|
21465
|
+
return merged;
|
|
21466
|
+
}
|
|
21467
|
+
async function rerankSearchResults(rootDir, query, results, limit) {
|
|
21468
|
+
const provider = await getProviderForTask(rootDir, "queryProvider");
|
|
21469
|
+
const candidates = results.slice(0, Math.min(results.length, 20)).map((r, i) => `[${i}] ${r.title} \u2014 ${r.snippet || r.path}`).join("\n");
|
|
21470
|
+
const prompt = `Given the search query: "${query}"
|
|
21471
|
+
|
|
21472
|
+
Rank these results by relevance (most relevant first).
|
|
21473
|
+
|
|
21474
|
+
${candidates}`;
|
|
21475
|
+
try {
|
|
21476
|
+
const indices = await provider.generateStructured(
|
|
21477
|
+
{ prompt, system: "You are a search result ranker." },
|
|
21478
|
+
z7.array(z7.number().int().nonnegative())
|
|
21479
|
+
);
|
|
21480
|
+
const reranked = [];
|
|
21481
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21482
|
+
for (const idx of indices) {
|
|
21483
|
+
if (idx >= 0 && idx < results.length && !seen.has(idx)) {
|
|
21484
|
+
seen.add(idx);
|
|
21485
|
+
reranked.push(results[idx]);
|
|
21486
|
+
}
|
|
21487
|
+
}
|
|
21488
|
+
for (let i = 0; i < results.length && reranked.length < limit; i++) {
|
|
21489
|
+
if (!seen.has(i)) {
|
|
21490
|
+
reranked.push(results[i]);
|
|
21491
|
+
}
|
|
21492
|
+
}
|
|
21493
|
+
return reranked.slice(0, limit);
|
|
21494
|
+
} catch {
|
|
21495
|
+
return results.slice(0, limit);
|
|
21496
|
+
}
|
|
20918
21497
|
}
|
|
20919
21498
|
async function ensureCompiledGraph(rootDir) {
|
|
20920
21499
|
const { paths } = await loadVaultConfig(rootDir);
|
|
@@ -21018,6 +21597,10 @@ async function listGodNodes(rootDir, limit = 10) {
|
|
|
21018
21597
|
const graph = await ensureCompiledGraph(rootDir);
|
|
21019
21598
|
return topGodNodes(graph, limit);
|
|
21020
21599
|
}
|
|
21600
|
+
async function blastRadiusVault(rootDir, target, options) {
|
|
21601
|
+
const graph = await ensureCompiledGraph(rootDir);
|
|
21602
|
+
return blastRadius(graph, target, options);
|
|
21603
|
+
}
|
|
21021
21604
|
async function listPages(rootDir) {
|
|
21022
21605
|
const { paths } = await loadVaultConfig(rootDir);
|
|
21023
21606
|
const graph = await readJsonFile(paths.graphPath);
|
|
@@ -21252,7 +21835,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
21252
21835
|
}
|
|
21253
21836
|
|
|
21254
21837
|
// src/mcp.ts
|
|
21255
|
-
var SERVER_VERSION = "0.7.
|
|
21838
|
+
var SERVER_VERSION = "0.7.25";
|
|
21256
21839
|
async function createMcpServer(rootDir) {
|
|
21257
21840
|
const server = new McpServer({
|
|
21258
21841
|
name: "swarmvault",
|
|
@@ -21402,6 +21985,19 @@ async function createMcpServer(rootDir) {
|
|
|
21402
21985
|
return asToolText(await listGodNodes(rootDir, limit ?? 10));
|
|
21403
21986
|
})
|
|
21404
21987
|
);
|
|
21988
|
+
server.registerTool(
|
|
21989
|
+
"blast_radius",
|
|
21990
|
+
{
|
|
21991
|
+
description: "Analyze the impact of changing a file or module by tracing reverse import edges.",
|
|
21992
|
+
inputSchema: {
|
|
21993
|
+
target: z8.string().min(1).describe("File path, module label, or module id"),
|
|
21994
|
+
maxDepth: z8.number().int().min(1).max(10).optional().describe("Maximum traversal depth (default 3)")
|
|
21995
|
+
}
|
|
21996
|
+
},
|
|
21997
|
+
safeHandler(async ({ target, maxDepth }) => {
|
|
21998
|
+
return asToolText(await blastRadiusVault(rootDir, target, { maxDepth: maxDepth ?? 3 }));
|
|
21999
|
+
})
|
|
22000
|
+
);
|
|
21405
22001
|
server.registerTool(
|
|
21406
22002
|
"query_vault",
|
|
21407
22003
|
{
|
|
@@ -21439,11 +22035,12 @@ async function createMcpServer(rootDir) {
|
|
|
21439
22035
|
{
|
|
21440
22036
|
description: "Compile source manifests into wiki pages, graph data, and search index.",
|
|
21441
22037
|
inputSchema: {
|
|
21442
|
-
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
|
|
22038
|
+
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes"),
|
|
22039
|
+
maxTokens: z8.number().int().min(1e3).optional().describe("Maximum token budget for wiki output")
|
|
21443
22040
|
}
|
|
21444
22041
|
},
|
|
21445
|
-
safeHandler(async ({ approve }) => {
|
|
21446
|
-
const result = await compileVault(rootDir, { approve: approve ?? false });
|
|
22042
|
+
safeHandler(async ({ approve, maxTokens }) => {
|
|
22043
|
+
const result = await compileVault(rootDir, { approve: approve ?? false, maxTokens });
|
|
21447
22044
|
return asToolText(result);
|
|
21448
22045
|
})
|
|
21449
22046
|
);
|
|
@@ -23078,7 +23675,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
|
23078
23675
|
async function stageSourceReviewForScope(rootDir, scope) {
|
|
23079
23676
|
const output = await buildSourceReviewStagedPage(rootDir, scope);
|
|
23080
23677
|
const approval = await stageGeneratedOutputPages(rootDir, [{ page: output.page, content: output.content, label: "source-review" }], {
|
|
23081
|
-
bundleType: "
|
|
23678
|
+
bundleType: "source-review",
|
|
23082
23679
|
title: `Source Review: ${scope.title}`
|
|
23083
23680
|
});
|
|
23084
23681
|
return {
|
|
@@ -23430,7 +24027,7 @@ async function stageSourceGuideForScope(rootDir, scope, options = {}) {
|
|
|
23430
24027
|
...guidedUpdates
|
|
23431
24028
|
],
|
|
23432
24029
|
{
|
|
23433
|
-
bundleType: "
|
|
24030
|
+
bundleType: "guided-session",
|
|
23434
24031
|
title: `Guided Session: ${scope.title}`,
|
|
23435
24032
|
sourceSessionId: session.sessionId
|
|
23436
24033
|
}
|
|
@@ -23708,11 +24305,11 @@ async function deleteManagedSource(rootDir, id) {
|
|
|
23708
24305
|
}
|
|
23709
24306
|
|
|
23710
24307
|
// src/viewer.ts
|
|
23711
|
-
import { execFile } from "child_process";
|
|
24308
|
+
import { execFile as execFile2 } from "child_process";
|
|
23712
24309
|
import fs23 from "fs/promises";
|
|
23713
24310
|
import http from "http";
|
|
23714
24311
|
import path28 from "path";
|
|
23715
|
-
import { promisify } from "util";
|
|
24312
|
+
import { promisify as promisify2 } from "util";
|
|
23716
24313
|
import matter11 from "gray-matter";
|
|
23717
24314
|
import mime2 from "mime-types";
|
|
23718
24315
|
|
|
@@ -24326,7 +24923,7 @@ async function getWatchStatus(rootDir) {
|
|
|
24326
24923
|
}
|
|
24327
24924
|
|
|
24328
24925
|
// src/viewer.ts
|
|
24329
|
-
var
|
|
24926
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
24330
24927
|
async function isReadableFile(absolutePath) {
|
|
24331
24928
|
try {
|
|
24332
24929
|
const stats = await fs23.stat(absolutePath);
|
|
@@ -24393,7 +24990,7 @@ async function ensureViewerDist(viewerDistDir) {
|
|
|
24393
24990
|
}
|
|
24394
24991
|
const viewerProjectDir = path28.dirname(viewerDistDir);
|
|
24395
24992
|
if (await fileExists(path28.join(viewerProjectDir, "package.json"))) {
|
|
24396
|
-
await
|
|
24993
|
+
await execFileAsync2("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
24397
24994
|
}
|
|
24398
24995
|
}
|
|
24399
24996
|
async function startGraphServer(rootDir, port, options = {}) {
|
|
@@ -24573,6 +25170,44 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
24573
25170
|
response.end(JSON.stringify(result));
|
|
24574
25171
|
return;
|
|
24575
25172
|
}
|
|
25173
|
+
if (url.pathname === "/api/clip" && request.method === "POST") {
|
|
25174
|
+
const body = await readJsonBody(request);
|
|
25175
|
+
const clipUrl = typeof body.url === "string" ? body.url.trim() : "";
|
|
25176
|
+
if (!clipUrl) {
|
|
25177
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
25178
|
+
response.end(JSON.stringify({ error: "Missing url field." }));
|
|
25179
|
+
return;
|
|
25180
|
+
}
|
|
25181
|
+
const manifest = await ingestInput(rootDir, clipUrl);
|
|
25182
|
+
response.writeHead(200, { "content-type": "application/json", "access-control-allow-origin": "*" });
|
|
25183
|
+
response.end(JSON.stringify({ ok: true, sourceId: manifest.sourceId, title: manifest.title }));
|
|
25184
|
+
return;
|
|
25185
|
+
}
|
|
25186
|
+
if (url.pathname === "/api/clip" && request.method === "OPTIONS") {
|
|
25187
|
+
response.writeHead(204, {
|
|
25188
|
+
"access-control-allow-origin": "*",
|
|
25189
|
+
"access-control-allow-methods": "POST, OPTIONS",
|
|
25190
|
+
"access-control-allow-headers": "content-type"
|
|
25191
|
+
});
|
|
25192
|
+
response.end();
|
|
25193
|
+
return;
|
|
25194
|
+
}
|
|
25195
|
+
if (url.pathname === "/api/bookmarklet") {
|
|
25196
|
+
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)))`;
|
|
25197
|
+
response.writeHead(200, { "content-type": "text/html" });
|
|
25198
|
+
response.end(
|
|
25199
|
+
[
|
|
25200
|
+
"<!doctype html><html><head><title>SwarmVault Clipper</title></head><body>",
|
|
25201
|
+
"<h1>SwarmVault Clipper</h1>",
|
|
25202
|
+
`<p>Drag this link to your bookmarks bar:</p>`,
|
|
25203
|
+
`<p style="font-size:1.5em"><a href="${script.replace(/&/g, "&").replace(/"/g, """)}">Clip to SwarmVault</a></p>`,
|
|
25204
|
+
`<p>When clicked on any page, it sends the URL to your running SwarmVault instance for ingestion.</p>`,
|
|
25205
|
+
`<p>Server: <code>http://localhost:${effectivePort}</code></p>`,
|
|
25206
|
+
"</body></html>"
|
|
25207
|
+
].join("\n")
|
|
25208
|
+
);
|
|
25209
|
+
return;
|
|
25210
|
+
}
|
|
24576
25211
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
24577
25212
|
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
24578
25213
|
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
@@ -24691,7 +25326,10 @@ export {
|
|
|
24691
25326
|
addManagedSource,
|
|
24692
25327
|
archiveCandidate,
|
|
24693
25328
|
assertProviderCapability,
|
|
25329
|
+
autoCommitWikiChanges,
|
|
24694
25330
|
benchmarkVault,
|
|
25331
|
+
blastRadius,
|
|
25332
|
+
blastRadiusVault,
|
|
24695
25333
|
bootstrapDemo,
|
|
24696
25334
|
compileVault,
|
|
24697
25335
|
createMcpServer,
|
|
@@ -24700,10 +25338,13 @@ export {
|
|
|
24700
25338
|
defaultVaultConfig,
|
|
24701
25339
|
defaultVaultSchema,
|
|
24702
25340
|
deleteManagedSource,
|
|
25341
|
+
estimatePageTokens,
|
|
25342
|
+
estimateTokens,
|
|
24703
25343
|
explainGraphVault,
|
|
24704
25344
|
exploreVault,
|
|
24705
25345
|
exportGraphFormat,
|
|
24706
25346
|
exportGraphHtml,
|
|
25347
|
+
exportGraphReportHtml,
|
|
24707
25348
|
exportObsidianCanvas,
|
|
24708
25349
|
exportObsidianVault,
|
|
24709
25350
|
getGitHookStatus,
|
|
@@ -24760,6 +25401,7 @@ export {
|
|
|
24760
25401
|
startMcpServer,
|
|
24761
25402
|
syncTrackedRepos,
|
|
24762
25403
|
syncTrackedReposForWatch,
|
|
25404
|
+
trimToTokenBudget,
|
|
24763
25405
|
uninstallGitHooks,
|
|
24764
25406
|
watchVault
|
|
24765
25407
|
};
|