@gmickel/gno 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skill/SKILL.md +11 -1
- package/assets/skill/cli-reference.md +9 -1
- package/assets/skill/mcp-reference.md +16 -0
- package/package.json +2 -2
- package/src/cli/commands/graph.ts +237 -4
- package/src/cli/program.ts +31 -0
- package/src/core/graph-analysis.ts +201 -0
- package/src/mcp/tools/index.ts +58 -4
- package/src/mcp/tools/links.ts +304 -7
- package/src/mcp/tools/query.ts +2 -0
- package/src/pipeline/explain.ts +2 -0
- package/src/pipeline/fusion.ts +28 -0
- package/src/pipeline/graph-retrieval.ts +368 -0
- package/src/pipeline/hybrid.ts +45 -3
- package/src/pipeline/types.ts +18 -1
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/pages/GraphView.tsx +237 -16
- package/src/serve/public/pages/Search.tsx +1 -0
- package/src/serve/routes/api.ts +2 -0
- package/src/store/sqlite/adapter.ts +343 -16
- package/src/store/types.ts +120 -0
|
@@ -51,6 +51,26 @@ interface GraphNode {
|
|
|
51
51
|
collection: string;
|
|
52
52
|
relPath: string;
|
|
53
53
|
degree: number;
|
|
54
|
+
communityId?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface GraphReportNode {
|
|
58
|
+
id: string;
|
|
59
|
+
uri: string;
|
|
60
|
+
title: string | null;
|
|
61
|
+
collection: string;
|
|
62
|
+
relPath: string;
|
|
63
|
+
degree: number;
|
|
64
|
+
communityId?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface GraphCommunity {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
size: number;
|
|
71
|
+
edgeCount: number;
|
|
72
|
+
density: number;
|
|
73
|
+
topNodes: GraphReportNode[];
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
interface GraphLink {
|
|
@@ -58,6 +78,17 @@ interface GraphLink {
|
|
|
58
78
|
target: string;
|
|
59
79
|
type: "wiki" | "markdown" | "similar";
|
|
60
80
|
weight: number;
|
|
81
|
+
confidence: "explicit" | "inferred" | "ambiguous" | "similarity";
|
|
82
|
+
audit: {
|
|
83
|
+
resolution:
|
|
84
|
+
| "exact-title"
|
|
85
|
+
| "exact-path"
|
|
86
|
+
| "path-fallback"
|
|
87
|
+
| "ambiguous-fallback"
|
|
88
|
+
| "similarity";
|
|
89
|
+
matchCount?: number;
|
|
90
|
+
score?: number;
|
|
91
|
+
};
|
|
61
92
|
}
|
|
62
93
|
|
|
63
94
|
interface GraphMeta {
|
|
@@ -78,9 +109,49 @@ interface GraphMeta {
|
|
|
78
109
|
warnings: string[];
|
|
79
110
|
}
|
|
80
111
|
|
|
112
|
+
interface GraphReport {
|
|
113
|
+
hubs: GraphReportNode[];
|
|
114
|
+
bridgeCandidates: GraphReportNode[];
|
|
115
|
+
isolated: {
|
|
116
|
+
total: number;
|
|
117
|
+
examples: GraphReportNode[];
|
|
118
|
+
};
|
|
119
|
+
unresolvedLinks: {
|
|
120
|
+
total: number;
|
|
121
|
+
byType: {
|
|
122
|
+
wiki: number;
|
|
123
|
+
markdown: number;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
edgeTypes: {
|
|
127
|
+
wiki: number;
|
|
128
|
+
markdown: number;
|
|
129
|
+
similar: number;
|
|
130
|
+
};
|
|
131
|
+
edgeConfidence: {
|
|
132
|
+
explicit: number;
|
|
133
|
+
inferred: number;
|
|
134
|
+
ambiguous: number;
|
|
135
|
+
similarity: number;
|
|
136
|
+
};
|
|
137
|
+
audit: {
|
|
138
|
+
inferredEdges: number;
|
|
139
|
+
ambiguousEdges: number;
|
|
140
|
+
similarityEdges: number;
|
|
141
|
+
};
|
|
142
|
+
communities: {
|
|
143
|
+
total: number;
|
|
144
|
+
algorithm: "deterministic-label-propagation";
|
|
145
|
+
skipped: boolean;
|
|
146
|
+
assignments: Record<string, string>;
|
|
147
|
+
top: GraphCommunity[];
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
81
151
|
interface GraphResponse {
|
|
82
152
|
nodes: GraphNode[];
|
|
83
153
|
links: GraphLink[];
|
|
154
|
+
report: GraphReport;
|
|
84
155
|
meta: GraphMeta;
|
|
85
156
|
}
|
|
86
157
|
|
|
@@ -111,6 +182,8 @@ const COLORS = {
|
|
|
111
182
|
edgeWiki: "rgba(77, 184, 168, 0.4)", // Teal threads
|
|
112
183
|
edgeMarkdown: "rgba(77, 184, 168, 0.25)", // Fainter teal
|
|
113
184
|
edgeSimilar: "rgba(212, 160, 83, 0.3)", // Gold similarity
|
|
185
|
+
edgeInferred: "rgba(124, 158, 178, 0.35)",
|
|
186
|
+
edgeAmbiguous: "rgba(245, 215, 142, 0.55)",
|
|
114
187
|
|
|
115
188
|
// Background
|
|
116
189
|
canvas: "#050505", // Deep black - observatory darkness
|
|
@@ -136,6 +209,22 @@ function getCollectionColor(collection: string): string {
|
|
|
136
209
|
return palette[Math.abs(hash) % palette.length]!;
|
|
137
210
|
}
|
|
138
211
|
|
|
212
|
+
function getCommunityColor(communityId: string | undefined): string | null {
|
|
213
|
+
if (!communityId) return null;
|
|
214
|
+
const palette = [
|
|
215
|
+
"#4db8a8",
|
|
216
|
+
"#d4a053",
|
|
217
|
+
"#6ba3d6",
|
|
218
|
+
"#a8c686",
|
|
219
|
+
"#c9a7c7",
|
|
220
|
+
"#e2a76f",
|
|
221
|
+
"#f5d78e",
|
|
222
|
+
"#7c9eb2",
|
|
223
|
+
];
|
|
224
|
+
const index = Math.max(0, Number.parseInt(communityId.slice(1), 10) - 1);
|
|
225
|
+
return palette[index % palette.length] ?? null;
|
|
226
|
+
}
|
|
227
|
+
|
|
139
228
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
140
229
|
// Loading & Empty States
|
|
141
230
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -240,6 +329,7 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
240
329
|
// Filter state
|
|
241
330
|
const [selectedCollection, setSelectedCollection] = useState<string>("_all");
|
|
242
331
|
const [includeSimilar, setIncludeSimilar] = useState(false);
|
|
332
|
+
const [selectedCommunity, setSelectedCommunity] = useState<string>("_all");
|
|
243
333
|
|
|
244
334
|
// UI state
|
|
245
335
|
const [showTruncationBanner, setShowTruncationBanner] = useState(true);
|
|
@@ -289,6 +379,7 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
289
379
|
setLoading(true);
|
|
290
380
|
setError(null);
|
|
291
381
|
setShowTruncationBanner(true);
|
|
382
|
+
setSelectedCommunity("_all");
|
|
292
383
|
clearZoomTimeouts();
|
|
293
384
|
|
|
294
385
|
const params = new URLSearchParams();
|
|
@@ -369,24 +460,47 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
369
460
|
const forceGraphData = useMemo(() => {
|
|
370
461
|
if (!graphData) return { nodes: [], links: [] };
|
|
371
462
|
|
|
463
|
+
const visibleNodeIds = new Set(
|
|
464
|
+
graphData.nodes
|
|
465
|
+
.filter(
|
|
466
|
+
(node) =>
|
|
467
|
+
selectedCommunity === "_all" ||
|
|
468
|
+
node.communityId === selectedCommunity
|
|
469
|
+
)
|
|
470
|
+
.map((node) => node.id)
|
|
471
|
+
);
|
|
472
|
+
|
|
372
473
|
return {
|
|
373
|
-
nodes: graphData.nodes
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
474
|
+
nodes: graphData.nodes
|
|
475
|
+
.filter((node) => visibleNodeIds.has(node.id))
|
|
476
|
+
.map((n) => ({
|
|
477
|
+
...n,
|
|
478
|
+
// Add computed properties for rendering
|
|
479
|
+
color:
|
|
480
|
+
getCommunityColor(n.communityId) ??
|
|
481
|
+
getCollectionColor(n.collection),
|
|
482
|
+
size: Math.max(4, Math.min(20, 4 + Math.sqrt(n.degree) * 2)),
|
|
483
|
+
})),
|
|
484
|
+
links: graphData.links
|
|
485
|
+
.filter(
|
|
486
|
+
(link) =>
|
|
487
|
+
visibleNodeIds.has(link.source) && visibleNodeIds.has(link.target)
|
|
488
|
+
)
|
|
489
|
+
.map((l) => ({
|
|
490
|
+
...l,
|
|
491
|
+
color:
|
|
492
|
+
l.confidence === "ambiguous"
|
|
493
|
+
? COLORS.edgeAmbiguous
|
|
494
|
+
: l.confidence === "inferred"
|
|
495
|
+
? COLORS.edgeInferred
|
|
496
|
+
: l.type === "similar"
|
|
497
|
+
? COLORS.edgeSimilar
|
|
498
|
+
: l.type === "wiki"
|
|
499
|
+
? COLORS.edgeWiki
|
|
500
|
+
: COLORS.edgeMarkdown,
|
|
501
|
+
})),
|
|
388
502
|
};
|
|
389
|
-
}, [graphData]);
|
|
503
|
+
}, [graphData, selectedCommunity]);
|
|
390
504
|
|
|
391
505
|
// Node canvas rendering for custom aesthetics
|
|
392
506
|
// biome-ignore lint: dynamic import typing
|
|
@@ -494,6 +608,27 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
494
608
|
<span className="hidden sm:inline">Dashboard</span>
|
|
495
609
|
</Button>
|
|
496
610
|
|
|
611
|
+
{graphData &&
|
|
612
|
+
!graphData.report.communities.skipped &&
|
|
613
|
+
graphData.report.communities.top.length > 0 && (
|
|
614
|
+
<Select
|
|
615
|
+
onValueChange={setSelectedCommunity}
|
|
616
|
+
value={selectedCommunity}
|
|
617
|
+
>
|
|
618
|
+
<SelectTrigger className="h-8 w-[170px] border-border/50 bg-background/50 text-xs">
|
|
619
|
+
<SelectValue placeholder="All communities" />
|
|
620
|
+
</SelectTrigger>
|
|
621
|
+
<SelectContent>
|
|
622
|
+
<SelectItem value="_all">All communities</SelectItem>
|
|
623
|
+
{graphData.report.communities.top.map((community) => (
|
|
624
|
+
<SelectItem key={community.id} value={community.id}>
|
|
625
|
+
{community.id} · {community.size} docs
|
|
626
|
+
</SelectItem>
|
|
627
|
+
))}
|
|
628
|
+
</SelectContent>
|
|
629
|
+
</Select>
|
|
630
|
+
)}
|
|
631
|
+
|
|
497
632
|
<div className="h-4 w-px bg-border/50" />
|
|
498
633
|
|
|
499
634
|
<h1 className="font-serif text-lg text-[#4db8a8]">Knowledge Graph</h1>
|
|
@@ -578,6 +713,36 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
578
713
|
{graphData.links.length.toLocaleString()}
|
|
579
714
|
</span>{" "}
|
|
580
715
|
edges
|
|
716
|
+
<span className="text-border">•</span>
|
|
717
|
+
<span className="text-[#f5d78e]">
|
|
718
|
+
{graphData.report.hubs[0]?.degree ?? 0}
|
|
719
|
+
</span>{" "}
|
|
720
|
+
top degree
|
|
721
|
+
<span className="text-border">•</span>
|
|
722
|
+
<span className="text-[#d4a053]">
|
|
723
|
+
{graphData.report.unresolvedLinks.total.toLocaleString()}
|
|
724
|
+
</span>{" "}
|
|
725
|
+
unresolved
|
|
726
|
+
<span className="text-border">•</span>
|
|
727
|
+
<span className="text-[#4db8a8]">
|
|
728
|
+
{graphData.report.isolated.total.toLocaleString()}
|
|
729
|
+
</span>{" "}
|
|
730
|
+
isolated
|
|
731
|
+
<span className="text-border">•</span>
|
|
732
|
+
<span className="text-[#7c9eb2]">
|
|
733
|
+
{graphData.report.audit.inferredEdges.toLocaleString()}
|
|
734
|
+
</span>{" "}
|
|
735
|
+
inferred
|
|
736
|
+
<span className="text-border">•</span>
|
|
737
|
+
<span className="text-[#f5d78e]">
|
|
738
|
+
{graphData.report.audit.ambiguousEdges.toLocaleString()}
|
|
739
|
+
</span>{" "}
|
|
740
|
+
ambiguous
|
|
741
|
+
<span className="text-border">•</span>
|
|
742
|
+
<span className="text-[#6ba3d6]">
|
|
743
|
+
{graphData.report.communities.total.toLocaleString()}
|
|
744
|
+
</span>{" "}
|
|
745
|
+
communities
|
|
581
746
|
</div>
|
|
582
747
|
)}
|
|
583
748
|
|
|
@@ -611,6 +776,48 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
611
776
|
/>
|
|
612
777
|
)}
|
|
613
778
|
|
|
779
|
+
{graphData &&
|
|
780
|
+
!loading &&
|
|
781
|
+
!graphData.report.communities.skipped &&
|
|
782
|
+
graphData.report.communities.top.length > 0 && (
|
|
783
|
+
<div className="absolute right-4 bottom-4 z-20 hidden max-w-xs rounded-lg border border-border/30 bg-[#0f1115]/85 p-3 shadow-lg backdrop-blur-md md:block">
|
|
784
|
+
<p className="mb-2 font-mono text-[#4db8a8] text-[10px] uppercase tracking-[0.12em]">
|
|
785
|
+
Communities
|
|
786
|
+
</p>
|
|
787
|
+
<div className="space-y-1.5">
|
|
788
|
+
{graphData.report.communities.top.slice(0, 6).map((community) => (
|
|
789
|
+
<button
|
|
790
|
+
className={cn(
|
|
791
|
+
"flex w-full items-center gap-2 rounded px-1.5 py-1 text-left text-xs transition-colors hover:bg-white/5",
|
|
792
|
+
selectedCommunity === community.id && "bg-white/10"
|
|
793
|
+
)}
|
|
794
|
+
key={community.id}
|
|
795
|
+
onClick={() =>
|
|
796
|
+
setSelectedCommunity((current) =>
|
|
797
|
+
current === community.id ? "_all" : community.id
|
|
798
|
+
)
|
|
799
|
+
}
|
|
800
|
+
type="button"
|
|
801
|
+
>
|
|
802
|
+
<span
|
|
803
|
+
className="size-2.5 shrink-0 rounded-full"
|
|
804
|
+
style={{
|
|
805
|
+
backgroundColor:
|
|
806
|
+
getCommunityColor(community.id) ?? COLORS.nodeDefault,
|
|
807
|
+
}}
|
|
808
|
+
/>
|
|
809
|
+
<span className="min-w-0 flex-1 truncate text-muted-foreground">
|
|
810
|
+
{community.label}
|
|
811
|
+
</span>
|
|
812
|
+
<span className="font-mono text-[#d4a053]">
|
|
813
|
+
{community.size}
|
|
814
|
+
</span>
|
|
815
|
+
</button>
|
|
816
|
+
))}
|
|
817
|
+
</div>
|
|
818
|
+
</div>
|
|
819
|
+
)}
|
|
820
|
+
|
|
614
821
|
{/* Graph canvas */}
|
|
615
822
|
<div className="h-full pt-12">
|
|
616
823
|
{loading ? (
|
|
@@ -718,6 +925,20 @@ export default function GraphView({ navigate }: PageProps) {
|
|
|
718
925
|
<span className="text-muted-foreground">Similarity</span>
|
|
719
926
|
</div>
|
|
720
927
|
)}
|
|
928
|
+
<div className="flex items-center gap-2">
|
|
929
|
+
<span
|
|
930
|
+
className="size-2 rounded-full"
|
|
931
|
+
style={{ backgroundColor: COLORS.edgeInferred }}
|
|
932
|
+
/>
|
|
933
|
+
<span className="text-muted-foreground">Inferred</span>
|
|
934
|
+
</div>
|
|
935
|
+
<div className="flex items-center gap-2">
|
|
936
|
+
<span
|
|
937
|
+
className="size-2 rounded-full"
|
|
938
|
+
style={{ backgroundColor: COLORS.edgeAmbiguous }}
|
|
939
|
+
/>
|
|
940
|
+
<span className="text-muted-foreground">Ambiguous</span>
|
|
941
|
+
</div>
|
|
721
942
|
</div>
|
|
722
943
|
</div>
|
|
723
944
|
</div>
|
|
@@ -388,6 +388,7 @@ export default function Search({ navigate }: PageProps) {
|
|
|
388
388
|
if (!useBm25) {
|
|
389
389
|
body.noExpand = depthPolicy.noExpand;
|
|
390
390
|
body.noRerank = depthPolicy.noRerank;
|
|
391
|
+
body.noGraph = thoroughness === "fast";
|
|
391
392
|
if (queryModes.length > 0) {
|
|
392
393
|
body.queryModes = queryModes;
|
|
393
394
|
}
|
package/src/serve/routes/api.ts
CHANGED
|
@@ -172,6 +172,7 @@ export interface QueryRequestBody {
|
|
|
172
172
|
queryModes?: QueryModeInput[];
|
|
173
173
|
noExpand?: boolean;
|
|
174
174
|
noRerank?: boolean;
|
|
175
|
+
noGraph?: boolean;
|
|
175
176
|
/** Comma-separated tags - filter to docs having ALL (AND) */
|
|
176
177
|
tagsAll?: string;
|
|
177
178
|
/** Comma-separated tags - filter to docs having ANY (OR) */
|
|
@@ -3310,6 +3311,7 @@ export async function handleQuery(
|
|
|
3310
3311
|
queryModes: normalizedQueryModes,
|
|
3311
3312
|
noExpand: body.noExpand,
|
|
3312
3313
|
noRerank: body.noRerank,
|
|
3314
|
+
noGraph: body.noGraph,
|
|
3313
3315
|
tagsAll,
|
|
3314
3316
|
tagsAny,
|
|
3315
3317
|
since: body.since,
|