stellavault 0.7.1 → 0.7.3
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/README.md +2 -1
- package/dist/stellavault.js +343 -263
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -232,9 +232,10 @@ node tests/stress.mjs 500 # Test with 500 synthetic documents
|
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
Key optimizations:
|
|
235
|
+
- **HNSW graph building** — sqlite-vec KNN for 200+ docs (O(n·K·log n) vs O(n²))
|
|
235
236
|
- Pre-normalized vectors: cosine similarity → single dot product
|
|
236
237
|
- Batched embedding loading (500/batch, prevents RAM overflow)
|
|
237
|
-
- Upper-triangle
|
|
238
|
+
- Upper-triangle brute-force for small vaults (< 200 docs)
|
|
238
239
|
- O(n) K-Means centroid updates with typed arrays
|
|
239
240
|
|
|
240
241
|
---
|
package/dist/stellavault.js
CHANGED
|
@@ -645,33 +645,50 @@ async function buildGraphData(store, options = {}) {
|
|
|
645
645
|
]);
|
|
646
646
|
const edges = [];
|
|
647
647
|
const edgeCounts = /* @__PURE__ */ new Map();
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
neighbors[j].push({ peer: i, sim });
|
|
648
|
+
const USE_HNSW_THRESHOLD = 200;
|
|
649
|
+
const docsWithVecs = docs.filter((d) => embeddings.has(d.id));
|
|
650
|
+
if (docsWithVecs.length > USE_HNSW_THRESHOLD) {
|
|
651
|
+
for (const doc of docsWithVecs) {
|
|
652
|
+
const vec = embeddings.get(doc.id);
|
|
653
|
+
const neighbors = await store.findDocumentNeighbors(vec, maxEdgesPerNode + 1);
|
|
654
|
+
for (const { documentId: targetId, similarity } of neighbors) {
|
|
655
|
+
if (targetId === doc.id)
|
|
656
|
+
continue;
|
|
657
|
+
if (similarity < edgeThreshold)
|
|
658
|
+
continue;
|
|
659
|
+
const edgeKey = [doc.id, targetId].sort().join(":");
|
|
660
|
+
if (!edgeCounts.has(edgeKey)) {
|
|
661
|
+
edges.push({ source: doc.id, target: targetId, weight: similarity });
|
|
662
|
+
edgeCounts.set(edgeKey, 1);
|
|
663
|
+
}
|
|
665
664
|
}
|
|
666
665
|
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
666
|
+
} else {
|
|
667
|
+
const normalizedVecs = /* @__PURE__ */ new Map();
|
|
668
|
+
for (const doc of docsWithVecs) {
|
|
669
|
+
normalizedVecs.set(doc.id, normalizeVector([...embeddings.get(doc.id)]));
|
|
670
|
+
}
|
|
671
|
+
const docIds = [...normalizedVecs.keys()];
|
|
672
|
+
const vecArray = docIds.map((id) => normalizedVecs.get(id));
|
|
673
|
+
const n = docIds.length;
|
|
674
|
+
const neighbors = Array.from({ length: n }, () => []);
|
|
675
|
+
for (let i = 0; i < n; i++) {
|
|
676
|
+
for (let j = i + 1; j < n; j++) {
|
|
677
|
+
const sim = dotProduct(vecArray[i], vecArray[j]);
|
|
678
|
+
if (sim >= edgeThreshold) {
|
|
679
|
+
neighbors[i].push({ peer: j, sim });
|
|
680
|
+
neighbors[j].push({ peer: i, sim });
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
for (let i = 0; i < n; i++) {
|
|
685
|
+
neighbors[i].sort((a, b) => b.sim - a.sim);
|
|
686
|
+
for (const { peer: j, sim } of neighbors[i].slice(0, maxEdgesPerNode)) {
|
|
687
|
+
const edgeKey = i < j ? `${i}:${j}` : `${j}:${i}`;
|
|
688
|
+
if (!edgeCounts.has(edgeKey)) {
|
|
689
|
+
edges.push({ source: docIds[i], target: docIds[j], weight: sim });
|
|
690
|
+
edgeCounts.set(edgeKey, 1);
|
|
691
|
+
}
|
|
675
692
|
}
|
|
676
693
|
}
|
|
677
694
|
}
|
|
@@ -705,16 +722,16 @@ async function buildGraphData(store, options = {}) {
|
|
|
705
722
|
nodeCount: folderCounts.get(i) ?? 0
|
|
706
723
|
}));
|
|
707
724
|
} else {
|
|
708
|
-
const
|
|
709
|
-
const vectors =
|
|
710
|
-
const k = Math.min(Math.max(5, Math.round(Math.sqrt(
|
|
725
|
+
const docIds = docs.filter((d) => embeddings.has(d.id)).map((d) => d.id);
|
|
726
|
+
const vectors = docIds.map((id) => embeddings.get(id));
|
|
727
|
+
const k = Math.min(Math.max(5, Math.round(Math.sqrt(docIds.length / 5))), 10);
|
|
711
728
|
const assignments = kMeans(vectors, k);
|
|
712
729
|
const clusterDocInfos = /* @__PURE__ */ new Map();
|
|
713
|
-
for (let i = 0; i <
|
|
730
|
+
for (let i = 0; i < docIds.length; i++) {
|
|
714
731
|
const cId = assignments[i];
|
|
715
732
|
if (!clusterDocInfos.has(cId))
|
|
716
733
|
clusterDocInfos.set(cId, []);
|
|
717
|
-
const doc = docs.find((d) => d.id ===
|
|
734
|
+
const doc = docs.find((d) => d.id === docIds[i]);
|
|
718
735
|
if (doc)
|
|
719
736
|
clusterDocInfos.get(cId).push({ id: doc.id, title: doc.title });
|
|
720
737
|
}
|
|
@@ -735,8 +752,8 @@ async function buildGraphData(store, options = {}) {
|
|
|
735
752
|
});
|
|
736
753
|
}
|
|
737
754
|
assignmentMap = /* @__PURE__ */ new Map();
|
|
738
|
-
for (let i = 0; i <
|
|
739
|
-
assignmentMap.set(
|
|
755
|
+
for (let i = 0; i < docIds.length; i++) {
|
|
756
|
+
assignmentMap.set(docIds[i], assignments[i]);
|
|
740
757
|
}
|
|
741
758
|
}
|
|
742
759
|
const connectionCounts = /* @__PURE__ */ new Map();
|
|
@@ -3258,6 +3275,21 @@ function createSqliteVecStore(dbPath, dimensions = 384) {
|
|
|
3258
3275
|
}
|
|
3259
3276
|
return result;
|
|
3260
3277
|
},
|
|
3278
|
+
async findDocumentNeighbors(embedding, limit) {
|
|
3279
|
+
const rows = db.prepare(`
|
|
3280
|
+
SELECT c.document_id, MIN(ce.distance) as distance
|
|
3281
|
+
FROM chunk_embeddings ce
|
|
3282
|
+
JOIN chunks c ON c.id = ce.chunk_id
|
|
3283
|
+
WHERE ce.embedding MATCH ?
|
|
3284
|
+
GROUP BY c.document_id
|
|
3285
|
+
ORDER BY distance
|
|
3286
|
+
LIMIT ?
|
|
3287
|
+
`).all(float32Buffer(embedding), limit * 2);
|
|
3288
|
+
return rows.slice(0, limit).map((r) => ({
|
|
3289
|
+
documentId: r.document_id,
|
|
3290
|
+
similarity: 1 / (1 + r.distance)
|
|
3291
|
+
}));
|
|
3292
|
+
},
|
|
3261
3293
|
async close() {
|
|
3262
3294
|
db.close();
|
|
3263
3295
|
},
|
|
@@ -4746,7 +4778,7 @@ function createMcpServer(options) {
|
|
|
4746
4778
|
const askTool = createAskTool(searchEngine, vaultPath);
|
|
4747
4779
|
const generateDraftTool = createGenerateDraftTool(searchEngine, vaultPath);
|
|
4748
4780
|
const agenticTools = embedder ? createAgenticGraphTools(store, embedder, vaultPath) : [];
|
|
4749
|
-
const server = new Server({ name: "stellavault", version: "0.7.
|
|
4781
|
+
const server = new Server({ name: "stellavault", version: "0.7.3" }, { capabilities: { tools: {} } });
|
|
4750
4782
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
4751
4783
|
tools: [
|
|
4752
4784
|
searchToolDef,
|
|
@@ -5608,6 +5640,250 @@ ${content}`;
|
|
|
5608
5640
|
return router;
|
|
5609
5641
|
}
|
|
5610
5642
|
|
|
5643
|
+
// packages/core/dist/api/routes/profile-card.js
|
|
5644
|
+
init_graph_data();
|
|
5645
|
+
import { Router as Router4 } from "express";
|
|
5646
|
+
function createProfileCardRouter(opts) {
|
|
5647
|
+
const { store, graphCaches, GRAPH_CACHE_TTL } = opts;
|
|
5648
|
+
const router = Router4();
|
|
5649
|
+
router.get("/profile-card", async (_req, res) => {
|
|
5650
|
+
try {
|
|
5651
|
+
const mode = String(_req.query.mode ?? "") === "folder" ? "folder" : "semantic";
|
|
5652
|
+
const cached = graphCaches.get(mode);
|
|
5653
|
+
if (!cached || Date.now() - cached.cachedAt > GRAPH_CACHE_TTL) {
|
|
5654
|
+
const data = await buildGraphData(store, { mode });
|
|
5655
|
+
graphCaches.set(mode, { data, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), cachedAt: Date.now() });
|
|
5656
|
+
}
|
|
5657
|
+
const graphData = graphCaches.get(mode).data;
|
|
5658
|
+
const topics = await store.getTopics();
|
|
5659
|
+
const stats = await store.getStats();
|
|
5660
|
+
const top6 = [...graphData.clusters].sort((a, b) => b.nodeCount - a.nodeCount).slice(0, 6);
|
|
5661
|
+
const maxCount = Math.max(1, ...top6.map((c) => c.nodeCount));
|
|
5662
|
+
const W = 800, H = 420;
|
|
5663
|
+
const radarCx = 200, radarCy = 220, radarR = 100;
|
|
5664
|
+
const radarPoints = top6.map((c, i) => {
|
|
5665
|
+
const angle = Math.PI * 2 * i / top6.length - Math.PI / 2;
|
|
5666
|
+
const r = radarR * (c.nodeCount / maxCount);
|
|
5667
|
+
return {
|
|
5668
|
+
x: radarCx + r * Math.cos(angle),
|
|
5669
|
+
y: radarCy + r * Math.sin(angle),
|
|
5670
|
+
lx: radarCx + (radarR + 20) * Math.cos(angle),
|
|
5671
|
+
ly: radarCy + (radarR + 20) * Math.sin(angle),
|
|
5672
|
+
label: c.label.split(",")[0].trim().slice(0, 12),
|
|
5673
|
+
color: c.color
|
|
5674
|
+
};
|
|
5675
|
+
});
|
|
5676
|
+
const radarPath = radarPoints.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ") + "Z";
|
|
5677
|
+
const gridPaths = [0.33, 0.66, 1].map((s) => top6.map((_, i) => {
|
|
5678
|
+
const a = Math.PI * 2 * i / top6.length - Math.PI / 2;
|
|
5679
|
+
return `${i === 0 ? "M" : "L"}${radarCx + radarR * s * Math.cos(a)},${radarCy + radarR * s * Math.sin(a)}`;
|
|
5680
|
+
}).join(" ") + "Z");
|
|
5681
|
+
const tags20 = topics.slice(0, 20);
|
|
5682
|
+
const maxTag = Math.max(1, ...tags20.map((t2) => t2.count));
|
|
5683
|
+
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5684
|
+
const tagEls = tags20.map((t2, i) => {
|
|
5685
|
+
const sz = 10 + 14 * (t2.count / maxTag);
|
|
5686
|
+
const x = 480 + Math.floor(i / 2) % 5 * 60;
|
|
5687
|
+
const y = 140 + i % 2 * 30 + Math.floor(i / 10) * 70;
|
|
5688
|
+
const op = 0.5 + 0.5 * (t2.count / maxTag);
|
|
5689
|
+
return `<text x="${x}" y="${y}" font-size="${sz}" fill="#88aaff" opacity="${op}" font-family="monospace">#${esc(t2.topic)}</text>`;
|
|
5690
|
+
}).join("\n ");
|
|
5691
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
|
|
5692
|
+
<defs>
|
|
5693
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5694
|
+
<stop offset="0%" stop-color="#0d1028"/><stop offset="100%" stop-color="#050510"/>
|
|
5695
|
+
</linearGradient>
|
|
5696
|
+
<linearGradient id="rf" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5697
|
+
<stop offset="0%" stop-color="#6366f1" stop-opacity="0.3"/><stop offset="100%" stop-color="#06b6d4" stop-opacity="0.15"/>
|
|
5698
|
+
</linearGradient>
|
|
5699
|
+
</defs>
|
|
5700
|
+
<rect width="${W}" height="${H}" rx="16" fill="url(#bg)"/>
|
|
5701
|
+
<rect width="${W}" height="${H}" rx="16" fill="none" stroke="#6366f140"/>
|
|
5702
|
+
<text x="30" y="40" font-size="20" font-weight="700" fill="#c0c0f0" font-family="system-ui">\u{1F9E0} Knowledge Universe</text>
|
|
5703
|
+
<text x="30" y="65" font-size="13" fill="#556" font-family="monospace">${stats.documentCount} docs \xB7 ${graphData.clusters.length} clusters \xB7 ${graphData.edges.length} connections</text>
|
|
5704
|
+
<line x1="30" y1="80" x2="${W - 30}" y2="80" stroke="#6366f120"/>
|
|
5705
|
+
<text x="${radarCx}" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">KNOWLEDGE DISTRIBUTION</text>
|
|
5706
|
+
${gridPaths.map((p) => `<path d="${p}" fill="none" stroke="#6366f115" stroke-width="0.5"/>`).join("\n ")}
|
|
5707
|
+
${radarPoints.map((p) => `<line x1="${radarCx}" y1="${radarCy}" x2="${p.lx}" y2="${p.ly}" stroke="#6366f110" stroke-width="0.5"/>`).join("\n ")}
|
|
5708
|
+
<path d="${radarPath}" fill="url(#rf)" stroke="#818cf8" stroke-width="1.5"/>
|
|
5709
|
+
${radarPoints.map((p) => `<circle cx="${p.x}" cy="${p.y}" r="3" fill="${p.color}"/><text x="${p.lx}" y="${p.ly + 4}" font-size="9" fill="#889" text-anchor="middle" font-family="monospace">${esc(p.label)}</text>`).join("\n ")}
|
|
5710
|
+
<text x="580" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">TOP TOPICS</text>
|
|
5711
|
+
<rect x="440" y="115" width="320" height="240" rx="8" fill="#6366f108"/>
|
|
5712
|
+
${tagEls}
|
|
5713
|
+
<text x="${W / 2}" y="${H - 15}" font-size="10" fill="#334" text-anchor="middle" font-family="monospace">Generated by Stellavault</text>
|
|
5714
|
+
</svg>`;
|
|
5715
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
5716
|
+
res.send(svg);
|
|
5717
|
+
} catch (err) {
|
|
5718
|
+
console.error(err);
|
|
5719
|
+
res.status(500).json({ error: "Internal server error" });
|
|
5720
|
+
}
|
|
5721
|
+
});
|
|
5722
|
+
return router;
|
|
5723
|
+
}
|
|
5724
|
+
|
|
5725
|
+
// packages/core/dist/api/routes/health.js
|
|
5726
|
+
import { Router as Router5 } from "express";
|
|
5727
|
+
function createHealthRouter(opts) {
|
|
5728
|
+
const { store, vaultName, decayEngine } = opts;
|
|
5729
|
+
const router = Router5();
|
|
5730
|
+
router.get("/health", async (_req, res) => {
|
|
5731
|
+
try {
|
|
5732
|
+
const stats = await store.getStats();
|
|
5733
|
+
const docs = await store.getAllDocuments();
|
|
5734
|
+
let decaySummary = { totalDocuments: 0, criticalCount: 0, decayingCount: 0, averageR: 1, topDecaying: [] };
|
|
5735
|
+
if (decayEngine) {
|
|
5736
|
+
const report = await decayEngine.computeAll();
|
|
5737
|
+
decaySummary = {
|
|
5738
|
+
totalDocuments: report.totalDocuments ?? docs.length,
|
|
5739
|
+
criticalCount: report.criticalCount ?? 0,
|
|
5740
|
+
decayingCount: report.decayingCount ?? 0,
|
|
5741
|
+
averageR: report.averageR ?? 1,
|
|
5742
|
+
topDecaying: (report.topDecaying ?? []).slice(0, 5)
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
let gapSummary = { gapCount: 0, isolatedCount: 0 };
|
|
5746
|
+
try {
|
|
5747
|
+
const { detectKnowledgeGaps: detectKnowledgeGaps2 } = await Promise.resolve().then(() => (init_gap_detector(), gap_detector_exports));
|
|
5748
|
+
const gapReport = await detectKnowledgeGaps2(store);
|
|
5749
|
+
gapSummary = {
|
|
5750
|
+
gapCount: gapReport.gaps?.length ?? 0,
|
|
5751
|
+
isolatedCount: gapReport.isolatedNodes?.length ?? 0
|
|
5752
|
+
};
|
|
5753
|
+
} catch (e) {
|
|
5754
|
+
console.error("[health] Gap detection failed:", e instanceof Error ? e.message : e);
|
|
5755
|
+
}
|
|
5756
|
+
let dupCount = 0;
|
|
5757
|
+
try {
|
|
5758
|
+
const { detectDuplicates: detectDuplicates2 } = await Promise.resolve().then(() => (init_duplicate_detector(), duplicate_detector_exports));
|
|
5759
|
+
const pairs = await detectDuplicates2(store, 0.88, 50);
|
|
5760
|
+
dupCount = pairs.length;
|
|
5761
|
+
} catch (e) {
|
|
5762
|
+
console.error("[health] Duplicate detection failed:", e instanceof Error ? e.message : e);
|
|
5763
|
+
}
|
|
5764
|
+
const sourceDist = /* @__PURE__ */ new Map();
|
|
5765
|
+
const typeDist = /* @__PURE__ */ new Map();
|
|
5766
|
+
for (const doc of docs) {
|
|
5767
|
+
const s = doc.source ?? "local";
|
|
5768
|
+
const t2 = doc.type ?? "note";
|
|
5769
|
+
sourceDist.set(s, (sourceDist.get(s) ?? 0) + 1);
|
|
5770
|
+
typeDist.set(t2, (typeDist.get(t2) ?? 0) + 1);
|
|
5771
|
+
}
|
|
5772
|
+
const monthlyGrowth = /* @__PURE__ */ new Map();
|
|
5773
|
+
for (const doc of docs) {
|
|
5774
|
+
const month = doc.lastModified?.slice(0, 7) ?? "unknown";
|
|
5775
|
+
monthlyGrowth.set(month, (monthlyGrowth.get(month) ?? 0) + 1);
|
|
5776
|
+
}
|
|
5777
|
+
res.json({
|
|
5778
|
+
stats: { ...stats, vaultName },
|
|
5779
|
+
decay: decaySummary,
|
|
5780
|
+
gaps: gapSummary,
|
|
5781
|
+
duplicates: { count: dupCount },
|
|
5782
|
+
distribution: {
|
|
5783
|
+
source: Object.fromEntries(sourceDist),
|
|
5784
|
+
type: Object.fromEntries(typeDist)
|
|
5785
|
+
},
|
|
5786
|
+
growth: Object.fromEntries([...monthlyGrowth.entries()].sort((a, b) => a[0].localeCompare(b[0])))
|
|
5787
|
+
});
|
|
5788
|
+
} catch (err) {
|
|
5789
|
+
console.error(err);
|
|
5790
|
+
res.status(500).json({ error: "Internal server error" });
|
|
5791
|
+
}
|
|
5792
|
+
});
|
|
5793
|
+
return router;
|
|
5794
|
+
}
|
|
5795
|
+
|
|
5796
|
+
// packages/core/dist/api/routes/analytics.js
|
|
5797
|
+
init_graph_data();
|
|
5798
|
+
import { Router as Router6 } from "express";
|
|
5799
|
+
function createAnalyticsRouter(opts) {
|
|
5800
|
+
const { store, vaultName, decayEngine, graphCaches, GRAPH_CACHE_TTL } = opts;
|
|
5801
|
+
const router = Router6();
|
|
5802
|
+
router.get("/profile", async (_req, res) => {
|
|
5803
|
+
try {
|
|
5804
|
+
const stats = await store.getStats();
|
|
5805
|
+
const topics = await store.getTopics();
|
|
5806
|
+
const docs = await store.getAllDocuments();
|
|
5807
|
+
let decaySummary = { averageR: 1, criticalCount: 0, healthScore: 100 };
|
|
5808
|
+
if (decayEngine) {
|
|
5809
|
+
const report = await decayEngine.computeAll();
|
|
5810
|
+
const avgR = report.averageR ?? 1;
|
|
5811
|
+
decaySummary = { averageR: avgR, criticalCount: report.criticalCount ?? 0, healthScore: Math.round(avgR * 100) };
|
|
5812
|
+
}
|
|
5813
|
+
const sourceDist = {};
|
|
5814
|
+
const typeDist = {};
|
|
5815
|
+
for (const doc of docs) {
|
|
5816
|
+
sourceDist[doc.source ?? "local"] = (sourceDist[doc.source ?? "local"] ?? 0) + 1;
|
|
5817
|
+
typeDist[doc.type ?? "note"] = (typeDist[doc.type ?? "note"] ?? 0) + 1;
|
|
5818
|
+
}
|
|
5819
|
+
const monthlyActivity = {};
|
|
5820
|
+
for (const doc of docs) {
|
|
5821
|
+
const month = doc.lastModified?.slice(0, 7);
|
|
5822
|
+
if (month)
|
|
5823
|
+
monthlyActivity[month] = (monthlyActivity[month] ?? 0) + 1;
|
|
5824
|
+
}
|
|
5825
|
+
res.json({
|
|
5826
|
+
name: vaultName || "Knowledge Vault",
|
|
5827
|
+
stats: { documents: stats.documentCount, chunks: stats.chunkCount, topics: topics.length },
|
|
5828
|
+
healthScore: decaySummary.healthScore,
|
|
5829
|
+
topTopics: topics.slice(0, 15).map((t2) => ({ name: t2.topic, count: t2.count })),
|
|
5830
|
+
distribution: { source: sourceDist, type: typeDist },
|
|
5831
|
+
activity: Object.fromEntries(Object.entries(monthlyActivity).sort((a, b) => a[0].localeCompare(b[0])).slice(-12)),
|
|
5832
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5833
|
+
});
|
|
5834
|
+
} catch (err) {
|
|
5835
|
+
console.error(err);
|
|
5836
|
+
res.status(500).json({ error: "Internal server error" });
|
|
5837
|
+
}
|
|
5838
|
+
});
|
|
5839
|
+
router.get("/embed", async (req, res) => {
|
|
5840
|
+
try {
|
|
5841
|
+
const mode = req.query.mode === "folder" ? "folder" : "semantic";
|
|
5842
|
+
const maxNodes = Math.min(parseInt(String(req.query.max ?? "200"), 10), 500);
|
|
5843
|
+
const cached = graphCaches.get(mode);
|
|
5844
|
+
if (!cached || Date.now() - cached.cachedAt > GRAPH_CACHE_TTL) {
|
|
5845
|
+
const data = await buildGraphData(store, { mode });
|
|
5846
|
+
graphCaches.set(mode, { data, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), cachedAt: Date.now() });
|
|
5847
|
+
}
|
|
5848
|
+
const { nodes, edges, clusters } = graphCaches.get(mode).data;
|
|
5849
|
+
const connCount = /* @__PURE__ */ new Map();
|
|
5850
|
+
for (const e of edges) {
|
|
5851
|
+
connCount.set(e.source, (connCount.get(e.source) ?? 0) + 1);
|
|
5852
|
+
connCount.set(e.target, (connCount.get(e.target) ?? 0) + 1);
|
|
5853
|
+
}
|
|
5854
|
+
const sortedNodes = [...nodes].sort((a, b) => (connCount.get(b.id) ?? 0) - (connCount.get(a.id) ?? 0));
|
|
5855
|
+
const selectedNodes = sortedNodes.slice(0, maxNodes);
|
|
5856
|
+
const selectedIds = new Set(selectedNodes.map((n) => n.id));
|
|
5857
|
+
const selectedEdges = edges.filter((e) => selectedIds.has(e.source) && selectedIds.has(e.target));
|
|
5858
|
+
const embedNodes = selectedNodes.map((n, i) => {
|
|
5859
|
+
const angle = i / selectedNodes.length * Math.PI * 2;
|
|
5860
|
+
const r = 100 + n.clusterId * 15;
|
|
5861
|
+
return {
|
|
5862
|
+
id: n.id,
|
|
5863
|
+
label: n.label,
|
|
5864
|
+
clusterId: n.clusterId,
|
|
5865
|
+
size: n.size,
|
|
5866
|
+
position: [
|
|
5867
|
+
r * Math.cos(angle) + (Math.random() - 0.5) * 60,
|
|
5868
|
+
(Math.random() - 0.5) * 200,
|
|
5869
|
+
r * Math.sin(angle) + (Math.random() - 0.5) * 60
|
|
5870
|
+
]
|
|
5871
|
+
};
|
|
5872
|
+
});
|
|
5873
|
+
res.json({
|
|
5874
|
+
nodes: embedNodes,
|
|
5875
|
+
edges: selectedEdges,
|
|
5876
|
+
stats: { nodeCount: embedNodes.length, edgeCount: selectedEdges.length, clusterCount: clusters.length, totalNodes: nodes.length },
|
|
5877
|
+
title: vaultName || "Knowledge Graph"
|
|
5878
|
+
});
|
|
5879
|
+
} catch (err) {
|
|
5880
|
+
console.error(err);
|
|
5881
|
+
res.status(500).json({ error: "Internal server error" });
|
|
5882
|
+
}
|
|
5883
|
+
});
|
|
5884
|
+
return router;
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5611
5887
|
// packages/core/dist/api/server.js
|
|
5612
5888
|
function createApiServer(options) {
|
|
5613
5889
|
const { store, searchEngine, port = 3333, vaultName = "", vaultPath = "", decayEngine, graphUiPath } = options;
|
|
@@ -5808,79 +6084,7 @@ function createApiServer(options) {
|
|
|
5808
6084
|
res.status(500).json({ error: "Internal server error" });
|
|
5809
6085
|
}
|
|
5810
6086
|
});
|
|
5811
|
-
app.
|
|
5812
|
-
try {
|
|
5813
|
-
const mode = String(_req.query.mode ?? "") === "folder" ? "folder" : "semantic";
|
|
5814
|
-
const cachedProfile = graphCaches.get(mode);
|
|
5815
|
-
if (!cachedProfile || Date.now() - cachedProfile.cachedAt > GRAPH_CACHE_TTL) {
|
|
5816
|
-
const data = await buildGraphData(store, { mode });
|
|
5817
|
-
graphCaches.set(mode, { data, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), cachedAt: Date.now() });
|
|
5818
|
-
}
|
|
5819
|
-
const graphData = graphCaches.get(mode).data;
|
|
5820
|
-
const topics = await store.getTopics();
|
|
5821
|
-
const stats = await store.getStats();
|
|
5822
|
-
const top6 = [...graphData.clusters].sort((a, b) => b.nodeCount - a.nodeCount).slice(0, 6);
|
|
5823
|
-
const maxCount = Math.max(1, ...top6.map((c) => c.nodeCount));
|
|
5824
|
-
const W = 800, H = 420;
|
|
5825
|
-
const radarCx = 200, radarCy = 220, radarR = 100;
|
|
5826
|
-
const radarPoints = top6.map((c, i) => {
|
|
5827
|
-
const angle = Math.PI * 2 * i / top6.length - Math.PI / 2;
|
|
5828
|
-
const r = radarR * (c.nodeCount / maxCount);
|
|
5829
|
-
return {
|
|
5830
|
-
x: radarCx + r * Math.cos(angle),
|
|
5831
|
-
y: radarCy + r * Math.sin(angle),
|
|
5832
|
-
lx: radarCx + (radarR + 20) * Math.cos(angle),
|
|
5833
|
-
ly: radarCy + (radarR + 20) * Math.sin(angle),
|
|
5834
|
-
label: c.label.split(",")[0].trim().slice(0, 12),
|
|
5835
|
-
color: c.color
|
|
5836
|
-
};
|
|
5837
|
-
});
|
|
5838
|
-
const radarPath = radarPoints.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ") + "Z";
|
|
5839
|
-
const gridPaths = [0.33, 0.66, 1].map((s) => top6.map((_, i) => {
|
|
5840
|
-
const a = Math.PI * 2 * i / top6.length - Math.PI / 2;
|
|
5841
|
-
return `${i === 0 ? "M" : "L"}${radarCx + radarR * s * Math.cos(a)},${radarCy + radarR * s * Math.sin(a)}`;
|
|
5842
|
-
}).join(" ") + "Z");
|
|
5843
|
-
const tags20 = topics.slice(0, 20);
|
|
5844
|
-
const maxTag = Math.max(1, ...tags20.map((t2) => t2.count));
|
|
5845
|
-
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5846
|
-
const tagEls = tags20.map((t2, i) => {
|
|
5847
|
-
const sz = 10 + 14 * (t2.count / maxTag);
|
|
5848
|
-
const x = 480 + Math.floor(i / 2) % 5 * 60;
|
|
5849
|
-
const y = 140 + i % 2 * 30 + Math.floor(i / 10) * 70;
|
|
5850
|
-
const op = 0.5 + 0.5 * (t2.count / maxTag);
|
|
5851
|
-
return `<text x="${x}" y="${y}" font-size="${sz}" fill="#88aaff" opacity="${op}" font-family="monospace">#${esc(t2.topic)}</text>`;
|
|
5852
|
-
}).join("\n ");
|
|
5853
|
-
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
|
|
5854
|
-
<defs>
|
|
5855
|
-
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5856
|
-
<stop offset="0%" stop-color="#0d1028"/><stop offset="100%" stop-color="#050510"/>
|
|
5857
|
-
</linearGradient>
|
|
5858
|
-
<linearGradient id="rf" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5859
|
-
<stop offset="0%" stop-color="#6366f1" stop-opacity="0.3"/><stop offset="100%" stop-color="#06b6d4" stop-opacity="0.15"/>
|
|
5860
|
-
</linearGradient>
|
|
5861
|
-
</defs>
|
|
5862
|
-
<rect width="${W}" height="${H}" rx="16" fill="url(#bg)"/>
|
|
5863
|
-
<rect width="${W}" height="${H}" rx="16" fill="none" stroke="#6366f140"/>
|
|
5864
|
-
<text x="30" y="40" font-size="20" font-weight="700" fill="#c0c0f0" font-family="system-ui">\u{1F9E0} Knowledge Universe</text>
|
|
5865
|
-
<text x="30" y="65" font-size="13" fill="#556" font-family="monospace">${stats.documentCount} docs \xB7 ${graphData.clusters.length} clusters \xB7 ${graphData.edges.length} connections</text>
|
|
5866
|
-
<line x1="30" y1="80" x2="${W - 30}" y2="80" stroke="#6366f120"/>
|
|
5867
|
-
<text x="${radarCx}" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">KNOWLEDGE DISTRIBUTION</text>
|
|
5868
|
-
${gridPaths.map((p) => `<path d="${p}" fill="none" stroke="#6366f115" stroke-width="0.5"/>`).join("\n ")}
|
|
5869
|
-
${radarPoints.map((p) => `<line x1="${radarCx}" y1="${radarCy}" x2="${p.lx}" y2="${p.ly}" stroke="#6366f110" stroke-width="0.5"/>`).join("\n ")}
|
|
5870
|
-
<path d="${radarPath}" fill="url(#rf)" stroke="#818cf8" stroke-width="1.5"/>
|
|
5871
|
-
${radarPoints.map((p) => `<circle cx="${p.x}" cy="${p.y}" r="3" fill="${p.color}"/><text x="${p.lx}" y="${p.ly + 4}" font-size="9" fill="#889" text-anchor="middle" font-family="monospace">${esc(p.label)}</text>`).join("\n ")}
|
|
5872
|
-
<text x="580" y="105" font-size="11" fill="#667" text-anchor="middle" font-family="system-ui">TOP TOPICS</text>
|
|
5873
|
-
<rect x="440" y="115" width="320" height="240" rx="8" fill="#6366f108"/>
|
|
5874
|
-
${tagEls}
|
|
5875
|
-
<text x="${W / 2}" y="${H - 15}" font-size="10" fill="#334" text-anchor="middle" font-family="monospace">Generated by Stellavault</text>
|
|
5876
|
-
</svg>`;
|
|
5877
|
-
res.setHeader("Content-Type", "image/svg+xml");
|
|
5878
|
-
res.send(svg);
|
|
5879
|
-
} catch (err) {
|
|
5880
|
-
console.error(err);
|
|
5881
|
-
res.status(500).json({ error: "Internal server error" });
|
|
5882
|
-
}
|
|
5883
|
-
});
|
|
6087
|
+
app.use("/api", createProfileCardRouter({ store, graphCaches, GRAPH_CACHE_TTL }));
|
|
5884
6088
|
app.get("/api/stats", async (_req, res) => {
|
|
5885
6089
|
try {
|
|
5886
6090
|
const stats = await store.getStats();
|
|
@@ -6076,162 +6280,8 @@ function createApiServer(options) {
|
|
|
6076
6280
|
}
|
|
6077
6281
|
});
|
|
6078
6282
|
app.use("/api", createKnowledgeRouter({ store, searchEngine, vaultPath, requireAuth }));
|
|
6079
|
-
app.
|
|
6080
|
-
|
|
6081
|
-
const stats = await store.getStats();
|
|
6082
|
-
const docs = await store.getAllDocuments();
|
|
6083
|
-
let decaySummary = { totalDocuments: 0, criticalCount: 0, decayingCount: 0, averageR: 1, topDecaying: [] };
|
|
6084
|
-
if (decayEngine) {
|
|
6085
|
-
const report = await decayEngine.computeAll();
|
|
6086
|
-
decaySummary = {
|
|
6087
|
-
totalDocuments: report.totalDocuments ?? docs.length,
|
|
6088
|
-
criticalCount: report.criticalCount ?? 0,
|
|
6089
|
-
decayingCount: report.decayingCount ?? 0,
|
|
6090
|
-
averageR: report.averageR ?? 1,
|
|
6091
|
-
topDecaying: (report.topDecaying ?? []).slice(0, 5)
|
|
6092
|
-
};
|
|
6093
|
-
}
|
|
6094
|
-
let gapSummary = { gapCount: 0, isolatedCount: 0 };
|
|
6095
|
-
try {
|
|
6096
|
-
const { detectKnowledgeGaps: detectKnowledgeGaps2 } = await Promise.resolve().then(() => (init_gap_detector(), gap_detector_exports));
|
|
6097
|
-
const gapReport = await detectKnowledgeGaps2(store);
|
|
6098
|
-
gapSummary = {
|
|
6099
|
-
gapCount: gapReport.gaps?.length ?? 0,
|
|
6100
|
-
isolatedCount: gapReport.isolatedNodes?.length ?? 0
|
|
6101
|
-
};
|
|
6102
|
-
} catch (e) {
|
|
6103
|
-
console.error("[health] Gap detection failed:", e instanceof Error ? e.message : e);
|
|
6104
|
-
}
|
|
6105
|
-
let dupCount = 0;
|
|
6106
|
-
try {
|
|
6107
|
-
const { detectDuplicates: detectDuplicates2 } = await Promise.resolve().then(() => (init_duplicate_detector(), duplicate_detector_exports));
|
|
6108
|
-
const pairs = await detectDuplicates2(store, 0.88, 50);
|
|
6109
|
-
dupCount = pairs.length;
|
|
6110
|
-
} catch (e) {
|
|
6111
|
-
console.error("[health] Duplicate detection failed:", e instanceof Error ? e.message : e);
|
|
6112
|
-
}
|
|
6113
|
-
const sourceDist = /* @__PURE__ */ new Map();
|
|
6114
|
-
const typeDist = /* @__PURE__ */ new Map();
|
|
6115
|
-
for (const doc of docs) {
|
|
6116
|
-
const s = doc.source ?? "local";
|
|
6117
|
-
const t2 = doc.type ?? "note";
|
|
6118
|
-
sourceDist.set(s, (sourceDist.get(s) ?? 0) + 1);
|
|
6119
|
-
typeDist.set(t2, (typeDist.get(t2) ?? 0) + 1);
|
|
6120
|
-
}
|
|
6121
|
-
const monthlyGrowth = /* @__PURE__ */ new Map();
|
|
6122
|
-
for (const doc of docs) {
|
|
6123
|
-
const month = doc.lastModified?.slice(0, 7) ?? "unknown";
|
|
6124
|
-
monthlyGrowth.set(month, (monthlyGrowth.get(month) ?? 0) + 1);
|
|
6125
|
-
}
|
|
6126
|
-
res.json({
|
|
6127
|
-
stats: { ...stats, vaultName },
|
|
6128
|
-
decay: decaySummary,
|
|
6129
|
-
gaps: gapSummary,
|
|
6130
|
-
duplicates: { count: dupCount },
|
|
6131
|
-
distribution: {
|
|
6132
|
-
source: Object.fromEntries(sourceDist),
|
|
6133
|
-
type: Object.fromEntries(typeDist)
|
|
6134
|
-
},
|
|
6135
|
-
growth: Object.fromEntries([...monthlyGrowth.entries()].sort((a, b) => a[0].localeCompare(b[0])))
|
|
6136
|
-
});
|
|
6137
|
-
} catch (err) {
|
|
6138
|
-
console.error(err);
|
|
6139
|
-
res.status(500).json({ error: "Internal server error" });
|
|
6140
|
-
}
|
|
6141
|
-
});
|
|
6142
|
-
app.get("/api/profile", async (_req, res) => {
|
|
6143
|
-
try {
|
|
6144
|
-
const stats = await store.getStats();
|
|
6145
|
-
const topics = await store.getTopics();
|
|
6146
|
-
const docs = await store.getAllDocuments();
|
|
6147
|
-
let decaySummary = { averageR: 1, criticalCount: 0, healthScore: 100 };
|
|
6148
|
-
if (decayEngine) {
|
|
6149
|
-
const report = await decayEngine.computeAll();
|
|
6150
|
-
const avgR = report.averageR ?? 1;
|
|
6151
|
-
decaySummary = {
|
|
6152
|
-
averageR: avgR,
|
|
6153
|
-
criticalCount: report.criticalCount ?? 0,
|
|
6154
|
-
healthScore: Math.round(avgR * 100)
|
|
6155
|
-
};
|
|
6156
|
-
}
|
|
6157
|
-
const sourceDist = {};
|
|
6158
|
-
const typeDist = {};
|
|
6159
|
-
for (const doc of docs) {
|
|
6160
|
-
const s = doc.source ?? "local";
|
|
6161
|
-
const t2 = doc.type ?? "note";
|
|
6162
|
-
sourceDist[s] = (sourceDist[s] ?? 0) + 1;
|
|
6163
|
-
typeDist[t2] = (typeDist[t2] ?? 0) + 1;
|
|
6164
|
-
}
|
|
6165
|
-
const monthlyActivity = {};
|
|
6166
|
-
for (const doc of docs) {
|
|
6167
|
-
const month = doc.lastModified?.slice(0, 7);
|
|
6168
|
-
if (month)
|
|
6169
|
-
monthlyActivity[month] = (monthlyActivity[month] ?? 0) + 1;
|
|
6170
|
-
}
|
|
6171
|
-
res.json({
|
|
6172
|
-
name: vaultName || "Knowledge Vault",
|
|
6173
|
-
stats: {
|
|
6174
|
-
documents: stats.documentCount,
|
|
6175
|
-
chunks: stats.chunkCount,
|
|
6176
|
-
topics: topics.length
|
|
6177
|
-
},
|
|
6178
|
-
healthScore: decaySummary.healthScore,
|
|
6179
|
-
topTopics: topics.slice(0, 15).map((t2) => ({ name: t2.topic, count: t2.count })),
|
|
6180
|
-
distribution: { source: sourceDist, type: typeDist },
|
|
6181
|
-
activity: Object.fromEntries(Object.entries(monthlyActivity).sort((a, b) => a[0].localeCompare(b[0])).slice(-12)),
|
|
6182
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6183
|
-
});
|
|
6184
|
-
} catch (err) {
|
|
6185
|
-
console.error(err);
|
|
6186
|
-
res.status(500).json({ error: "Internal server error" });
|
|
6187
|
-
}
|
|
6188
|
-
});
|
|
6189
|
-
app.get("/api/embed", async (req, res) => {
|
|
6190
|
-
try {
|
|
6191
|
-
const mode = req.query.mode === "folder" ? "folder" : "semantic";
|
|
6192
|
-
const maxNodes = Math.min(parseInt(String(req.query.max ?? "200"), 10), 500);
|
|
6193
|
-
const cachedConstellation = graphCaches.get(mode);
|
|
6194
|
-
if (!cachedConstellation || Date.now() - cachedConstellation.cachedAt > GRAPH_CACHE_TTL) {
|
|
6195
|
-
const data = await buildGraphData(store, { mode });
|
|
6196
|
-
graphCaches.set(mode, { data, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), cachedAt: Date.now() });
|
|
6197
|
-
}
|
|
6198
|
-
const cached = graphCaches.get(mode);
|
|
6199
|
-
const { nodes, edges, clusters } = cached.data;
|
|
6200
|
-
const connCount = /* @__PURE__ */ new Map();
|
|
6201
|
-
for (const e of edges) {
|
|
6202
|
-
connCount.set(e.source, (connCount.get(e.source) ?? 0) + 1);
|
|
6203
|
-
connCount.set(e.target, (connCount.get(e.target) ?? 0) + 1);
|
|
6204
|
-
}
|
|
6205
|
-
const sortedNodes = [...nodes].sort((a, b) => (connCount.get(b.id) ?? 0) - (connCount.get(a.id) ?? 0));
|
|
6206
|
-
const selectedNodes = sortedNodes.slice(0, maxNodes);
|
|
6207
|
-
const selectedIds = new Set(selectedNodes.map((n) => n.id));
|
|
6208
|
-
const selectedEdges = edges.filter((e) => selectedIds.has(e.source) && selectedIds.has(e.target));
|
|
6209
|
-
const embedNodes = selectedNodes.map((n, i) => {
|
|
6210
|
-
const angle = i / selectedNodes.length * Math.PI * 2;
|
|
6211
|
-
const r = 100 + n.clusterId * 15;
|
|
6212
|
-
return {
|
|
6213
|
-
id: n.id,
|
|
6214
|
-
label: n.label,
|
|
6215
|
-
clusterId: n.clusterId,
|
|
6216
|
-
size: n.size,
|
|
6217
|
-
position: [
|
|
6218
|
-
r * Math.cos(angle) + (Math.random() - 0.5) * 60,
|
|
6219
|
-
(Math.random() - 0.5) * 200,
|
|
6220
|
-
r * Math.sin(angle) + (Math.random() - 0.5) * 60
|
|
6221
|
-
]
|
|
6222
|
-
};
|
|
6223
|
-
});
|
|
6224
|
-
res.json({
|
|
6225
|
-
nodes: embedNodes,
|
|
6226
|
-
edges: selectedEdges,
|
|
6227
|
-
stats: { nodeCount: embedNodes.length, edgeCount: selectedEdges.length, clusterCount: clusters.length, totalNodes: nodes.length },
|
|
6228
|
-
title: vaultName || "Knowledge Graph"
|
|
6229
|
-
});
|
|
6230
|
-
} catch (err) {
|
|
6231
|
-
console.error(err);
|
|
6232
|
-
res.status(500).json({ error: "Internal server error" });
|
|
6233
|
-
}
|
|
6234
|
-
});
|
|
6283
|
+
app.use("/api", createHealthRouter({ store, vaultName, decayEngine }));
|
|
6284
|
+
app.use("/api", createAnalyticsRouter({ store, vaultName, decayEngine, graphCaches, GRAPH_CACHE_TTL }));
|
|
6235
6285
|
let syncState = {
|
|
6236
6286
|
running: false,
|
|
6237
6287
|
startedAt: "",
|
|
@@ -6884,6 +6934,7 @@ var CREDITS_FILE = join19(homedir11(), ".stellavault", "federation", "credits.js
|
|
|
6884
6934
|
|
|
6885
6935
|
// packages/core/dist/index.js
|
|
6886
6936
|
init_retry();
|
|
6937
|
+
init_math();
|
|
6887
6938
|
init_indexer();
|
|
6888
6939
|
function createKnowledgeHub(config) {
|
|
6889
6940
|
const embedder = createLocalEmbedder(config.embedding.localModel);
|
|
@@ -9629,7 +9680,7 @@ if (nodeVersion < 20) {
|
|
|
9629
9680
|
process.exit(1);
|
|
9630
9681
|
}
|
|
9631
9682
|
var program = new Command();
|
|
9632
|
-
var SV_VERSION = true ? "0.7.
|
|
9683
|
+
var SV_VERSION = true ? "0.7.3" : "0.0.0-dev";
|
|
9633
9684
|
program.name("stellavault").description("Stellavault \u2014 Self-compiling knowledge base for your Obsidian vault").version(SV_VERSION).option("--json", "Output in JSON format (for scripting)").option("--quiet", "Suppress non-essential output");
|
|
9634
9685
|
program.command("init").description("Interactive setup wizard \u2014 get started in 3 minutes").action(initCommand);
|
|
9635
9686
|
program.command("doctor").description("Diagnose setup issues (config, vault, DB, model, Node version)").action(doctorCommand);
|
|
@@ -9679,4 +9730,33 @@ pack.command("export <name>").description("Export a pack as a .sv-pack file").op
|
|
|
9679
9730
|
pack.command("import <file>").description("Import a .sv-pack file into your vector DB").action(packImportCommand);
|
|
9680
9731
|
pack.command("list").description("List installed packs").action(packListCommand);
|
|
9681
9732
|
pack.command("info <name>").description("Show pack details").action(packInfoCommand);
|
|
9733
|
+
program.command("completion").description("Output shell completion script (bash/zsh/fish)").option("--shell <type>", "Shell type: bash, zsh, fish", "bash").action((opts) => {
|
|
9734
|
+
const commands = program.commands.map((c) => c.name()).filter((n) => n !== "completion");
|
|
9735
|
+
const name = "stellavault";
|
|
9736
|
+
if (opts.shell === "zsh") {
|
|
9737
|
+
console.log(`#compdef ${name} sv
|
|
9738
|
+
_${name}() {
|
|
9739
|
+
local -a commands
|
|
9740
|
+
commands=(
|
|
9741
|
+
${commands.map((c) => ` '${c}:${program.commands.find((cmd) => cmd.name() === c)?.description() ?? ""}'`).join("\n")}
|
|
9742
|
+
)
|
|
9743
|
+
_describe 'command' commands
|
|
9744
|
+
}
|
|
9745
|
+
compdef _${name} ${name} sv`);
|
|
9746
|
+
} else if (opts.shell === "fish") {
|
|
9747
|
+
console.log(commands.map((c) => {
|
|
9748
|
+
const desc = program.commands.find((cmd) => cmd.name() === c)?.description() ?? "";
|
|
9749
|
+
return `complete -c ${name} -n "__fish_use_subcommand" -a "${c}" -d "${desc}"`;
|
|
9750
|
+
}).join("\n"));
|
|
9751
|
+
} else {
|
|
9752
|
+
console.log(`_${name}_completions() {
|
|
9753
|
+
local commands="${commands.join(" ")}"
|
|
9754
|
+
COMPREPLY=($(compgen -W "$commands" -- "\${COMP_WORDS[COMP_CWORD]}"))
|
|
9755
|
+
}
|
|
9756
|
+
complete -F _${name}_completions ${name} sv`);
|
|
9757
|
+
}
|
|
9758
|
+
console.error(`
|
|
9759
|
+
# Add to your shell profile:
|
|
9760
|
+
# eval "$(stellavault completion --shell ${opts.shell})"`);
|
|
9761
|
+
});
|
|
9682
9762
|
program.parse();
|
package/package.json
CHANGED