@swarmvaultai/engine 0.1.33 → 0.2.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/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/chunk-CWLDFLH2.js +1163 -0
- package/dist/index.d.ts +61 -1
- package/dist/index.js +1321 -453
- package/dist/registry-2REAPKPO.js +12 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
uniqueBy,
|
|
22
22
|
writeFileIfChanged,
|
|
23
23
|
writeJsonFile
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-CWLDFLH2.js";
|
|
25
25
|
|
|
26
26
|
// src/agents.ts
|
|
27
27
|
import crypto from "crypto";
|
|
@@ -1721,8 +1721,8 @@ async function uninstallGitHooks(rootDir) {
|
|
|
1721
1721
|
}
|
|
1722
1722
|
|
|
1723
1723
|
// src/ingest.ts
|
|
1724
|
-
import
|
|
1725
|
-
import
|
|
1724
|
+
import fs11 from "fs/promises";
|
|
1725
|
+
import path12 from "path";
|
|
1726
1726
|
import { pathToFileURL } from "url";
|
|
1727
1727
|
import { Readability } from "@mozilla/readability";
|
|
1728
1728
|
import matter3 from "gray-matter";
|
|
@@ -4724,9 +4724,123 @@ function aggregateManifestSourceClass(manifests, sourceIds) {
|
|
|
4724
4724
|
return aggregateSourceClass(sourceIds.map((sourceId) => byId.get(sourceId)));
|
|
4725
4725
|
}
|
|
4726
4726
|
|
|
4727
|
-
// src/
|
|
4727
|
+
// src/source-registry.ts
|
|
4728
4728
|
import fs9 from "fs/promises";
|
|
4729
4729
|
import path10 from "path";
|
|
4730
|
+
var MANAGED_SOURCES_VERSION = 1;
|
|
4731
|
+
function repoRootFromManifest(manifest) {
|
|
4732
|
+
if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
|
|
4733
|
+
return null;
|
|
4734
|
+
}
|
|
4735
|
+
const repoDir = path10.posix.dirname(manifest.repoRelativePath);
|
|
4736
|
+
const fileDir = path10.dirname(path10.resolve(manifest.originalPath));
|
|
4737
|
+
if (repoDir === "." || !repoDir) {
|
|
4738
|
+
return fileDir;
|
|
4739
|
+
}
|
|
4740
|
+
const segments = repoDir.split("/").filter(Boolean);
|
|
4741
|
+
return path10.resolve(fileDir, ...segments.map(() => ".."));
|
|
4742
|
+
}
|
|
4743
|
+
async function loadManifestArtifacts(paths) {
|
|
4744
|
+
const entries = await fs9.readdir(paths.manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
4745
|
+
const manifests = await Promise.all(
|
|
4746
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => await readJsonFile(path10.join(paths.manifestsDir, entry.name)))
|
|
4747
|
+
);
|
|
4748
|
+
return manifests.filter((manifest) => Boolean(manifest?.sourceId));
|
|
4749
|
+
}
|
|
4750
|
+
function buildLegacyDirectoryEntry(repoRoot, manifests) {
|
|
4751
|
+
const repoTitle = path10.basename(repoRoot) || repoRoot;
|
|
4752
|
+
const sourceIds = manifests.map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
|
|
4753
|
+
const createdAt = manifests.map((manifest) => manifest.createdAt).sort((left, right) => left.localeCompare(right))[0] ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4754
|
+
const updatedAt = manifests.map((manifest) => manifest.updatedAt).sort((left, right) => right.localeCompare(left))[0] ?? createdAt;
|
|
4755
|
+
return {
|
|
4756
|
+
id: `directory-${slugify(repoTitle)}-${sha256(repoRoot).slice(0, 8)}`,
|
|
4757
|
+
kind: "directory",
|
|
4758
|
+
title: repoTitle,
|
|
4759
|
+
path: repoRoot,
|
|
4760
|
+
repoRoot,
|
|
4761
|
+
createdAt,
|
|
4762
|
+
updatedAt,
|
|
4763
|
+
status: "ready",
|
|
4764
|
+
sourceIds,
|
|
4765
|
+
lastSyncAt: updatedAt,
|
|
4766
|
+
lastSyncStatus: "success",
|
|
4767
|
+
lastSyncCounts: {
|
|
4768
|
+
scannedCount: manifests.length,
|
|
4769
|
+
importedCount: manifests.length,
|
|
4770
|
+
updatedCount: 0,
|
|
4771
|
+
removedCount: 0,
|
|
4772
|
+
skippedCount: 0
|
|
4773
|
+
}
|
|
4774
|
+
};
|
|
4775
|
+
}
|
|
4776
|
+
async function buildLegacyArtifact(paths) {
|
|
4777
|
+
const manifests = await loadManifestArtifacts(paths);
|
|
4778
|
+
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
4779
|
+
for (const manifest of manifests) {
|
|
4780
|
+
const repoRoot = repoRootFromManifest(manifest);
|
|
4781
|
+
if (!repoRoot) {
|
|
4782
|
+
continue;
|
|
4783
|
+
}
|
|
4784
|
+
const key = path10.resolve(repoRoot);
|
|
4785
|
+
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
4786
|
+
bucket.push(manifest);
|
|
4787
|
+
manifestsByRepoRoot.set(key, bucket);
|
|
4788
|
+
}
|
|
4789
|
+
const repoRoots = [...manifestsByRepoRoot.entries()].filter(([, repoManifests]) => {
|
|
4790
|
+
return repoManifests.length > 1 || repoManifests.some((manifest) => manifest.sourceKind === "code");
|
|
4791
|
+
});
|
|
4792
|
+
return {
|
|
4793
|
+
version: MANAGED_SOURCES_VERSION,
|
|
4794
|
+
sources: repoRoots.sort((left, right) => left[0].localeCompare(right[0])).map(([repoRoot, repoManifests]) => buildLegacyDirectoryEntry(repoRoot, repoManifests))
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
async function ensureManagedSourcesArtifact(rootDir) {
|
|
4798
|
+
const { paths } = await initWorkspace(rootDir);
|
|
4799
|
+
if (await fileExists(paths.managedSourcesPath)) {
|
|
4800
|
+
const existing = await readJsonFile(paths.managedSourcesPath);
|
|
4801
|
+
if (existing?.version === MANAGED_SOURCES_VERSION && Array.isArray(existing.sources)) {
|
|
4802
|
+
return {
|
|
4803
|
+
version: MANAGED_SOURCES_VERSION,
|
|
4804
|
+
sources: [...existing.sources].sort(
|
|
4805
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
|
|
4806
|
+
)
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
const artifact = await buildLegacyArtifact(paths);
|
|
4811
|
+
await writeJsonFile(paths.managedSourcesPath, artifact);
|
|
4812
|
+
return artifact;
|
|
4813
|
+
}
|
|
4814
|
+
async function loadManagedSources(rootDir) {
|
|
4815
|
+
const artifact = await ensureManagedSourcesArtifact(rootDir);
|
|
4816
|
+
return artifact.sources;
|
|
4817
|
+
}
|
|
4818
|
+
async function readManagedSourcesIfPresent(rootDir) {
|
|
4819
|
+
const { paths } = await initWorkspace(rootDir);
|
|
4820
|
+
if (!await fileExists(paths.managedSourcesPath)) {
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4823
|
+
const existing = await readJsonFile(paths.managedSourcesPath);
|
|
4824
|
+
if (!existing?.version || !Array.isArray(existing.sources)) {
|
|
4825
|
+
return null;
|
|
4826
|
+
}
|
|
4827
|
+
return existing.sources;
|
|
4828
|
+
}
|
|
4829
|
+
async function saveManagedSources(rootDir, sources) {
|
|
4830
|
+
const { paths } = await initWorkspace(rootDir);
|
|
4831
|
+
await writeJsonFile(paths.managedSourcesPath, {
|
|
4832
|
+
version: MANAGED_SOURCES_VERSION,
|
|
4833
|
+
sources: [...sources].sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4836
|
+
async function managedSourceWorkingDir(rootDir, sourceId) {
|
|
4837
|
+
const { paths } = await initWorkspace(rootDir);
|
|
4838
|
+
return path10.join(paths.managedSourcesDir, sourceId);
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
// src/watch-state.ts
|
|
4842
|
+
import fs10 from "fs/promises";
|
|
4843
|
+
import path11 from "path";
|
|
4730
4844
|
import matter2 from "gray-matter";
|
|
4731
4845
|
function pendingEntryKey(entry) {
|
|
4732
4846
|
return entry.path;
|
|
@@ -4740,7 +4854,7 @@ function normalizeRelativePath(rootDir, filePath) {
|
|
|
4740
4854
|
if (!filePath) {
|
|
4741
4855
|
return void 0;
|
|
4742
4856
|
}
|
|
4743
|
-
return toPosix(
|
|
4857
|
+
return toPosix(path11.relative(rootDir, path11.resolve(filePath)));
|
|
4744
4858
|
}
|
|
4745
4859
|
async function readPendingSemanticRefresh(rootDir) {
|
|
4746
4860
|
const { paths } = await initWorkspace(rootDir);
|
|
@@ -4834,11 +4948,11 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
4834
4948
|
if (page.freshness !== "stale" || !page.sourceIds.some((sourceId) => affectedSourceIds.has(sourceId))) {
|
|
4835
4949
|
continue;
|
|
4836
4950
|
}
|
|
4837
|
-
const absolutePath =
|
|
4951
|
+
const absolutePath = path11.join(paths.wikiDir, page.path);
|
|
4838
4952
|
if (!await fileExists(absolutePath)) {
|
|
4839
4953
|
continue;
|
|
4840
4954
|
}
|
|
4841
|
-
const raw = await
|
|
4955
|
+
const raw = await fs10.readFile(absolutePath, "utf8");
|
|
4842
4956
|
const parsed = matter2(raw);
|
|
4843
4957
|
if (parsed.data.freshness === "stale") {
|
|
4844
4958
|
continue;
|
|
@@ -4888,7 +5002,7 @@ function inferKind(mimeType, filePath) {
|
|
|
4888
5002
|
return "binary";
|
|
4889
5003
|
}
|
|
4890
5004
|
function isRstFilePath(filePath) {
|
|
4891
|
-
const extension =
|
|
5005
|
+
const extension = path12.extname(filePath).toLowerCase();
|
|
4892
5006
|
return extension === ".rst" || extension === ".rest";
|
|
4893
5007
|
}
|
|
4894
5008
|
function titleFromText(fallback, content, filePath) {
|
|
@@ -5031,7 +5145,7 @@ function normalizeIngestOptions(options) {
|
|
|
5031
5145
|
return {
|
|
5032
5146
|
includeAssets: options?.includeAssets ?? true,
|
|
5033
5147
|
maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
|
|
5034
|
-
repoRoot: options?.repoRoot ?
|
|
5148
|
+
repoRoot: options?.repoRoot ? path12.resolve(options.repoRoot) : void 0,
|
|
5035
5149
|
include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
5036
5150
|
exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
5037
5151
|
maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
|
|
@@ -5051,27 +5165,27 @@ async function resolveRepoIngestOptions(rootDir, options) {
|
|
|
5051
5165
|
}
|
|
5052
5166
|
function matchesAnyGlob2(relativePath, patterns) {
|
|
5053
5167
|
return patterns.some(
|
|
5054
|
-
(pattern) =>
|
|
5168
|
+
(pattern) => path12.matchesGlob(relativePath, pattern) || path12.matchesGlob(path12.posix.basename(relativePath), pattern)
|
|
5055
5169
|
);
|
|
5056
5170
|
}
|
|
5057
5171
|
function supportedDirectoryKind(sourceKind) {
|
|
5058
5172
|
return sourceKind !== "binary";
|
|
5059
5173
|
}
|
|
5060
5174
|
async function findNearestGitRoot2(startPath) {
|
|
5061
|
-
let current =
|
|
5175
|
+
let current = path12.resolve(startPath);
|
|
5062
5176
|
try {
|
|
5063
|
-
const stat = await
|
|
5177
|
+
const stat = await fs11.stat(current);
|
|
5064
5178
|
if (!stat.isDirectory()) {
|
|
5065
|
-
current =
|
|
5179
|
+
current = path12.dirname(current);
|
|
5066
5180
|
}
|
|
5067
5181
|
} catch {
|
|
5068
|
-
current =
|
|
5182
|
+
current = path12.dirname(current);
|
|
5069
5183
|
}
|
|
5070
5184
|
while (true) {
|
|
5071
|
-
if (await fileExists(
|
|
5185
|
+
if (await fileExists(path12.join(current, ".git"))) {
|
|
5072
5186
|
return current;
|
|
5073
5187
|
}
|
|
5074
|
-
const parent =
|
|
5188
|
+
const parent = path12.dirname(current);
|
|
5075
5189
|
if (parent === current) {
|
|
5076
5190
|
return null;
|
|
5077
5191
|
}
|
|
@@ -5079,26 +5193,26 @@ async function findNearestGitRoot2(startPath) {
|
|
|
5079
5193
|
}
|
|
5080
5194
|
}
|
|
5081
5195
|
function withinRoot(rootPath, targetPath) {
|
|
5082
|
-
const relative =
|
|
5083
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
5196
|
+
const relative = path12.relative(rootPath, targetPath);
|
|
5197
|
+
return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
|
|
5084
5198
|
}
|
|
5085
|
-
function
|
|
5199
|
+
function repoRootFromManifest2(manifest) {
|
|
5086
5200
|
if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
|
|
5087
5201
|
return null;
|
|
5088
5202
|
}
|
|
5089
|
-
const repoDir =
|
|
5090
|
-
const fileDir =
|
|
5203
|
+
const repoDir = path12.posix.dirname(manifest.repoRelativePath);
|
|
5204
|
+
const fileDir = path12.dirname(path12.resolve(manifest.originalPath));
|
|
5091
5205
|
if (repoDir === "." || !repoDir) {
|
|
5092
5206
|
return fileDir;
|
|
5093
5207
|
}
|
|
5094
5208
|
const segments = repoDir.split("/").filter(Boolean);
|
|
5095
|
-
return
|
|
5209
|
+
return path12.resolve(fileDir, ...segments.map(() => ".."));
|
|
5096
5210
|
}
|
|
5097
5211
|
function repoRelativePathFor(absolutePath, repoRoot) {
|
|
5098
5212
|
if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
|
|
5099
5213
|
return void 0;
|
|
5100
5214
|
}
|
|
5101
|
-
const relative = toPosix(
|
|
5215
|
+
const relative = toPosix(path12.relative(repoRoot, absolutePath));
|
|
5102
5216
|
return relative && !relative.startsWith("..") ? relative : void 0;
|
|
5103
5217
|
}
|
|
5104
5218
|
function normalizeOriginUrl(input) {
|
|
@@ -5430,7 +5544,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
|
|
|
5430
5544
|
return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
|
|
5431
5545
|
}
|
|
5432
5546
|
function sanitizeAssetRelativePath(value) {
|
|
5433
|
-
const normalized =
|
|
5547
|
+
const normalized = path12.posix.normalize(value.replace(/\\/g, "/"));
|
|
5434
5548
|
const segments = normalized.split("/").filter(Boolean).map((segment) => {
|
|
5435
5549
|
if (segment === ".") {
|
|
5436
5550
|
return "";
|
|
@@ -5450,7 +5564,7 @@ function normalizeLocalReference(value) {
|
|
|
5450
5564
|
return null;
|
|
5451
5565
|
}
|
|
5452
5566
|
const lowered = candidate.toLowerCase();
|
|
5453
|
-
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") ||
|
|
5567
|
+
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path12.isAbsolute(candidate)) {
|
|
5454
5568
|
return null;
|
|
5455
5569
|
}
|
|
5456
5570
|
return candidate.replace(/\\/g, "/");
|
|
@@ -5528,12 +5642,12 @@ async function convertHtmlToMarkdown(html, url) {
|
|
|
5528
5642
|
};
|
|
5529
5643
|
}
|
|
5530
5644
|
async function readManifestByHash(manifestsDir, contentHash) {
|
|
5531
|
-
const entries = await
|
|
5645
|
+
const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
5532
5646
|
for (const entry of entries) {
|
|
5533
5647
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
5534
5648
|
continue;
|
|
5535
5649
|
}
|
|
5536
|
-
const manifest = await readJsonFile(
|
|
5650
|
+
const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
|
|
5537
5651
|
if (manifest?.contentHash === contentHash) {
|
|
5538
5652
|
return manifest;
|
|
5539
5653
|
}
|
|
@@ -5541,12 +5655,12 @@ async function readManifestByHash(manifestsDir, contentHash) {
|
|
|
5541
5655
|
return null;
|
|
5542
5656
|
}
|
|
5543
5657
|
async function readManifestByOrigin(manifestsDir, prepared) {
|
|
5544
|
-
const entries = await
|
|
5658
|
+
const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
5545
5659
|
for (const entry of entries) {
|
|
5546
5660
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
5547
5661
|
continue;
|
|
5548
5662
|
}
|
|
5549
|
-
const manifest = await readJsonFile(
|
|
5663
|
+
const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
|
|
5550
5664
|
if (manifest && manifestMatchesOrigin(manifest, prepared)) {
|
|
5551
5665
|
return manifest;
|
|
5552
5666
|
}
|
|
@@ -5557,12 +5671,12 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
|
5557
5671
|
if (!enabled) {
|
|
5558
5672
|
return null;
|
|
5559
5673
|
}
|
|
5560
|
-
const gitignorePath =
|
|
5674
|
+
const gitignorePath = path12.join(repoRoot, ".gitignore");
|
|
5561
5675
|
if (!await fileExists(gitignorePath)) {
|
|
5562
5676
|
return null;
|
|
5563
5677
|
}
|
|
5564
5678
|
const matcher = ignore();
|
|
5565
|
-
matcher.add(await
|
|
5679
|
+
matcher.add(await fs11.readFile(gitignorePath, "utf8"));
|
|
5566
5680
|
return matcher;
|
|
5567
5681
|
}
|
|
5568
5682
|
function builtInIgnoreReason(relativePath) {
|
|
@@ -5586,23 +5700,23 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
5586
5700
|
if (!currentDir) {
|
|
5587
5701
|
continue;
|
|
5588
5702
|
}
|
|
5589
|
-
const entries = await
|
|
5703
|
+
const entries = await fs11.readdir(currentDir, { withFileTypes: true });
|
|
5590
5704
|
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
5591
5705
|
for (const entry of entries) {
|
|
5592
|
-
const absolutePath =
|
|
5593
|
-
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
5706
|
+
const absolutePath = path12.join(currentDir, entry.name);
|
|
5707
|
+
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(inputDir, absolutePath));
|
|
5594
5708
|
const relativePath = relativeToRepo || entry.name;
|
|
5595
5709
|
const builtInReason = builtInIgnoreReason(relativePath);
|
|
5596
5710
|
if (builtInReason) {
|
|
5597
|
-
skipped.push({ path: toPosix(
|
|
5711
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: builtInReason });
|
|
5598
5712
|
continue;
|
|
5599
5713
|
}
|
|
5600
5714
|
if (matcher?.ignores(relativePath)) {
|
|
5601
|
-
skipped.push({ path: toPosix(
|
|
5715
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "gitignore" });
|
|
5602
5716
|
continue;
|
|
5603
5717
|
}
|
|
5604
5718
|
if (matchesAnyGlob2(relativePath, options.exclude)) {
|
|
5605
|
-
skipped.push({ path: toPosix(
|
|
5719
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "exclude_glob" });
|
|
5606
5720
|
continue;
|
|
5607
5721
|
}
|
|
5608
5722
|
if (entry.isDirectory()) {
|
|
@@ -5610,26 +5724,26 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
5610
5724
|
continue;
|
|
5611
5725
|
}
|
|
5612
5726
|
if (!entry.isFile()) {
|
|
5613
|
-
skipped.push({ path: toPosix(
|
|
5727
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
|
|
5614
5728
|
continue;
|
|
5615
5729
|
}
|
|
5616
5730
|
if (options.include.length > 0 && !matchesAnyGlob2(relativePath, options.include)) {
|
|
5617
|
-
skipped.push({ path: toPosix(
|
|
5731
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "include_glob" });
|
|
5618
5732
|
continue;
|
|
5619
5733
|
}
|
|
5620
5734
|
const mimeType = guessMimeType(absolutePath);
|
|
5621
5735
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
5622
5736
|
const sourceClass = sourceClassForRelativePath(relativePath, options);
|
|
5623
5737
|
if (!supportedDirectoryKind(sourceKind)) {
|
|
5624
|
-
skipped.push({ path: toPosix(
|
|
5738
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
5625
5739
|
continue;
|
|
5626
5740
|
}
|
|
5627
5741
|
if (!options.extractClasses.includes(sourceClass)) {
|
|
5628
|
-
skipped.push({ path: toPosix(
|
|
5742
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
|
|
5629
5743
|
continue;
|
|
5630
5744
|
}
|
|
5631
5745
|
if (files.length >= options.maxFiles) {
|
|
5632
|
-
skipped.push({ path: toPosix(
|
|
5746
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "max_files" });
|
|
5633
5747
|
continue;
|
|
5634
5748
|
}
|
|
5635
5749
|
files.push(absolutePath);
|
|
@@ -5651,12 +5765,12 @@ function resolveUrlMimeType(input, response) {
|
|
|
5651
5765
|
function buildRemoteAssetRelativePath(assetUrl, mimeType) {
|
|
5652
5766
|
const url = new URL(assetUrl);
|
|
5653
5767
|
const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
|
|
5654
|
-
const extension =
|
|
5655
|
-
const directory =
|
|
5656
|
-
const basename = extension ?
|
|
5768
|
+
const extension = path12.posix.extname(normalized);
|
|
5769
|
+
const directory = path12.posix.dirname(normalized);
|
|
5770
|
+
const basename = extension ? path12.posix.basename(normalized, extension) : path12.posix.basename(normalized);
|
|
5657
5771
|
const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
5658
5772
|
const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
|
|
5659
|
-
return directory === "." ? hashedName :
|
|
5773
|
+
return directory === "." ? hashedName : path12.posix.join(directory, hashedName);
|
|
5660
5774
|
}
|
|
5661
5775
|
async function readResponseBytesWithinLimit(response, maxBytes) {
|
|
5662
5776
|
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
@@ -5809,34 +5923,34 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
5809
5923
|
const previous = existingByOrigin ?? void 0;
|
|
5810
5924
|
const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
|
|
5811
5925
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5812
|
-
const storedPath =
|
|
5813
|
-
const extractedTextPath = prepared.extractedText ?
|
|
5814
|
-
const extractedMetadataPath = prepared.extractionArtifact ?
|
|
5815
|
-
const attachmentsDir =
|
|
5926
|
+
const storedPath = path12.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
|
|
5927
|
+
const extractedTextPath = prepared.extractedText ? path12.join(paths.extractsDir, `${sourceId}.md`) : void 0;
|
|
5928
|
+
const extractedMetadataPath = prepared.extractionArtifact ? path12.join(paths.extractsDir, `${sourceId}.json`) : void 0;
|
|
5929
|
+
const attachmentsDir = path12.join(paths.rawAssetsDir, sourceId);
|
|
5816
5930
|
if (previous?.storedPath) {
|
|
5817
|
-
await
|
|
5931
|
+
await fs11.rm(path12.resolve(rootDir, previous.storedPath), { force: true });
|
|
5818
5932
|
}
|
|
5819
5933
|
if (previous?.extractedTextPath) {
|
|
5820
|
-
await
|
|
5934
|
+
await fs11.rm(path12.resolve(rootDir, previous.extractedTextPath), { force: true });
|
|
5821
5935
|
}
|
|
5822
5936
|
if (previous?.extractedMetadataPath) {
|
|
5823
|
-
await
|
|
5937
|
+
await fs11.rm(path12.resolve(rootDir, previous.extractedMetadataPath), { force: true });
|
|
5824
5938
|
}
|
|
5825
|
-
await
|
|
5826
|
-
await
|
|
5939
|
+
await fs11.rm(attachmentsDir, { recursive: true, force: true });
|
|
5940
|
+
await fs11.writeFile(storedPath, prepared.payloadBytes);
|
|
5827
5941
|
if (prepared.extractedText && extractedTextPath) {
|
|
5828
|
-
await
|
|
5942
|
+
await fs11.writeFile(extractedTextPath, prepared.extractedText, "utf8");
|
|
5829
5943
|
}
|
|
5830
5944
|
if (prepared.extractionArtifact && extractedMetadataPath) {
|
|
5831
5945
|
await writeJsonFile(extractedMetadataPath, prepared.extractionArtifact);
|
|
5832
5946
|
}
|
|
5833
5947
|
const manifestAttachments = [];
|
|
5834
5948
|
for (const attachment of attachments) {
|
|
5835
|
-
const absoluteAttachmentPath =
|
|
5836
|
-
await ensureDir(
|
|
5837
|
-
await
|
|
5949
|
+
const absoluteAttachmentPath = path12.join(attachmentsDir, attachment.relativePath);
|
|
5950
|
+
await ensureDir(path12.dirname(absoluteAttachmentPath));
|
|
5951
|
+
await fs11.writeFile(absoluteAttachmentPath, attachment.bytes);
|
|
5838
5952
|
manifestAttachments.push({
|
|
5839
|
-
path: toPosix(
|
|
5953
|
+
path: toPosix(path12.relative(rootDir, absoluteAttachmentPath)),
|
|
5840
5954
|
mimeType: attachment.mimeType,
|
|
5841
5955
|
originalPath: attachment.originalPath
|
|
5842
5956
|
});
|
|
@@ -5852,9 +5966,9 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
5852
5966
|
originalPath: prepared.originalPath,
|
|
5853
5967
|
repoRelativePath: prepared.repoRelativePath,
|
|
5854
5968
|
url: prepared.url,
|
|
5855
|
-
storedPath: toPosix(
|
|
5856
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
5857
|
-
extractedMetadataPath: extractedMetadataPath ? toPosix(
|
|
5969
|
+
storedPath: toPosix(path12.relative(rootDir, storedPath)),
|
|
5970
|
+
extractedTextPath: extractedTextPath ? toPosix(path12.relative(rootDir, extractedTextPath)) : void 0,
|
|
5971
|
+
extractedMetadataPath: extractedMetadataPath ? toPosix(path12.relative(rootDir, extractedMetadataPath)) : void 0,
|
|
5858
5972
|
extractionHash,
|
|
5859
5973
|
mimeType: prepared.mimeType,
|
|
5860
5974
|
contentHash,
|
|
@@ -5862,7 +5976,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
5862
5976
|
updatedAt: now,
|
|
5863
5977
|
attachments: manifestAttachments.length ? manifestAttachments : void 0
|
|
5864
5978
|
};
|
|
5865
|
-
await writeJsonFile(
|
|
5979
|
+
await writeJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`), manifest);
|
|
5866
5980
|
await appendLogEntry(rootDir, "ingest", prepared.title, [
|
|
5867
5981
|
`source_id=${sourceId}`,
|
|
5868
5982
|
`kind=${prepared.sourceKind}`,
|
|
@@ -5880,16 +5994,16 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
5880
5994
|
return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
|
|
5881
5995
|
}
|
|
5882
5996
|
async function removeManifestArtifacts(rootDir, manifest, paths) {
|
|
5883
|
-
await
|
|
5884
|
-
await
|
|
5997
|
+
await fs11.rm(path12.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
|
|
5998
|
+
await fs11.rm(path12.resolve(rootDir, manifest.storedPath), { force: true });
|
|
5885
5999
|
if (manifest.extractedTextPath) {
|
|
5886
|
-
await
|
|
6000
|
+
await fs11.rm(path12.resolve(rootDir, manifest.extractedTextPath), { force: true });
|
|
5887
6001
|
}
|
|
5888
6002
|
if (manifest.extractedMetadataPath) {
|
|
5889
|
-
await
|
|
6003
|
+
await fs11.rm(path12.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
|
|
5890
6004
|
}
|
|
5891
|
-
await
|
|
5892
|
-
await
|
|
6005
|
+
await fs11.rm(path12.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
|
|
6006
|
+
await fs11.rm(path12.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
|
|
5893
6007
|
}
|
|
5894
6008
|
function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
5895
6009
|
const candidates = [
|
|
@@ -5898,11 +6012,11 @@ function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
|
5898
6012
|
paths.stateDir,
|
|
5899
6013
|
paths.agentDir,
|
|
5900
6014
|
paths.inboxDir,
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
6015
|
+
path12.join(rootDir, ".claude"),
|
|
6016
|
+
path12.join(rootDir, ".cursor"),
|
|
6017
|
+
path12.join(rootDir, ".obsidian")
|
|
5904
6018
|
];
|
|
5905
|
-
return candidates.map((candidate) =>
|
|
6019
|
+
return candidates.map((candidate) => path12.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
|
|
5906
6020
|
}
|
|
5907
6021
|
function preparedMatchesManifest(manifest, prepared, contentHash) {
|
|
5908
6022
|
return manifest.contentHash === contentHash && manifest.extractionHash === (prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact)) && manifest.title === prepared.title && manifest.sourceKind === prepared.sourceKind && manifest.sourceType === prepared.sourceType && manifest.sourceClass === prepared.sourceClass && manifest.language === prepared.language && manifest.mimeType === prepared.mimeType && manifest.repoRelativePath === prepared.repoRelativePath;
|
|
@@ -5914,8 +6028,15 @@ function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
|
|
|
5914
6028
|
return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
|
|
5915
6029
|
}
|
|
5916
6030
|
async function listTrackedRepoRoots(rootDir) {
|
|
6031
|
+
const managedSources = await readManagedSourcesIfPresent(rootDir).catch(() => null);
|
|
6032
|
+
if (managedSources && managedSources.length > 0) {
|
|
6033
|
+
const directoryRoots = managedSources.filter((source) => source.kind === "directory" && source.path).map((source) => path12.resolve(source.path));
|
|
6034
|
+
if (directoryRoots.length > 0) {
|
|
6035
|
+
return [...new Set(directoryRoots)].sort((left, right) => left.localeCompare(right));
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
5917
6038
|
const manifests = await listManifests(rootDir);
|
|
5918
|
-
return [...new Set(manifests.map((manifest) =>
|
|
6039
|
+
return [...new Set(manifests.map((manifest) => repoRootFromManifest2(manifest)).filter((item) => Boolean(item)))].sort(
|
|
5919
6040
|
(left, right) => left.localeCompare(right)
|
|
5920
6041
|
);
|
|
5921
6042
|
}
|
|
@@ -5924,16 +6045,16 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5924
6045
|
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
5925
6046
|
const manifests = await listManifests(rootDir);
|
|
5926
6047
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
5927
|
-
(item) =>
|
|
6048
|
+
(item) => path12.resolve(item)
|
|
5928
6049
|
);
|
|
5929
6050
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
5930
6051
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
5931
6052
|
for (const manifest of manifests) {
|
|
5932
|
-
const repoRoot =
|
|
5933
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
6053
|
+
const repoRoot = repoRootFromManifest2(manifest);
|
|
6054
|
+
if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
|
|
5934
6055
|
continue;
|
|
5935
6056
|
}
|
|
5936
|
-
const key =
|
|
6057
|
+
const key = path12.resolve(repoRoot);
|
|
5937
6058
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
5938
6059
|
bucket.push(manifest);
|
|
5939
6060
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -5958,15 +6079,15 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5958
6079
|
skipped.push(
|
|
5959
6080
|
...collected.skipped,
|
|
5960
6081
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
5961
|
-
path: toPosix(
|
|
6082
|
+
path: toPosix(path12.relative(rootDir, absolutePath)),
|
|
5962
6083
|
reason: "workspace_generated"
|
|
5963
6084
|
}))
|
|
5964
6085
|
);
|
|
5965
6086
|
scannedCount += files.length;
|
|
5966
6087
|
const progress = createProgressReporter("sync", files.length);
|
|
5967
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
6088
|
+
const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
|
|
5968
6089
|
for (const absolutePath of files) {
|
|
5969
|
-
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
6090
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
|
|
5970
6091
|
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
5971
6092
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
5972
6093
|
if (result.isNew) {
|
|
@@ -5976,9 +6097,9 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5976
6097
|
}
|
|
5977
6098
|
progress.tick();
|
|
5978
6099
|
}
|
|
5979
|
-
progress.finish(`repo=${toPosix(
|
|
6100
|
+
progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
|
|
5980
6101
|
for (const manifest of repoManifests) {
|
|
5981
|
-
const originalPath = manifest.originalPath ?
|
|
6102
|
+
const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
|
|
5982
6103
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
5983
6104
|
await removeManifestArtifacts(rootDir, manifest, paths);
|
|
5984
6105
|
removed.push(manifest);
|
|
@@ -5986,7 +6107,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5986
6107
|
}
|
|
5987
6108
|
}
|
|
5988
6109
|
if (uniqueRoots.length > 0) {
|
|
5989
|
-
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(
|
|
6110
|
+
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","), [
|
|
5990
6111
|
`repo_roots=${uniqueRoots.length}`,
|
|
5991
6112
|
`scanned=${scannedCount}`,
|
|
5992
6113
|
`imported=${imported.length}`,
|
|
@@ -6009,16 +6130,16 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6009
6130
|
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
6010
6131
|
const manifests = await listManifests(rootDir);
|
|
6011
6132
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
6012
|
-
(item) =>
|
|
6133
|
+
(item) => path12.resolve(item)
|
|
6013
6134
|
);
|
|
6014
6135
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
6015
6136
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
6016
6137
|
for (const manifest of manifests) {
|
|
6017
|
-
const repoRoot =
|
|
6018
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
6138
|
+
const repoRoot = repoRootFromManifest2(manifest);
|
|
6139
|
+
if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
|
|
6019
6140
|
continue;
|
|
6020
6141
|
}
|
|
6021
|
-
const key =
|
|
6142
|
+
const key = path12.resolve(repoRoot);
|
|
6022
6143
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
6023
6144
|
bucket.push(manifest);
|
|
6024
6145
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -6033,7 +6154,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6033
6154
|
for (const repoRoot of uniqueRoots) {
|
|
6034
6155
|
const repoManifests = manifestsByRepoRoot.get(repoRoot) ?? [];
|
|
6035
6156
|
const manifestsByOriginalPath = new Map(
|
|
6036
|
-
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [
|
|
6157
|
+
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path12.resolve(manifest.originalPath), manifest])
|
|
6037
6158
|
);
|
|
6038
6159
|
if (!await fileExists(repoRoot)) {
|
|
6039
6160
|
for (const manifest of repoManifests) {
|
|
@@ -6041,7 +6162,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6041
6162
|
pendingSemanticRefresh.push({
|
|
6042
6163
|
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? manifest.storedPath),
|
|
6043
6164
|
repoRoot,
|
|
6044
|
-
path: toPosix(
|
|
6165
|
+
path: toPosix(path12.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
|
|
6045
6166
|
changeType: "removed",
|
|
6046
6167
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6047
6168
|
sourceId: manifest.sourceId,
|
|
@@ -6061,18 +6182,18 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6061
6182
|
skipped.push(
|
|
6062
6183
|
...collected.skipped,
|
|
6063
6184
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
6064
|
-
path: toPosix(
|
|
6185
|
+
path: toPosix(path12.relative(rootDir, absolutePath)),
|
|
6065
6186
|
reason: "workspace_generated"
|
|
6066
6187
|
}))
|
|
6067
6188
|
);
|
|
6068
6189
|
scannedCount += files.length;
|
|
6069
6190
|
const progress = createProgressReporter("sync-watch", files.length);
|
|
6070
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
6191
|
+
const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
|
|
6071
6192
|
for (const absolutePath of files) {
|
|
6072
|
-
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
6193
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
|
|
6073
6194
|
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
6074
6195
|
if (shouldDeferWatchSemanticRefresh(prepared.sourceKind)) {
|
|
6075
|
-
const existing = manifestsByOriginalPath.get(
|
|
6196
|
+
const existing = manifestsByOriginalPath.get(path12.resolve(absolutePath));
|
|
6076
6197
|
const contentHash = buildCompositeHash(prepared.payloadBytes, prepared.attachments);
|
|
6077
6198
|
const changed = !existing || !preparedMatchesManifest(existing, prepared, contentHash);
|
|
6078
6199
|
if (changed) {
|
|
@@ -6080,10 +6201,10 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6080
6201
|
id: pendingSemanticRefreshId(
|
|
6081
6202
|
existing ? "modified" : "added",
|
|
6082
6203
|
repoRoot,
|
|
6083
|
-
prepared.repoRelativePath ?? toPosix(
|
|
6204
|
+
prepared.repoRelativePath ?? toPosix(path12.relative(repoRoot, absolutePath))
|
|
6084
6205
|
),
|
|
6085
6206
|
repoRoot,
|
|
6086
|
-
path: toPosix(
|
|
6207
|
+
path: toPosix(path12.relative(rootDir, absolutePath)),
|
|
6087
6208
|
changeType: existing ? "modified" : "added",
|
|
6088
6209
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6089
6210
|
sourceId: existing?.sourceId,
|
|
@@ -6104,15 +6225,15 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6104
6225
|
}
|
|
6105
6226
|
progress.tick();
|
|
6106
6227
|
}
|
|
6107
|
-
progress.finish(`repo=${toPosix(
|
|
6228
|
+
progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
|
|
6108
6229
|
for (const manifest of repoManifests) {
|
|
6109
|
-
const originalPath = manifest.originalPath ?
|
|
6230
|
+
const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
|
|
6110
6231
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
6111
6232
|
if (shouldDeferWatchSemanticRefresh(manifest.sourceKind)) {
|
|
6112
6233
|
pendingSemanticRefresh.push({
|
|
6113
|
-
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(
|
|
6234
|
+
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path12.relative(repoRoot, originalPath))),
|
|
6114
6235
|
repoRoot,
|
|
6115
|
-
path: toPosix(
|
|
6236
|
+
path: toPosix(path12.relative(rootDir, originalPath)),
|
|
6116
6237
|
changeType: "removed",
|
|
6117
6238
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6118
6239
|
sourceId: manifest.sourceId,
|
|
@@ -6130,7 +6251,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6130
6251
|
await appendLogEntry(
|
|
6131
6252
|
rootDir,
|
|
6132
6253
|
"sync_repo_watch",
|
|
6133
|
-
uniqueRoots.map((repoRoot) => toPosix(
|
|
6254
|
+
uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","),
|
|
6134
6255
|
[
|
|
6135
6256
|
`repo_roots=${uniqueRoots.length}`,
|
|
6136
6257
|
`scanned=${scannedCount}`,
|
|
@@ -6156,17 +6277,17 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
6156
6277
|
};
|
|
6157
6278
|
}
|
|
6158
6279
|
async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
6159
|
-
const payloadBytes = await
|
|
6280
|
+
const payloadBytes = await fs11.readFile(absoluteInput);
|
|
6160
6281
|
const mimeType = guessMimeType(absoluteInput);
|
|
6161
6282
|
const sourceKind = inferKind(mimeType, absoluteInput);
|
|
6162
6283
|
const language = inferCodeLanguage(absoluteInput, mimeType);
|
|
6163
|
-
const storedExtension =
|
|
6284
|
+
const storedExtension = path12.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
6164
6285
|
let title;
|
|
6165
6286
|
let extractedText;
|
|
6166
6287
|
let extractionArtifact;
|
|
6167
6288
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
6168
6289
|
extractedText = extractedTextForPlainSource(absoluteInput, sourceKind, payloadBytes.toString("utf8"));
|
|
6169
|
-
title = titleFromText(
|
|
6290
|
+
title = titleFromText(path12.basename(absoluteInput, path12.extname(absoluteInput)), extractedText, absoluteInput);
|
|
6170
6291
|
extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
|
|
6171
6292
|
} else if (sourceKind === "html") {
|
|
6172
6293
|
const html = payloadBytes.toString("utf8");
|
|
@@ -6175,18 +6296,18 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
|
6175
6296
|
extractedText = converted.markdown;
|
|
6176
6297
|
extractionArtifact = createHtmlReadabilityExtractionArtifact(sourceKind, mimeType);
|
|
6177
6298
|
} else if (sourceKind === "pdf") {
|
|
6178
|
-
title =
|
|
6299
|
+
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
6179
6300
|
const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
|
|
6180
6301
|
extractedText = extracted.extractedText;
|
|
6181
6302
|
extractionArtifact = extracted.artifact;
|
|
6182
6303
|
} else if (sourceKind === "docx") {
|
|
6183
|
-
title =
|
|
6304
|
+
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
6184
6305
|
const extracted = await extractDocxText({ mimeType, bytes: payloadBytes });
|
|
6185
6306
|
title = extracted.artifact.metadata?.title?.trim() || title;
|
|
6186
6307
|
extractedText = extracted.extractedText;
|
|
6187
6308
|
extractionArtifact = extracted.artifact;
|
|
6188
6309
|
} else if (sourceKind === "image") {
|
|
6189
|
-
title =
|
|
6310
|
+
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
6190
6311
|
const extracted = await extractImageWithVision(rootDir, {
|
|
6191
6312
|
title,
|
|
6192
6313
|
mimeType,
|
|
@@ -6196,7 +6317,7 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
|
6196
6317
|
extractedText = extracted.extractedText;
|
|
6197
6318
|
extractionArtifact = extracted.artifact;
|
|
6198
6319
|
} else {
|
|
6199
|
-
title =
|
|
6320
|
+
title = path12.basename(absoluteInput, path12.extname(absoluteInput));
|
|
6200
6321
|
}
|
|
6201
6322
|
return {
|
|
6202
6323
|
title,
|
|
@@ -6273,7 +6394,7 @@ async function prepareUrlInput(rootDir, input, options) {
|
|
|
6273
6394
|
sourceKind = "markdown";
|
|
6274
6395
|
storedExtension = ".md";
|
|
6275
6396
|
} else {
|
|
6276
|
-
const extension =
|
|
6397
|
+
const extension = path12.extname(inputUrl.pathname);
|
|
6277
6398
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
6278
6399
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
6279
6400
|
extractedText = extractedTextForPlainSource(inputUrl.pathname, sourceKind, payloadBytes.toString("utf8"));
|
|
@@ -6344,14 +6465,14 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
6344
6465
|
if (sourceKind !== "markdown" && sourceKind !== "html") {
|
|
6345
6466
|
continue;
|
|
6346
6467
|
}
|
|
6347
|
-
const content = await
|
|
6468
|
+
const content = await fs11.readFile(absolutePath, "utf8");
|
|
6348
6469
|
const refs = sourceKind === "html" ? extractHtmlLocalReferences(content, pathToFileURL(absolutePath).toString()) : extractMarkdownReferences(content);
|
|
6349
6470
|
if (!refs.length) {
|
|
6350
6471
|
continue;
|
|
6351
6472
|
}
|
|
6352
6473
|
const sourceRefs = [];
|
|
6353
6474
|
for (const ref of refs) {
|
|
6354
|
-
const resolved =
|
|
6475
|
+
const resolved = path12.resolve(path12.dirname(absolutePath), ref);
|
|
6355
6476
|
if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
|
|
6356
6477
|
continue;
|
|
6357
6478
|
}
|
|
@@ -6385,12 +6506,12 @@ function rewriteMarkdownReferences(content, replacements) {
|
|
|
6385
6506
|
});
|
|
6386
6507
|
}
|
|
6387
6508
|
async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
6388
|
-
const originalBytes = await
|
|
6509
|
+
const originalBytes = await fs11.readFile(absolutePath);
|
|
6389
6510
|
const originalText = originalBytes.toString("utf8");
|
|
6390
|
-
const title = titleFromText(
|
|
6511
|
+
const title = titleFromText(path12.basename(absolutePath, path12.extname(absolutePath)), originalText);
|
|
6391
6512
|
const attachments = [];
|
|
6392
6513
|
for (const attachmentRef of attachmentRefs) {
|
|
6393
|
-
const bytes = await
|
|
6514
|
+
const bytes = await fs11.readFile(attachmentRef.absolutePath);
|
|
6394
6515
|
attachments.push({
|
|
6395
6516
|
relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
|
|
6396
6517
|
mimeType: guessMimeType(attachmentRef.absolutePath),
|
|
@@ -6414,7 +6535,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
6414
6535
|
sourceKind: "markdown",
|
|
6415
6536
|
originalPath: toPosix(absolutePath),
|
|
6416
6537
|
mimeType: "text/markdown",
|
|
6417
|
-
storedExtension:
|
|
6538
|
+
storedExtension: path12.extname(absolutePath) || ".md",
|
|
6418
6539
|
payloadBytes: Buffer.from(rewrittenText, "utf8"),
|
|
6419
6540
|
extractedText: rewrittenText,
|
|
6420
6541
|
extractionArtifact,
|
|
@@ -6424,12 +6545,12 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
6424
6545
|
};
|
|
6425
6546
|
}
|
|
6426
6547
|
async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
|
|
6427
|
-
const originalBytes = await
|
|
6548
|
+
const originalBytes = await fs11.readFile(absolutePath);
|
|
6428
6549
|
const originalHtml = originalBytes.toString("utf8");
|
|
6429
6550
|
const initialConversion = await convertHtmlToMarkdown(originalHtml, pathToFileURL(absolutePath).toString());
|
|
6430
6551
|
const attachments = [];
|
|
6431
6552
|
for (const attachmentRef of attachmentRefs) {
|
|
6432
|
-
const bytes = await
|
|
6553
|
+
const bytes = await fs11.readFile(attachmentRef.absolutePath);
|
|
6433
6554
|
attachments.push({
|
|
6434
6555
|
relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
|
|
6435
6556
|
mimeType: guessMimeType(attachmentRef.absolutePath),
|
|
@@ -6438,7 +6559,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
|
|
|
6438
6559
|
});
|
|
6439
6560
|
}
|
|
6440
6561
|
const contentHash = buildCompositeHash(originalBytes, attachments);
|
|
6441
|
-
const fallbackTitle =
|
|
6562
|
+
const fallbackTitle = path12.basename(absolutePath, path12.extname(absolutePath));
|
|
6442
6563
|
const title = initialConversion.title || fallbackTitle;
|
|
6443
6564
|
const sourceId = `${slugify(title)}-${contentHash.slice(0, 8)}`;
|
|
6444
6565
|
const replacements = new Map(
|
|
@@ -6456,7 +6577,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
|
|
|
6456
6577
|
sourceKind: "html",
|
|
6457
6578
|
originalPath: toPosix(absolutePath),
|
|
6458
6579
|
mimeType: "text/html",
|
|
6459
|
-
storedExtension:
|
|
6580
|
+
storedExtension: path12.extname(absolutePath) || ".html",
|
|
6460
6581
|
payloadBytes: Buffer.from(rewrittenHtml, "utf8"),
|
|
6461
6582
|
extractedText: converted.markdown,
|
|
6462
6583
|
extractionArtifact,
|
|
@@ -6468,14 +6589,16 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
|
|
|
6468
6589
|
function isSupportedInboxKind(sourceKind) {
|
|
6469
6590
|
return ["markdown", "text", "html", "pdf", "docx", "image"].includes(sourceKind);
|
|
6470
6591
|
}
|
|
6471
|
-
async function
|
|
6592
|
+
async function ingestInputDetailed(rootDir, input, options) {
|
|
6472
6593
|
const { paths } = await initWorkspace(rootDir);
|
|
6473
6594
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
6474
|
-
const absoluteInput =
|
|
6475
|
-
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ??
|
|
6595
|
+
const absoluteInput = path12.resolve(rootDir, input);
|
|
6596
|
+
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path12.dirname(absoluteInput));
|
|
6476
6597
|
const prepared = isHttpUrl(input) ? await prepareUrlInput(rootDir, input, normalizedOptions) : await prepareFileInput(rootDir, absoluteInput, repoRoot);
|
|
6477
|
-
|
|
6478
|
-
|
|
6598
|
+
return await persistPreparedInput(rootDir, prepared, paths);
|
|
6599
|
+
}
|
|
6600
|
+
async function ingestInput(rootDir, input, options) {
|
|
6601
|
+
return (await ingestInputDetailed(rootDir, input, options)).manifest;
|
|
6479
6602
|
}
|
|
6480
6603
|
async function addInput(rootDir, input, options = {}) {
|
|
6481
6604
|
const { paths } = await initWorkspace(rootDir);
|
|
@@ -6562,7 +6685,7 @@ async function addInput(rootDir, input, options = {}) {
|
|
|
6562
6685
|
async function ingestDirectory(rootDir, inputDir, options) {
|
|
6563
6686
|
const { paths } = await initWorkspace(rootDir);
|
|
6564
6687
|
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
6565
|
-
const absoluteInputDir =
|
|
6688
|
+
const absoluteInputDir = path12.resolve(rootDir, inputDir);
|
|
6566
6689
|
const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot2(absoluteInputDir) ?? absoluteInputDir;
|
|
6567
6690
|
if (!await fileExists(absoluteInputDir)) {
|
|
6568
6691
|
throw new Error(`Directory not found: ${absoluteInputDir}`);
|
|
@@ -6572,7 +6695,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
6572
6695
|
const updated = [];
|
|
6573
6696
|
const progress = createProgressReporter("ingest", files.length);
|
|
6574
6697
|
for (const absolutePath of files) {
|
|
6575
|
-
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
6698
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
|
|
6576
6699
|
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
6577
6700
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
6578
6701
|
if (result.isNew) {
|
|
@@ -6580,13 +6703,13 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
6580
6703
|
} else if (result.wasUpdated) {
|
|
6581
6704
|
updated.push(result.manifest);
|
|
6582
6705
|
} else {
|
|
6583
|
-
skipped.push({ path: toPosix(
|
|
6706
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
6584
6707
|
}
|
|
6585
6708
|
progress.tick();
|
|
6586
6709
|
}
|
|
6587
6710
|
progress.finish(`imported=${imported.length}, updated=${updated.length}, skipped=${skipped.length}`);
|
|
6588
|
-
await appendLogEntry(rootDir, "ingest_directory", toPosix(
|
|
6589
|
-
`repo_root=${toPosix(
|
|
6711
|
+
await appendLogEntry(rootDir, "ingest_directory", toPosix(path12.relative(rootDir, absoluteInputDir)) || ".", [
|
|
6712
|
+
`repo_root=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`,
|
|
6590
6713
|
`scanned=${files.length}`,
|
|
6591
6714
|
`imported=${imported.length}`,
|
|
6592
6715
|
`updated=${updated.length}`,
|
|
@@ -6603,7 +6726,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
6603
6726
|
}
|
|
6604
6727
|
async function importInbox(rootDir, inputDir) {
|
|
6605
6728
|
const { paths } = await initWorkspace(rootDir);
|
|
6606
|
-
const effectiveInputDir =
|
|
6729
|
+
const effectiveInputDir = path12.resolve(rootDir, inputDir ?? paths.inboxDir);
|
|
6607
6730
|
if (!await fileExists(effectiveInputDir)) {
|
|
6608
6731
|
throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
|
|
6609
6732
|
}
|
|
@@ -6614,31 +6737,31 @@ async function importInbox(rootDir, inputDir) {
|
|
|
6614
6737
|
const skipped = [];
|
|
6615
6738
|
let attachmentCount = 0;
|
|
6616
6739
|
for (const absolutePath of files) {
|
|
6617
|
-
const basename =
|
|
6740
|
+
const basename = path12.basename(absolutePath);
|
|
6618
6741
|
if (basename.startsWith(".")) {
|
|
6619
|
-
skipped.push({ path: toPosix(
|
|
6742
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "hidden_file" });
|
|
6620
6743
|
continue;
|
|
6621
6744
|
}
|
|
6622
6745
|
if (claimedAttachments.has(absolutePath)) {
|
|
6623
|
-
skipped.push({ path: toPosix(
|
|
6746
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
|
|
6624
6747
|
continue;
|
|
6625
6748
|
}
|
|
6626
6749
|
const mimeType = guessMimeType(absolutePath);
|
|
6627
6750
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
6628
6751
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
6629
|
-
skipped.push({ path: toPosix(
|
|
6752
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
6630
6753
|
continue;
|
|
6631
6754
|
}
|
|
6632
6755
|
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : sourceKind === "html" && refsBySource.has(absolutePath) ? await prepareInboxHtmlInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
6633
6756
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
6634
6757
|
if (!result.isNew) {
|
|
6635
|
-
skipped.push({ path: toPosix(
|
|
6758
|
+
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
6636
6759
|
continue;
|
|
6637
6760
|
}
|
|
6638
6761
|
attachmentCount += result.manifest.attachments?.length ?? 0;
|
|
6639
6762
|
imported.push(result.manifest);
|
|
6640
6763
|
}
|
|
6641
|
-
await appendLogEntry(rootDir, "inbox_import", toPosix(
|
|
6764
|
+
await appendLogEntry(rootDir, "inbox_import", toPosix(path12.relative(rootDir, effectiveInputDir)) || ".", [
|
|
6642
6765
|
`scanned=${files.length}`,
|
|
6643
6766
|
`imported=${imported.length}`,
|
|
6644
6767
|
`attachments=${attachmentCount}`,
|
|
@@ -6657,27 +6780,36 @@ async function listManifests(rootDir) {
|
|
|
6657
6780
|
if (!await fileExists(paths.manifestsDir)) {
|
|
6658
6781
|
return [];
|
|
6659
6782
|
}
|
|
6660
|
-
const entries = await
|
|
6783
|
+
const entries = await fs11.readdir(paths.manifestsDir);
|
|
6661
6784
|
const manifests = await Promise.all(
|
|
6662
|
-
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(
|
|
6785
|
+
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path12.join(paths.manifestsDir, entry)))
|
|
6663
6786
|
);
|
|
6664
6787
|
return manifests.filter((manifest) => Boolean(manifest));
|
|
6665
6788
|
}
|
|
6789
|
+
async function removeManifestBySourceId(rootDir, sourceId) {
|
|
6790
|
+
const { paths } = await initWorkspace(rootDir);
|
|
6791
|
+
const manifest = await readJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`));
|
|
6792
|
+
if (!manifest) {
|
|
6793
|
+
return null;
|
|
6794
|
+
}
|
|
6795
|
+
await removeManifestArtifacts(rootDir, manifest, paths);
|
|
6796
|
+
return manifest;
|
|
6797
|
+
}
|
|
6666
6798
|
async function readExtractedText(rootDir, manifest) {
|
|
6667
6799
|
if (!manifest.extractedTextPath) {
|
|
6668
6800
|
return void 0;
|
|
6669
6801
|
}
|
|
6670
|
-
const absolutePath =
|
|
6802
|
+
const absolutePath = path12.resolve(rootDir, manifest.extractedTextPath);
|
|
6671
6803
|
if (!await fileExists(absolutePath)) {
|
|
6672
6804
|
return void 0;
|
|
6673
6805
|
}
|
|
6674
|
-
return
|
|
6806
|
+
return fs11.readFile(absolutePath, "utf8");
|
|
6675
6807
|
}
|
|
6676
6808
|
async function readExtractionArtifact(rootDir, manifest) {
|
|
6677
6809
|
if (!manifest.extractedMetadataPath) {
|
|
6678
6810
|
return void 0;
|
|
6679
6811
|
}
|
|
6680
|
-
const absolutePath =
|
|
6812
|
+
const absolutePath = path12.resolve(rootDir, manifest.extractedMetadataPath);
|
|
6681
6813
|
if (!await fileExists(absolutePath)) {
|
|
6682
6814
|
return void 0;
|
|
6683
6815
|
}
|
|
@@ -6685,20 +6817,20 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
6685
6817
|
}
|
|
6686
6818
|
|
|
6687
6819
|
// src/mcp.ts
|
|
6688
|
-
import
|
|
6689
|
-
import
|
|
6820
|
+
import fs19 from "fs/promises";
|
|
6821
|
+
import path23 from "path";
|
|
6690
6822
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6691
6823
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6692
6824
|
import { z as z8 } from "zod";
|
|
6693
6825
|
|
|
6694
6826
|
// src/schema.ts
|
|
6695
|
-
import
|
|
6696
|
-
import
|
|
6827
|
+
import fs12 from "fs/promises";
|
|
6828
|
+
import path13 from "path";
|
|
6697
6829
|
function normalizeSchemaContent(content) {
|
|
6698
6830
|
return content.trim() ? content.trim() : defaultVaultSchema().trim();
|
|
6699
6831
|
}
|
|
6700
6832
|
async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
6701
|
-
const content = await fileExists(schemaPath) ? await
|
|
6833
|
+
const content = await fileExists(schemaPath) ? await fs12.readFile(schemaPath, "utf8") : fallback;
|
|
6702
6834
|
const normalized = normalizeSchemaContent(content);
|
|
6703
6835
|
return {
|
|
6704
6836
|
path: schemaPath,
|
|
@@ -6707,7 +6839,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
|
6707
6839
|
};
|
|
6708
6840
|
}
|
|
6709
6841
|
function resolveProjectSchemaPath(rootDir, schemaPath) {
|
|
6710
|
-
return
|
|
6842
|
+
return path13.resolve(rootDir, schemaPath);
|
|
6711
6843
|
}
|
|
6712
6844
|
function composeVaultSchema(root, projectSchemas = []) {
|
|
6713
6845
|
if (!projectSchemas.length) {
|
|
@@ -6723,7 +6855,7 @@ function composeVaultSchema(root, projectSchemas = []) {
|
|
|
6723
6855
|
(schema) => [
|
|
6724
6856
|
`## Project Schema`,
|
|
6725
6857
|
"",
|
|
6726
|
-
`Path: ${toPosix(
|
|
6858
|
+
`Path: ${toPosix(path13.relative(path13.dirname(root.path), schema.path) || schema.path)}`,
|
|
6727
6859
|
"",
|
|
6728
6860
|
schema.content
|
|
6729
6861
|
].join("\n")
|
|
@@ -6799,13 +6931,13 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
6799
6931
|
}
|
|
6800
6932
|
|
|
6801
6933
|
// src/vault.ts
|
|
6802
|
-
import
|
|
6803
|
-
import
|
|
6934
|
+
import fs18 from "fs/promises";
|
|
6935
|
+
import path22 from "path";
|
|
6804
6936
|
import matter9 from "gray-matter";
|
|
6805
6937
|
import { z as z7 } from "zod";
|
|
6806
6938
|
|
|
6807
6939
|
// src/analysis.ts
|
|
6808
|
-
import
|
|
6940
|
+
import path14 from "path";
|
|
6809
6941
|
import { z as z2 } from "zod";
|
|
6810
6942
|
var ANALYSIS_FORMAT_VERSION = 6;
|
|
6811
6943
|
var sourceAnalysisSchema = z2.object({
|
|
@@ -7034,7 +7166,7 @@ function extractionWarningSummary(manifest, extraction) {
|
|
|
7034
7166
|
return `Imported ${manifest.sourceKind} source. Text extraction is not yet available for this source.`;
|
|
7035
7167
|
}
|
|
7036
7168
|
async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
7037
|
-
const cachePath =
|
|
7169
|
+
const cachePath = path14.join(paths.analysesDir, `${manifest.sourceId}.json`);
|
|
7038
7170
|
const cached = await readJsonFile(cachePath);
|
|
7039
7171
|
if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
|
|
7040
7172
|
return cached;
|
|
@@ -7124,8 +7256,8 @@ function conflictConfidence(claimA, claimB) {
|
|
|
7124
7256
|
}
|
|
7125
7257
|
|
|
7126
7258
|
// src/deep-lint.ts
|
|
7127
|
-
import
|
|
7128
|
-
import
|
|
7259
|
+
import fs13 from "fs/promises";
|
|
7260
|
+
import path17 from "path";
|
|
7129
7261
|
import matter4 from "gray-matter";
|
|
7130
7262
|
import { z as z5 } from "zod";
|
|
7131
7263
|
|
|
@@ -7146,7 +7278,7 @@ function normalizeFindingSeverity(value) {
|
|
|
7146
7278
|
|
|
7147
7279
|
// src/orchestration.ts
|
|
7148
7280
|
import { spawn } from "child_process";
|
|
7149
|
-
import
|
|
7281
|
+
import path15 from "path";
|
|
7150
7282
|
import { z as z3 } from "zod";
|
|
7151
7283
|
var orchestrationRoleResultSchema = z3.object({
|
|
7152
7284
|
summary: z3.string().optional(),
|
|
@@ -7239,7 +7371,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
|
|
|
7239
7371
|
}
|
|
7240
7372
|
async function runCommandRole(rootDir, role, executor, input) {
|
|
7241
7373
|
const [command, ...args] = executor.command;
|
|
7242
|
-
const cwd = executor.cwd ?
|
|
7374
|
+
const cwd = executor.cwd ? path15.resolve(rootDir, executor.cwd) : rootDir;
|
|
7243
7375
|
const child = spawn(command, args, {
|
|
7244
7376
|
cwd,
|
|
7245
7377
|
env: {
|
|
@@ -7333,7 +7465,7 @@ function summarizeRoleQuestions(results) {
|
|
|
7333
7465
|
}
|
|
7334
7466
|
|
|
7335
7467
|
// src/web-search/registry.ts
|
|
7336
|
-
import
|
|
7468
|
+
import path16 from "path";
|
|
7337
7469
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
7338
7470
|
import { z as z4 } from "zod";
|
|
7339
7471
|
|
|
@@ -7431,7 +7563,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
|
|
|
7431
7563
|
if (!config.module) {
|
|
7432
7564
|
throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
|
|
7433
7565
|
}
|
|
7434
|
-
const resolvedModule =
|
|
7566
|
+
const resolvedModule = path16.isAbsolute(config.module) ? config.module : path16.resolve(rootDir, config.module);
|
|
7435
7567
|
const loaded = await import(pathToFileURL2(resolvedModule).href);
|
|
7436
7568
|
const parsed = customWebSearchModuleSchema.parse(loaded);
|
|
7437
7569
|
return parsed.createAdapter(id, config, rootDir);
|
|
@@ -7498,8 +7630,8 @@ async function loadContextPages(rootDir, graph) {
|
|
|
7498
7630
|
);
|
|
7499
7631
|
return Promise.all(
|
|
7500
7632
|
contextPages.slice(0, 18).map(async (page) => {
|
|
7501
|
-
const absolutePath =
|
|
7502
|
-
const raw = await
|
|
7633
|
+
const absolutePath = path17.join(paths.wikiDir, page.path);
|
|
7634
|
+
const raw = await fs13.readFile(absolutePath, "utf8").catch(() => "");
|
|
7503
7635
|
const parsed = matter4(raw);
|
|
7504
7636
|
return {
|
|
7505
7637
|
id: page.id,
|
|
@@ -7547,7 +7679,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
|
|
|
7547
7679
|
code: "missing_citation",
|
|
7548
7680
|
message: finding.message,
|
|
7549
7681
|
pagePath: finding.pagePath,
|
|
7550
|
-
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${
|
|
7682
|
+
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path17.basename(finding.pagePath, ".md")}?` : void 0
|
|
7551
7683
|
});
|
|
7552
7684
|
}
|
|
7553
7685
|
for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
|
|
@@ -7726,8 +7858,8 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
|
|
|
7726
7858
|
}
|
|
7727
7859
|
|
|
7728
7860
|
// src/embeddings.ts
|
|
7729
|
-
import
|
|
7730
|
-
import
|
|
7861
|
+
import fs14 from "fs/promises";
|
|
7862
|
+
import path18 from "path";
|
|
7731
7863
|
var MAX_EMBEDDING_BATCH = 32;
|
|
7732
7864
|
var MAX_SIMILARITY_NODES = 240;
|
|
7733
7865
|
function cosineSimilarity(left, right) {
|
|
@@ -7761,8 +7893,8 @@ async function loadPageContents(rootDir, graph) {
|
|
|
7761
7893
|
const contents = /* @__PURE__ */ new Map();
|
|
7762
7894
|
await Promise.all(
|
|
7763
7895
|
graph.pages.map(async (page) => {
|
|
7764
|
-
const absolutePath =
|
|
7765
|
-
const content = await
|
|
7896
|
+
const absolutePath = path18.join(paths.wikiDir, page.path);
|
|
7897
|
+
const content = await fs14.readFile(absolutePath, "utf8").catch(() => {
|
|
7766
7898
|
process.stderr.write(`[swarmvault] Warning: could not read page ${page.path} for embedding
|
|
7767
7899
|
`);
|
|
7768
7900
|
return "";
|
|
@@ -9264,15 +9396,15 @@ function sourceTypeForNode(node, pagesById) {
|
|
|
9264
9396
|
return pagesById.get(node.pageId)?.sourceType;
|
|
9265
9397
|
}
|
|
9266
9398
|
function supportingPathDetails(graph, edge) {
|
|
9267
|
-
const
|
|
9399
|
+
const path28 = shortestGraphPath(graph, edge.source, edge.target);
|
|
9268
9400
|
const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
|
|
9269
|
-
const pathEdges =
|
|
9401
|
+
const pathEdges = path28.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
|
|
9270
9402
|
return {
|
|
9271
|
-
pathNodeIds:
|
|
9272
|
-
pathEdgeIds:
|
|
9403
|
+
pathNodeIds: path28.nodeIds,
|
|
9404
|
+
pathEdgeIds: path28.edgeIds,
|
|
9273
9405
|
pathRelations: pathEdges.map((item) => item.relation),
|
|
9274
9406
|
pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
|
|
9275
|
-
pathSummary:
|
|
9407
|
+
pathSummary: path28.summary
|
|
9276
9408
|
};
|
|
9277
9409
|
}
|
|
9278
9410
|
function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
|
|
@@ -9341,7 +9473,7 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
9341
9473
|
}).map((edge) => {
|
|
9342
9474
|
const source = nodesById.get(edge.source);
|
|
9343
9475
|
const target = nodesById.get(edge.target);
|
|
9344
|
-
const
|
|
9476
|
+
const path28 = supportingPathDetails(graph, edge);
|
|
9345
9477
|
const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
|
|
9346
9478
|
return {
|
|
9347
9479
|
id: edge.id,
|
|
@@ -9352,11 +9484,11 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
9352
9484
|
relation: edge.relation,
|
|
9353
9485
|
evidenceClass: edge.evidenceClass,
|
|
9354
9486
|
confidence: edge.confidence,
|
|
9355
|
-
pathNodeIds:
|
|
9356
|
-
pathEdgeIds:
|
|
9357
|
-
pathRelations:
|
|
9358
|
-
pathEvidenceClasses:
|
|
9359
|
-
pathSummary:
|
|
9487
|
+
pathNodeIds: path28.pathNodeIds,
|
|
9488
|
+
pathEdgeIds: path28.pathEdgeIds,
|
|
9489
|
+
pathRelations: path28.pathRelations,
|
|
9490
|
+
pathEvidenceClasses: path28.pathEvidenceClasses,
|
|
9491
|
+
pathSummary: path28.pathSummary,
|
|
9360
9492
|
why: scored.why,
|
|
9361
9493
|
explanation: scored.explanation,
|
|
9362
9494
|
surpriseScore: scored.score
|
|
@@ -10283,13 +10415,13 @@ function buildOutputAssetManifest(input) {
|
|
|
10283
10415
|
}
|
|
10284
10416
|
|
|
10285
10417
|
// src/outputs.ts
|
|
10286
|
-
import
|
|
10287
|
-
import
|
|
10418
|
+
import fs16 from "fs/promises";
|
|
10419
|
+
import path20 from "path";
|
|
10288
10420
|
import matter7 from "gray-matter";
|
|
10289
10421
|
|
|
10290
10422
|
// src/pages.ts
|
|
10291
|
-
import
|
|
10292
|
-
import
|
|
10423
|
+
import fs15 from "fs/promises";
|
|
10424
|
+
import path19 from "path";
|
|
10293
10425
|
import matter6 from "gray-matter";
|
|
10294
10426
|
function normalizeStringArray(value) {
|
|
10295
10427
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -10367,7 +10499,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
10367
10499
|
updatedAt: updatedFallback
|
|
10368
10500
|
};
|
|
10369
10501
|
}
|
|
10370
|
-
const content = await
|
|
10502
|
+
const content = await fs15.readFile(absolutePath, "utf8");
|
|
10371
10503
|
const parsed = matter6(content);
|
|
10372
10504
|
return {
|
|
10373
10505
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
@@ -10406,7 +10538,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
10406
10538
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10407
10539
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
10408
10540
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
10409
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
10541
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(relativePath, ".md");
|
|
10410
10542
|
const kind = inferPageKind(relativePath, parsed.data.kind);
|
|
10411
10543
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
10412
10544
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
@@ -10447,18 +10579,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
10447
10579
|
};
|
|
10448
10580
|
}
|
|
10449
10581
|
async function loadInsightPages(wikiDir) {
|
|
10450
|
-
const insightsDir =
|
|
10582
|
+
const insightsDir = path19.join(wikiDir, "insights");
|
|
10451
10583
|
if (!await fileExists(insightsDir)) {
|
|
10452
10584
|
return [];
|
|
10453
10585
|
}
|
|
10454
|
-
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) =>
|
|
10586
|
+
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path19.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
|
|
10455
10587
|
const insights = [];
|
|
10456
10588
|
for (const absolutePath of files) {
|
|
10457
|
-
const relativePath = toPosix(
|
|
10458
|
-
const content = await
|
|
10589
|
+
const relativePath = toPosix(path19.relative(wikiDir, absolutePath));
|
|
10590
|
+
const content = await fs15.readFile(absolutePath, "utf8");
|
|
10459
10591
|
const parsed = matter6(content);
|
|
10460
|
-
const stats = await
|
|
10461
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
10592
|
+
const stats = await fs15.stat(absolutePath);
|
|
10593
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(absolutePath, ".md");
|
|
10462
10594
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
10463
10595
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
10464
10596
|
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
@@ -10521,79 +10653,94 @@ function relatedOutputsForPage(targetPage, outputPages) {
|
|
|
10521
10653
|
return outputPages.map((page) => ({ page, rank: relationRank(page, targetPage) })).filter((item) => item.rank > 0).sort((left, right) => right.rank - left.rank || left.page.title.localeCompare(right.page.title)).map((item) => item.page);
|
|
10522
10654
|
}
|
|
10523
10655
|
async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
|
|
10524
|
-
const outputsDir =
|
|
10656
|
+
const outputsDir = path20.join(wikiDir, "outputs");
|
|
10525
10657
|
const root = baseSlug || "output";
|
|
10526
10658
|
let candidate = root;
|
|
10527
10659
|
let counter = 2;
|
|
10528
|
-
while (await fileExists(
|
|
10660
|
+
while (await fileExists(path20.join(outputsDir, `${candidate}.md`))) {
|
|
10529
10661
|
candidate = `${root}-${counter}`;
|
|
10530
10662
|
counter++;
|
|
10531
10663
|
}
|
|
10532
10664
|
return candidate;
|
|
10533
10665
|
}
|
|
10534
10666
|
async function loadSavedOutputPages(wikiDir) {
|
|
10535
|
-
const outputsDir =
|
|
10536
|
-
const entries = await
|
|
10667
|
+
const outputsDir = path20.join(wikiDir, "outputs");
|
|
10668
|
+
const entries = await fs16.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
10537
10669
|
const outputs = [];
|
|
10538
|
-
|
|
10539
|
-
|
|
10670
|
+
const queue = [{ absoluteDir: outputsDir, relativeDir: "outputs" }];
|
|
10671
|
+
while (queue.length > 0) {
|
|
10672
|
+
const current = queue.shift();
|
|
10673
|
+
if (!current) {
|
|
10540
10674
|
continue;
|
|
10541
10675
|
}
|
|
10542
|
-
const
|
|
10543
|
-
const
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10676
|
+
const currentEntries = current.absoluteDir === outputsDir ? entries : await fs16.readdir(current.absoluteDir, { withFileTypes: true }).catch(() => []);
|
|
10677
|
+
for (const entry of currentEntries) {
|
|
10678
|
+
if (entry.isDirectory()) {
|
|
10679
|
+
queue.push({
|
|
10680
|
+
absoluteDir: path20.join(current.absoluteDir, entry.name),
|
|
10681
|
+
relativeDir: path20.posix.join(current.relativeDir, entry.name)
|
|
10682
|
+
});
|
|
10683
|
+
continue;
|
|
10684
|
+
}
|
|
10685
|
+
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
|
|
10686
|
+
continue;
|
|
10687
|
+
}
|
|
10688
|
+
const relativePath = path20.posix.join(current.relativeDir, entry.name);
|
|
10689
|
+
const absolutePath = path20.join(current.absoluteDir, entry.name);
|
|
10690
|
+
const content = await fs16.readFile(absolutePath, "utf8");
|
|
10691
|
+
const parsed = matter7(content);
|
|
10692
|
+
const slug = relativePath.replace(/^outputs\//, "").replace(/\.md$/, "");
|
|
10693
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(slug);
|
|
10694
|
+
const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
|
|
10695
|
+
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
10696
|
+
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
10697
|
+
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
10698
|
+
const relatedPageIds = normalizeStringArray(parsed.data.related_page_ids);
|
|
10699
|
+
const relatedNodeIds = normalizeStringArray(parsed.data.related_node_ids);
|
|
10700
|
+
const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
|
|
10701
|
+
const backlinks = normalizeStringArray(parsed.data.backlinks);
|
|
10702
|
+
const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
|
|
10703
|
+
const stats = await fs16.stat(absolutePath);
|
|
10704
|
+
const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
|
|
10705
|
+
const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
|
|
10706
|
+
outputs.push({
|
|
10707
|
+
page: {
|
|
10708
|
+
id: pageId,
|
|
10709
|
+
path: relativePath,
|
|
10710
|
+
title,
|
|
10711
|
+
kind: "output",
|
|
10712
|
+
sourceIds,
|
|
10713
|
+
projectIds,
|
|
10714
|
+
nodeIds,
|
|
10715
|
+
freshness: parsed.data.freshness === "stale" ? "stale" : "fresh",
|
|
10716
|
+
status: normalizePageStatus(parsed.data.status, "active"),
|
|
10717
|
+
confidence: typeof parsed.data.confidence === "number" ? parsed.data.confidence : 0.74,
|
|
10718
|
+
backlinks,
|
|
10719
|
+
schemaHash: typeof parsed.data.schema_hash === "string" ? parsed.data.schema_hash : "",
|
|
10720
|
+
sourceHashes: normalizeSourceHashes(parsed.data.source_hashes),
|
|
10721
|
+
relatedPageIds,
|
|
10722
|
+
relatedNodeIds,
|
|
10723
|
+
relatedSourceIds,
|
|
10724
|
+
createdAt,
|
|
10725
|
+
updatedAt,
|
|
10726
|
+
compiledFrom: compiledFrom.length ? compiledFrom : relatedSourceIds,
|
|
10727
|
+
managedBy: normalizePageManager(parsed.data.managed_by, "system"),
|
|
10728
|
+
origin: typeof parsed.data.origin === "string" ? parsed.data.origin : void 0,
|
|
10729
|
+
question: typeof parsed.data.question === "string" ? parsed.data.question : void 0,
|
|
10730
|
+
outputFormat: parsed.data.output_format === "report" || parsed.data.output_format === "slides" || parsed.data.output_format === "chart" || parsed.data.output_format === "image" ? parsed.data.output_format : "markdown",
|
|
10731
|
+
outputAssets: normalizeOutputAssets(parsed.data.output_assets)
|
|
10732
|
+
},
|
|
10733
|
+
content,
|
|
10734
|
+
contentHash: sha256(content)
|
|
10735
|
+
});
|
|
10736
|
+
}
|
|
10590
10737
|
}
|
|
10591
10738
|
return outputs.sort((left, right) => left.page.title.localeCompare(right.page.title));
|
|
10592
10739
|
}
|
|
10593
10740
|
|
|
10594
10741
|
// src/search.ts
|
|
10595
|
-
import
|
|
10596
|
-
import
|
|
10742
|
+
import fs17 from "fs/promises";
|
|
10743
|
+
import path21 from "path";
|
|
10597
10744
|
import matter8 from "gray-matter";
|
|
10598
10745
|
function getDatabaseSync() {
|
|
10599
10746
|
const builtin = process.getBuiltinModule?.("node:sqlite");
|
|
@@ -10619,7 +10766,7 @@ function normalizeSourceClass2(value) {
|
|
|
10619
10766
|
return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
|
|
10620
10767
|
}
|
|
10621
10768
|
async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
10622
|
-
await ensureDir(
|
|
10769
|
+
await ensureDir(path21.dirname(dbPath));
|
|
10623
10770
|
const DatabaseSync = getDatabaseSync();
|
|
10624
10771
|
const db = new DatabaseSync(dbPath);
|
|
10625
10772
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -10651,8 +10798,8 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
10651
10798
|
"INSERT INTO pages (id, path, title, body, kind, status, source_type, source_class, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
10652
10799
|
);
|
|
10653
10800
|
for (const page of pages) {
|
|
10654
|
-
const absolutePath =
|
|
10655
|
-
const content = await
|
|
10801
|
+
const absolutePath = path21.join(wikiDir, page.path);
|
|
10802
|
+
const content = await fs17.readFile(absolutePath, "utf8");
|
|
10656
10803
|
const parsed = matter8(content);
|
|
10657
10804
|
insertPage.run(
|
|
10658
10805
|
page.id,
|
|
@@ -10801,7 +10948,7 @@ function outputFormatInstruction(format) {
|
|
|
10801
10948
|
}
|
|
10802
10949
|
}
|
|
10803
10950
|
function outputAssetPath(slug, fileName) {
|
|
10804
|
-
return toPosix(
|
|
10951
|
+
return toPosix(path22.join("outputs", "assets", slug, fileName));
|
|
10805
10952
|
}
|
|
10806
10953
|
function outputAssetId(slug, role) {
|
|
10807
10954
|
return `output:${slug}:asset:${role}`;
|
|
@@ -10941,7 +11088,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
10941
11088
|
if (!providerConfig) {
|
|
10942
11089
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
10943
11090
|
}
|
|
10944
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
11091
|
+
const { createProvider: createProvider2 } = await import("./registry-2REAPKPO.js");
|
|
10945
11092
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
10946
11093
|
}
|
|
10947
11094
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -11139,7 +11286,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
11139
11286
|
};
|
|
11140
11287
|
}
|
|
11141
11288
|
function normalizeProjectRoot(root) {
|
|
11142
|
-
const normalized = toPosix(
|
|
11289
|
+
const normalized = toPosix(path22.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
11143
11290
|
return normalized;
|
|
11144
11291
|
}
|
|
11145
11292
|
function projectEntries(config) {
|
|
@@ -11165,10 +11312,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
11165
11312
|
if (!rawPath) {
|
|
11166
11313
|
return toPosix(manifest.storedPath);
|
|
11167
11314
|
}
|
|
11168
|
-
if (!
|
|
11315
|
+
if (!path22.isAbsolute(rawPath)) {
|
|
11169
11316
|
return normalizeProjectRoot(rawPath);
|
|
11170
11317
|
}
|
|
11171
|
-
const relative = toPosix(
|
|
11318
|
+
const relative = toPosix(path22.relative(rootDir, rawPath));
|
|
11172
11319
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
11173
11320
|
}
|
|
11174
11321
|
function prefixMatches(value, prefix) {
|
|
@@ -11342,7 +11489,7 @@ function pageHashes(pages) {
|
|
|
11342
11489
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
11343
11490
|
}
|
|
11344
11491
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
11345
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
11492
|
+
const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
|
|
11346
11493
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
11347
11494
|
status: defaults.status ?? "active",
|
|
11348
11495
|
managedBy: defaults.managedBy
|
|
@@ -11380,7 +11527,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
11380
11527
|
return built;
|
|
11381
11528
|
}
|
|
11382
11529
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
11383
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
11530
|
+
const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
|
|
11384
11531
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
11385
11532
|
status: defaults.status ?? "active",
|
|
11386
11533
|
managedBy: defaults.managedBy
|
|
@@ -11503,7 +11650,7 @@ function resetGraphNodeMetrics(nodes) {
|
|
|
11503
11650
|
return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
|
|
11504
11651
|
}
|
|
11505
11652
|
function manifestRepoPath(manifest) {
|
|
11506
|
-
return toPosix(manifest.repoRelativePath ??
|
|
11653
|
+
return toPosix(manifest.repoRelativePath ?? path22.basename(manifest.originalPath ?? manifest.storedPath));
|
|
11507
11654
|
}
|
|
11508
11655
|
function goPackageScopeKey(manifest, analysis) {
|
|
11509
11656
|
if (analysis.code?.language !== "go") {
|
|
@@ -11513,7 +11660,7 @@ function goPackageScopeKey(manifest, analysis) {
|
|
|
11513
11660
|
if (!packageName) {
|
|
11514
11661
|
return null;
|
|
11515
11662
|
}
|
|
11516
|
-
return `${packageName}:${
|
|
11663
|
+
return `${packageName}:${path22.posix.dirname(manifestRepoPath(manifest))}`;
|
|
11517
11664
|
}
|
|
11518
11665
|
function buildGoPackageSymbolLookups(analyses, manifestsById) {
|
|
11519
11666
|
const lookups = /* @__PURE__ */ new Map();
|
|
@@ -11982,7 +12129,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
11982
12129
|
const benchmark = await readJsonFile(paths.benchmarkPath);
|
|
11983
12130
|
const communityRecords = [];
|
|
11984
12131
|
for (const community of graph.communities ?? []) {
|
|
11985
|
-
const absolutePath =
|
|
12132
|
+
const absolutePath = path22.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
|
|
11986
12133
|
communityRecords.push(
|
|
11987
12134
|
await buildManagedGraphPage(
|
|
11988
12135
|
absolutePath,
|
|
@@ -12011,7 +12158,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
12011
12158
|
graphHash: graphHash(graph),
|
|
12012
12159
|
contradictions
|
|
12013
12160
|
});
|
|
12014
|
-
const reportAbsolutePath =
|
|
12161
|
+
const reportAbsolutePath = path22.join(paths.wikiDir, "graph", "report.md");
|
|
12015
12162
|
const reportRecord = await buildManagedGraphPage(
|
|
12016
12163
|
reportAbsolutePath,
|
|
12017
12164
|
{
|
|
@@ -12032,7 +12179,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
12032
12179
|
};
|
|
12033
12180
|
}
|
|
12034
12181
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
12035
|
-
const absolutePath =
|
|
12182
|
+
const absolutePath = path22.resolve(wikiDir, relativePath);
|
|
12036
12183
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
12037
12184
|
if (changed) {
|
|
12038
12185
|
changedPages.push(relativePath);
|
|
@@ -12095,29 +12242,29 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
12095
12242
|
paths.graphPath,
|
|
12096
12243
|
paths.codeIndexPath,
|
|
12097
12244
|
paths.searchDbPath,
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12245
|
+
path22.join(paths.wikiDir, "index.md"),
|
|
12246
|
+
path22.join(paths.wikiDir, "sources", "index.md"),
|
|
12247
|
+
path22.join(paths.wikiDir, "code", "index.md"),
|
|
12248
|
+
path22.join(paths.wikiDir, "concepts", "index.md"),
|
|
12249
|
+
path22.join(paths.wikiDir, "entities", "index.md"),
|
|
12250
|
+
path22.join(paths.wikiDir, "outputs", "index.md"),
|
|
12251
|
+
path22.join(paths.wikiDir, "projects", "index.md"),
|
|
12252
|
+
path22.join(paths.wikiDir, "candidates", "index.md")
|
|
12106
12253
|
];
|
|
12107
12254
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
12108
12255
|
return checks.every(Boolean);
|
|
12109
12256
|
}
|
|
12110
12257
|
async function loadAvailableCachedAnalyses(paths, manifests) {
|
|
12111
12258
|
const analyses = await Promise.all(
|
|
12112
|
-
manifests.map(async (manifest) => readJsonFile(
|
|
12259
|
+
manifests.map(async (manifest) => readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`)))
|
|
12113
12260
|
);
|
|
12114
12261
|
return analyses.filter((analysis) => Boolean(analysis));
|
|
12115
12262
|
}
|
|
12116
12263
|
function approvalManifestPath(paths, approvalId) {
|
|
12117
|
-
return
|
|
12264
|
+
return path22.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
12118
12265
|
}
|
|
12119
12266
|
function approvalGraphPath(paths, approvalId) {
|
|
12120
|
-
return
|
|
12267
|
+
return path22.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
12121
12268
|
}
|
|
12122
12269
|
async function readApprovalManifest(paths, approvalId) {
|
|
12123
12270
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -12127,7 +12274,7 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
12127
12274
|
return manifest;
|
|
12128
12275
|
}
|
|
12129
12276
|
async function writeApprovalManifest(paths, manifest) {
|
|
12130
|
-
await
|
|
12277
|
+
await fs18.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
12131
12278
|
`, "utf8");
|
|
12132
12279
|
}
|
|
12133
12280
|
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
@@ -12142,7 +12289,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
12142
12289
|
continue;
|
|
12143
12290
|
}
|
|
12144
12291
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
12145
|
-
const currentExists = await fileExists(
|
|
12292
|
+
const currentExists = await fileExists(path22.join(paths.wikiDir, file.relativePath));
|
|
12146
12293
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
12147
12294
|
entries.push({
|
|
12148
12295
|
pageId: nextPage.id,
|
|
@@ -12175,7 +12322,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
12175
12322
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
12176
12323
|
entries.push({
|
|
12177
12324
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
12178
|
-
title: previousPage?.title ??
|
|
12325
|
+
title: previousPage?.title ?? path22.basename(deletedPath, ".md"),
|
|
12179
12326
|
kind: previousPage?.kind ?? "index",
|
|
12180
12327
|
changeType: "delete",
|
|
12181
12328
|
status: "pending",
|
|
@@ -12187,16 +12334,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
12187
12334
|
}
|
|
12188
12335
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
12189
12336
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
12190
|
-
const approvalDir =
|
|
12337
|
+
const approvalDir = path22.join(paths.approvalsDir, approvalId);
|
|
12191
12338
|
await ensureDir(approvalDir);
|
|
12192
|
-
await ensureDir(
|
|
12193
|
-
await ensureDir(
|
|
12339
|
+
await ensureDir(path22.join(approvalDir, "wiki"));
|
|
12340
|
+
await ensureDir(path22.join(approvalDir, "state"));
|
|
12194
12341
|
for (const file of changedFiles) {
|
|
12195
|
-
const targetPath =
|
|
12196
|
-
await ensureDir(
|
|
12197
|
-
await
|
|
12342
|
+
const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
|
|
12343
|
+
await ensureDir(path22.dirname(targetPath));
|
|
12344
|
+
await fs18.writeFile(targetPath, file.content, "utf8");
|
|
12198
12345
|
}
|
|
12199
|
-
await
|
|
12346
|
+
await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
12200
12347
|
await writeApprovalManifest(paths, {
|
|
12201
12348
|
approvalId,
|
|
12202
12349
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -12258,7 +12405,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12258
12405
|
confidence: 1
|
|
12259
12406
|
});
|
|
12260
12407
|
const sourceRecord = await buildManagedGraphPage(
|
|
12261
|
-
|
|
12408
|
+
path22.join(paths.wikiDir, preview.path),
|
|
12262
12409
|
{
|
|
12263
12410
|
managedBy: "system",
|
|
12264
12411
|
confidence: 1,
|
|
@@ -12304,7 +12451,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12304
12451
|
);
|
|
12305
12452
|
records.push(
|
|
12306
12453
|
await buildManagedGraphPage(
|
|
12307
|
-
|
|
12454
|
+
path22.join(paths.wikiDir, modulePreview.path),
|
|
12308
12455
|
{
|
|
12309
12456
|
managedBy: "system",
|
|
12310
12457
|
confidence: 1,
|
|
@@ -12338,8 +12485,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12338
12485
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
12339
12486
|
const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
|
|
12340
12487
|
const fallbackPaths = [
|
|
12341
|
-
|
|
12342
|
-
|
|
12488
|
+
path22.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
12489
|
+
path22.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
12343
12490
|
];
|
|
12344
12491
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
12345
12492
|
const preview = emptyGraphPage({
|
|
@@ -12357,7 +12504,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12357
12504
|
status: promoted ? "active" : "candidate"
|
|
12358
12505
|
});
|
|
12359
12506
|
const pageRecord = await buildManagedGraphPage(
|
|
12360
|
-
|
|
12507
|
+
path22.join(paths.wikiDir, relativePath),
|
|
12361
12508
|
{
|
|
12362
12509
|
status: promoted ? "active" : "candidate",
|
|
12363
12510
|
managedBy: "system",
|
|
@@ -12474,7 +12621,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12474
12621
|
confidence: 1
|
|
12475
12622
|
}),
|
|
12476
12623
|
content: await buildManagedContent(
|
|
12477
|
-
|
|
12624
|
+
path22.join(paths.wikiDir, "projects", "index.md"),
|
|
12478
12625
|
{
|
|
12479
12626
|
managedBy: "system",
|
|
12480
12627
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -12498,7 +12645,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12498
12645
|
records.push({
|
|
12499
12646
|
page: projectIndexRef,
|
|
12500
12647
|
content: await buildManagedContent(
|
|
12501
|
-
|
|
12648
|
+
path22.join(paths.wikiDir, projectIndexRef.path),
|
|
12502
12649
|
{
|
|
12503
12650
|
managedBy: "system",
|
|
12504
12651
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -12526,7 +12673,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12526
12673
|
confidence: 1
|
|
12527
12674
|
}),
|
|
12528
12675
|
content: await buildManagedContent(
|
|
12529
|
-
|
|
12676
|
+
path22.join(paths.wikiDir, "index.md"),
|
|
12530
12677
|
{
|
|
12531
12678
|
managedBy: "system",
|
|
12532
12679
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -12557,7 +12704,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12557
12704
|
confidence: 1
|
|
12558
12705
|
}),
|
|
12559
12706
|
content: await buildManagedContent(
|
|
12560
|
-
|
|
12707
|
+
path22.join(paths.wikiDir, relativePath),
|
|
12561
12708
|
{
|
|
12562
12709
|
managedBy: "system",
|
|
12563
12710
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -12568,12 +12715,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12568
12715
|
}
|
|
12569
12716
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
12570
12717
|
const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
12571
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
12718
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
12572
12719
|
const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
12573
12720
|
const changedFiles = [];
|
|
12574
12721
|
for (const record of records) {
|
|
12575
|
-
const absolutePath =
|
|
12576
|
-
const current = await fileExists(absolutePath) ? await
|
|
12722
|
+
const absolutePath = path22.join(paths.wikiDir, record.page.path);
|
|
12723
|
+
const current = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
|
|
12577
12724
|
if (current !== record.content) {
|
|
12578
12725
|
changedPages.push(record.page.path);
|
|
12579
12726
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -12598,10 +12745,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
12598
12745
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
12599
12746
|
}
|
|
12600
12747
|
for (const relativePath of obsoletePaths) {
|
|
12601
|
-
await
|
|
12748
|
+
await fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true });
|
|
12602
12749
|
}
|
|
12603
12750
|
await writeJsonFile(paths.graphPath, graph);
|
|
12604
|
-
await writeJsonFile(
|
|
12751
|
+
await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
12605
12752
|
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
12606
12753
|
await writeJsonFile(paths.compileStatePath, {
|
|
12607
12754
|
generatedAt: graph.generatedAt,
|
|
@@ -12672,17 +12819,17 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
12672
12819
|
})
|
|
12673
12820
|
);
|
|
12674
12821
|
await Promise.all([
|
|
12675
|
-
ensureDir(
|
|
12676
|
-
ensureDir(
|
|
12677
|
-
ensureDir(
|
|
12678
|
-
ensureDir(
|
|
12679
|
-
ensureDir(
|
|
12680
|
-
ensureDir(
|
|
12681
|
-
ensureDir(
|
|
12682
|
-
ensureDir(
|
|
12683
|
-
ensureDir(
|
|
12822
|
+
ensureDir(path22.join(paths.wikiDir, "sources")),
|
|
12823
|
+
ensureDir(path22.join(paths.wikiDir, "code")),
|
|
12824
|
+
ensureDir(path22.join(paths.wikiDir, "concepts")),
|
|
12825
|
+
ensureDir(path22.join(paths.wikiDir, "entities")),
|
|
12826
|
+
ensureDir(path22.join(paths.wikiDir, "outputs")),
|
|
12827
|
+
ensureDir(path22.join(paths.wikiDir, "graph")),
|
|
12828
|
+
ensureDir(path22.join(paths.wikiDir, "graph", "communities")),
|
|
12829
|
+
ensureDir(path22.join(paths.wikiDir, "projects")),
|
|
12830
|
+
ensureDir(path22.join(paths.wikiDir, "candidates"))
|
|
12684
12831
|
]);
|
|
12685
|
-
const projectsIndexPath =
|
|
12832
|
+
const projectsIndexPath = path22.join(paths.wikiDir, "projects", "index.md");
|
|
12686
12833
|
await writeFileIfChanged(
|
|
12687
12834
|
projectsIndexPath,
|
|
12688
12835
|
await buildManagedContent(
|
|
@@ -12703,7 +12850,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
12703
12850
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
12704
12851
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
12705
12852
|
};
|
|
12706
|
-
const absolutePath =
|
|
12853
|
+
const absolutePath = path22.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
12707
12854
|
await writeFileIfChanged(
|
|
12708
12855
|
absolutePath,
|
|
12709
12856
|
await buildManagedContent(
|
|
@@ -12721,7 +12868,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
12721
12868
|
)
|
|
12722
12869
|
);
|
|
12723
12870
|
}
|
|
12724
|
-
const rootIndexPath =
|
|
12871
|
+
const rootIndexPath = path22.join(paths.wikiDir, "index.md");
|
|
12725
12872
|
await writeFileIfChanged(
|
|
12726
12873
|
rootIndexPath,
|
|
12727
12874
|
await buildManagedContent(
|
|
@@ -12742,7 +12889,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
12742
12889
|
["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
|
|
12743
12890
|
["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
|
|
12744
12891
|
]) {
|
|
12745
|
-
const absolutePath =
|
|
12892
|
+
const absolutePath = path22.join(paths.wikiDir, relativePath);
|
|
12746
12893
|
await writeFileIfChanged(
|
|
12747
12894
|
absolutePath,
|
|
12748
12895
|
await buildManagedContent(
|
|
@@ -12756,23 +12903,23 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
12756
12903
|
);
|
|
12757
12904
|
}
|
|
12758
12905
|
for (const record of graphOrientation.records) {
|
|
12759
|
-
await writeFileIfChanged(
|
|
12906
|
+
await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
|
|
12760
12907
|
}
|
|
12761
12908
|
if (graphOrientation.report) {
|
|
12762
|
-
await writeJsonFile(
|
|
12909
|
+
await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
12763
12910
|
}
|
|
12764
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
12911
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
|
|
12765
12912
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
12766
12913
|
"projects/index.md",
|
|
12767
12914
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
12768
12915
|
]);
|
|
12769
12916
|
await Promise.all(
|
|
12770
|
-
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) =>
|
|
12917
|
+
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
|
|
12771
12918
|
);
|
|
12772
|
-
const existingGraphPages = (await listFilesRecursive(
|
|
12919
|
+
const existingGraphPages = (await listFilesRecursive(path22.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
|
|
12773
12920
|
const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
|
|
12774
12921
|
await Promise.all(
|
|
12775
|
-
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) =>
|
|
12922
|
+
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
|
|
12776
12923
|
);
|
|
12777
12924
|
await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
|
|
12778
12925
|
}
|
|
@@ -12792,7 +12939,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
12792
12939
|
confidence: 0.74
|
|
12793
12940
|
}
|
|
12794
12941
|
});
|
|
12795
|
-
const absolutePath =
|
|
12942
|
+
const absolutePath = path22.join(paths.wikiDir, output.page.path);
|
|
12796
12943
|
return {
|
|
12797
12944
|
page: output.page,
|
|
12798
12945
|
savedPath: absolutePath,
|
|
@@ -12804,15 +12951,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
12804
12951
|
async function persistOutputPage(rootDir, input) {
|
|
12805
12952
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12806
12953
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
12807
|
-
await ensureDir(
|
|
12808
|
-
await
|
|
12954
|
+
await ensureDir(path22.dirname(prepared.savedPath));
|
|
12955
|
+
await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
12809
12956
|
for (const assetFile of prepared.assetFiles) {
|
|
12810
|
-
const assetPath =
|
|
12811
|
-
await ensureDir(
|
|
12957
|
+
const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
|
|
12958
|
+
await ensureDir(path22.dirname(assetPath));
|
|
12812
12959
|
if (typeof assetFile.content === "string") {
|
|
12813
|
-
await
|
|
12960
|
+
await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
12814
12961
|
} else {
|
|
12815
|
-
await
|
|
12962
|
+
await fs18.writeFile(assetPath, assetFile.content);
|
|
12816
12963
|
}
|
|
12817
12964
|
}
|
|
12818
12965
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -12833,7 +12980,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
12833
12980
|
confidence: 0.76
|
|
12834
12981
|
}
|
|
12835
12982
|
});
|
|
12836
|
-
const absolutePath =
|
|
12983
|
+
const absolutePath = path22.join(paths.wikiDir, hub.page.path);
|
|
12837
12984
|
return {
|
|
12838
12985
|
page: hub.page,
|
|
12839
12986
|
savedPath: absolutePath,
|
|
@@ -12845,15 +12992,15 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
12845
12992
|
async function persistExploreHub(rootDir, input) {
|
|
12846
12993
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12847
12994
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
12848
|
-
await ensureDir(
|
|
12849
|
-
await
|
|
12995
|
+
await ensureDir(path22.dirname(prepared.savedPath));
|
|
12996
|
+
await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
12850
12997
|
for (const assetFile of prepared.assetFiles) {
|
|
12851
|
-
const assetPath =
|
|
12852
|
-
await ensureDir(
|
|
12998
|
+
const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
|
|
12999
|
+
await ensureDir(path22.dirname(assetPath));
|
|
12853
13000
|
if (typeof assetFile.content === "string") {
|
|
12854
|
-
await
|
|
13001
|
+
await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
12855
13002
|
} else {
|
|
12856
|
-
await
|
|
13003
|
+
await fs18.writeFile(assetPath, assetFile.content);
|
|
12857
13004
|
}
|
|
12858
13005
|
}
|
|
12859
13006
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -12870,17 +13017,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
12870
13017
|
}))
|
|
12871
13018
|
]);
|
|
12872
13019
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
12873
|
-
const approvalDir =
|
|
13020
|
+
const approvalDir = path22.join(paths.approvalsDir, approvalId);
|
|
12874
13021
|
await ensureDir(approvalDir);
|
|
12875
|
-
await ensureDir(
|
|
12876
|
-
await ensureDir(
|
|
13022
|
+
await ensureDir(path22.join(approvalDir, "wiki"));
|
|
13023
|
+
await ensureDir(path22.join(approvalDir, "state"));
|
|
12877
13024
|
for (const file of changedFiles) {
|
|
12878
|
-
const targetPath =
|
|
12879
|
-
await ensureDir(
|
|
13025
|
+
const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
|
|
13026
|
+
await ensureDir(path22.dirname(targetPath));
|
|
12880
13027
|
if ("binary" in file && file.binary) {
|
|
12881
|
-
await
|
|
13028
|
+
await fs18.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
12882
13029
|
} else {
|
|
12883
|
-
await
|
|
13030
|
+
await fs18.writeFile(targetPath, file.content, "utf8");
|
|
12884
13031
|
}
|
|
12885
13032
|
}
|
|
12886
13033
|
const nextPages = sortGraphPages([
|
|
@@ -12895,7 +13042,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
12895
13042
|
sources: previousGraph?.sources ?? [],
|
|
12896
13043
|
pages: nextPages
|
|
12897
13044
|
};
|
|
12898
|
-
await
|
|
13045
|
+
await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
12899
13046
|
await writeApprovalManifest(paths, {
|
|
12900
13047
|
approvalId,
|
|
12901
13048
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -12924,9 +13071,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
12924
13071
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
12925
13072
|
const excerpts = await Promise.all(
|
|
12926
13073
|
searchResults.map(async (result) => {
|
|
12927
|
-
const absolutePath =
|
|
13074
|
+
const absolutePath = path22.join(paths.wikiDir, result.path);
|
|
12928
13075
|
try {
|
|
12929
|
-
const content = await
|
|
13076
|
+
const content = await fs18.readFile(absolutePath, "utf8");
|
|
12930
13077
|
const parsed = matter9(content);
|
|
12931
13078
|
return `# ${result.title}
|
|
12932
13079
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -13160,7 +13307,7 @@ function computeChangeSummary(current, staged, changeType) {
|
|
|
13160
13307
|
async function listApprovals(rootDir) {
|
|
13161
13308
|
const { paths } = await loadVaultConfig(rootDir);
|
|
13162
13309
|
const manifests = await Promise.all(
|
|
13163
|
-
(await
|
|
13310
|
+
(await fs18.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
13164
13311
|
try {
|
|
13165
13312
|
return await readApprovalManifest(paths, entry.name);
|
|
13166
13313
|
} catch {
|
|
@@ -13176,8 +13323,8 @@ async function readApproval(rootDir, approvalId, options) {
|
|
|
13176
13323
|
const details = await Promise.all(
|
|
13177
13324
|
manifest.entries.map(async (entry) => {
|
|
13178
13325
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
13179
|
-
const currentContent = currentPath ? await
|
|
13180
|
-
const stagedContent = entry.nextPath ? await
|
|
13326
|
+
const currentContent = currentPath ? await fs18.readFile(path22.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
13327
|
+
const stagedContent = entry.nextPath ? await fs18.readFile(path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
13181
13328
|
const detail = {
|
|
13182
13329
|
...entry,
|
|
13183
13330
|
currentContent,
|
|
@@ -13210,26 +13357,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
13210
13357
|
if (!entry.nextPath) {
|
|
13211
13358
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
13212
13359
|
}
|
|
13213
|
-
const stagedAbsolutePath =
|
|
13214
|
-
const stagedContent = await
|
|
13215
|
-
const targetAbsolutePath =
|
|
13216
|
-
await ensureDir(
|
|
13217
|
-
await
|
|
13360
|
+
const stagedAbsolutePath = path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
|
|
13361
|
+
const stagedContent = await fs18.readFile(stagedAbsolutePath, "utf8");
|
|
13362
|
+
const targetAbsolutePath = path22.join(paths.wikiDir, entry.nextPath);
|
|
13363
|
+
await ensureDir(path22.dirname(targetAbsolutePath));
|
|
13364
|
+
await fs18.writeFile(targetAbsolutePath, stagedContent, "utf8");
|
|
13218
13365
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
13219
|
-
await
|
|
13366
|
+
await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
13220
13367
|
}
|
|
13221
13368
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
13222
13369
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
13223
|
-
const outputAssetDir =
|
|
13224
|
-
await
|
|
13370
|
+
const outputAssetDir = path22.join(paths.wikiDir, "outputs", "assets", path22.basename(nextPage.path, ".md"));
|
|
13371
|
+
await fs18.rm(outputAssetDir, { recursive: true, force: true });
|
|
13225
13372
|
for (const asset of nextPage.outputAssets) {
|
|
13226
|
-
const stagedAssetPath =
|
|
13373
|
+
const stagedAssetPath = path22.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
13227
13374
|
if (!await fileExists(stagedAssetPath)) {
|
|
13228
13375
|
continue;
|
|
13229
13376
|
}
|
|
13230
|
-
const targetAssetPath =
|
|
13231
|
-
await ensureDir(
|
|
13232
|
-
await
|
|
13377
|
+
const targetAssetPath = path22.join(paths.wikiDir, asset.path);
|
|
13378
|
+
await ensureDir(path22.dirname(targetAssetPath));
|
|
13379
|
+
await fs18.copyFile(stagedAssetPath, targetAssetPath);
|
|
13233
13380
|
}
|
|
13234
13381
|
}
|
|
13235
13382
|
nextPages = nextPages.filter(
|
|
@@ -13240,10 +13387,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
13240
13387
|
} else {
|
|
13241
13388
|
const deletedPage = nextPages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? bundleGraph?.pages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? null;
|
|
13242
13389
|
if (entry.previousPath) {
|
|
13243
|
-
await
|
|
13390
|
+
await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
13244
13391
|
}
|
|
13245
13392
|
if (deletedPage?.kind === "output") {
|
|
13246
|
-
await
|
|
13393
|
+
await fs18.rm(path22.join(paths.wikiDir, "outputs", "assets", path22.basename(deletedPage.path, ".md")), {
|
|
13247
13394
|
recursive: true,
|
|
13248
13395
|
force: true
|
|
13249
13396
|
});
|
|
@@ -13334,7 +13481,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
13334
13481
|
const { paths } = await loadVaultConfig(rootDir);
|
|
13335
13482
|
const graph = await readJsonFile(paths.graphPath);
|
|
13336
13483
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
13337
|
-
const raw = await
|
|
13484
|
+
const raw = await fs18.readFile(path22.join(paths.wikiDir, candidate.path), "utf8");
|
|
13338
13485
|
const parsed = matter9(raw);
|
|
13339
13486
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13340
13487
|
const nextContent = matter9.stringify(parsed.content, {
|
|
@@ -13346,10 +13493,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
13346
13493
|
)
|
|
13347
13494
|
});
|
|
13348
13495
|
const nextPath = candidateActivePath(candidate);
|
|
13349
|
-
const nextAbsolutePath =
|
|
13350
|
-
await ensureDir(
|
|
13351
|
-
await
|
|
13352
|
-
await
|
|
13496
|
+
const nextAbsolutePath = path22.join(paths.wikiDir, nextPath);
|
|
13497
|
+
await ensureDir(path22.dirname(nextAbsolutePath));
|
|
13498
|
+
await fs18.writeFile(nextAbsolutePath, nextContent, "utf8");
|
|
13499
|
+
await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
|
|
13353
13500
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
13354
13501
|
const nextPages = sortGraphPages(
|
|
13355
13502
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -13394,7 +13541,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
13394
13541
|
const { paths } = await loadVaultConfig(rootDir);
|
|
13395
13542
|
const graph = await readJsonFile(paths.graphPath);
|
|
13396
13543
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
13397
|
-
await
|
|
13544
|
+
await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
|
|
13398
13545
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
13399
13546
|
const nextGraph = {
|
|
13400
13547
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13433,18 +13580,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
13433
13580
|
}
|
|
13434
13581
|
async function ensureObsidianWorkspace(rootDir) {
|
|
13435
13582
|
const { config } = await loadVaultConfig(rootDir);
|
|
13436
|
-
const obsidianDir =
|
|
13583
|
+
const obsidianDir = path22.join(rootDir, ".obsidian");
|
|
13437
13584
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
13438
13585
|
await ensureDir(obsidianDir);
|
|
13439
13586
|
await Promise.all([
|
|
13440
|
-
writeJsonFile(
|
|
13587
|
+
writeJsonFile(path22.join(obsidianDir, "app.json"), {
|
|
13441
13588
|
alwaysUpdateLinks: true,
|
|
13442
13589
|
newFileLocation: "folder",
|
|
13443
13590
|
newFileFolderPath: "wiki/insights",
|
|
13444
13591
|
useMarkdownLinks: false,
|
|
13445
13592
|
attachmentFolderPath: "raw/assets"
|
|
13446
13593
|
}),
|
|
13447
|
-
writeJsonFile(
|
|
13594
|
+
writeJsonFile(path22.join(obsidianDir, "core-plugins.json"), [
|
|
13448
13595
|
"file-explorer",
|
|
13449
13596
|
"global-search",
|
|
13450
13597
|
"switcher",
|
|
@@ -13454,7 +13601,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
13454
13601
|
"tag-pane",
|
|
13455
13602
|
"page-preview"
|
|
13456
13603
|
]),
|
|
13457
|
-
writeJsonFile(
|
|
13604
|
+
writeJsonFile(path22.join(obsidianDir, "graph.json"), {
|
|
13458
13605
|
"collapse-filter": false,
|
|
13459
13606
|
search: "",
|
|
13460
13607
|
showTags: true,
|
|
@@ -13466,7 +13613,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
13466
13613
|
})),
|
|
13467
13614
|
localJumps: false
|
|
13468
13615
|
}),
|
|
13469
|
-
writeJsonFile(
|
|
13616
|
+
writeJsonFile(path22.join(obsidianDir, "workspace.json"), {
|
|
13470
13617
|
active: "root",
|
|
13471
13618
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
13472
13619
|
left: {
|
|
@@ -13481,7 +13628,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
13481
13628
|
async function initVault(rootDir, options = {}) {
|
|
13482
13629
|
const { paths } = await initWorkspace(rootDir);
|
|
13483
13630
|
await installConfiguredAgents(rootDir);
|
|
13484
|
-
const insightsIndexPath =
|
|
13631
|
+
const insightsIndexPath = path22.join(paths.wikiDir, "insights", "index.md");
|
|
13485
13632
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13486
13633
|
await writeFileIfChanged(
|
|
13487
13634
|
insightsIndexPath,
|
|
@@ -13517,7 +13664,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
13517
13664
|
)
|
|
13518
13665
|
);
|
|
13519
13666
|
await writeFileIfChanged(
|
|
13520
|
-
|
|
13667
|
+
path22.join(paths.wikiDir, "projects", "index.md"),
|
|
13521
13668
|
matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
13522
13669
|
page_id: "projects:index",
|
|
13523
13670
|
kind: "index",
|
|
@@ -13539,7 +13686,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
13539
13686
|
})
|
|
13540
13687
|
);
|
|
13541
13688
|
await writeFileIfChanged(
|
|
13542
|
-
|
|
13689
|
+
path22.join(paths.wikiDir, "candidates", "index.md"),
|
|
13543
13690
|
matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
13544
13691
|
page_id: "candidates:index",
|
|
13545
13692
|
kind: "index",
|
|
@@ -13678,7 +13825,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
13678
13825
|
),
|
|
13679
13826
|
Promise.all(
|
|
13680
13827
|
clean.map(async (manifest) => {
|
|
13681
|
-
const cached = await readJsonFile(
|
|
13828
|
+
const cached = await readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
13682
13829
|
if (cached) {
|
|
13683
13830
|
analysisProgress.tick(manifest.title);
|
|
13684
13831
|
return cached;
|
|
@@ -13706,22 +13853,22 @@ async function compileVault(rootDir, options = {}) {
|
|
|
13706
13853
|
}
|
|
13707
13854
|
const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
|
|
13708
13855
|
if (analysisSignature(enriched) !== analysisSignature(analysis)) {
|
|
13709
|
-
await writeJsonFile(
|
|
13856
|
+
await writeJsonFile(path22.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
|
|
13710
13857
|
}
|
|
13711
13858
|
return enriched;
|
|
13712
13859
|
})
|
|
13713
13860
|
);
|
|
13714
13861
|
await Promise.all([
|
|
13715
|
-
ensureDir(
|
|
13716
|
-
ensureDir(
|
|
13717
|
-
ensureDir(
|
|
13718
|
-
ensureDir(
|
|
13719
|
-
ensureDir(
|
|
13720
|
-
ensureDir(
|
|
13721
|
-
ensureDir(
|
|
13722
|
-
ensureDir(
|
|
13723
|
-
ensureDir(
|
|
13724
|
-
ensureDir(
|
|
13862
|
+
ensureDir(path22.join(paths.wikiDir, "sources")),
|
|
13863
|
+
ensureDir(path22.join(paths.wikiDir, "code")),
|
|
13864
|
+
ensureDir(path22.join(paths.wikiDir, "concepts")),
|
|
13865
|
+
ensureDir(path22.join(paths.wikiDir, "entities")),
|
|
13866
|
+
ensureDir(path22.join(paths.wikiDir, "outputs")),
|
|
13867
|
+
ensureDir(path22.join(paths.wikiDir, "projects")),
|
|
13868
|
+
ensureDir(path22.join(paths.wikiDir, "insights")),
|
|
13869
|
+
ensureDir(path22.join(paths.wikiDir, "candidates")),
|
|
13870
|
+
ensureDir(path22.join(paths.wikiDir, "candidates", "concepts")),
|
|
13871
|
+
ensureDir(path22.join(paths.wikiDir, "candidates", "entities"))
|
|
13725
13872
|
]);
|
|
13726
13873
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
13727
13874
|
schemas,
|
|
@@ -13868,7 +14015,7 @@ async function queryVault(rootDir, options) {
|
|
|
13868
14015
|
assetFiles: staged.assetFiles
|
|
13869
14016
|
}
|
|
13870
14017
|
]);
|
|
13871
|
-
stagedPath =
|
|
14018
|
+
stagedPath = path22.join(approval.approvalDir, "wiki", staged.page.path);
|
|
13872
14019
|
savedPageId = staged.page.id;
|
|
13873
14020
|
approvalId = approval.approvalId;
|
|
13874
14021
|
approvalDir = approval.approvalDir;
|
|
@@ -14124,9 +14271,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
14124
14271
|
approvalId = approval.approvalId;
|
|
14125
14272
|
approvalDir = approval.approvalDir;
|
|
14126
14273
|
stepResults.forEach((result, index) => {
|
|
14127
|
-
result.stagedPath =
|
|
14274
|
+
result.stagedPath = path22.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
14128
14275
|
});
|
|
14129
|
-
stagedHubPath =
|
|
14276
|
+
stagedHubPath = path22.join(approval.approvalDir, "wiki", hubPage.path);
|
|
14130
14277
|
} else {
|
|
14131
14278
|
await refreshVaultAfterOutputSave(rootDir);
|
|
14132
14279
|
}
|
|
@@ -14213,11 +14360,11 @@ async function benchmarkVault(rootDir, options = {}) {
|
|
|
14213
14360
|
}
|
|
14214
14361
|
}
|
|
14215
14362
|
for (const page of graph.pages) {
|
|
14216
|
-
const absolutePath =
|
|
14363
|
+
const absolutePath = path22.join(paths.wikiDir, page.path);
|
|
14217
14364
|
if (!await fileExists(absolutePath)) {
|
|
14218
14365
|
continue;
|
|
14219
14366
|
}
|
|
14220
|
-
const parsed = matter9(await
|
|
14367
|
+
const parsed = matter9(await fs18.readFile(absolutePath, "utf8"));
|
|
14221
14368
|
pageContentsById.set(page.id, parsed.content);
|
|
14222
14369
|
}
|
|
14223
14370
|
const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
@@ -14262,7 +14409,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
|
|
|
14262
14409
|
}
|
|
14263
14410
|
async function readGraphReport(rootDir) {
|
|
14264
14411
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14265
|
-
return readJsonFile(
|
|
14412
|
+
return readJsonFile(path22.join(paths.wikiDir, "graph", "report.json"));
|
|
14266
14413
|
}
|
|
14267
14414
|
async function listGodNodes(rootDir, limit = 10) {
|
|
14268
14415
|
const graph = await ensureCompiledGraph(rootDir);
|
|
@@ -14275,15 +14422,15 @@ async function listPages(rootDir) {
|
|
|
14275
14422
|
}
|
|
14276
14423
|
async function readPage(rootDir, relativePath) {
|
|
14277
14424
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14278
|
-
const absolutePath =
|
|
14425
|
+
const absolutePath = path22.resolve(paths.wikiDir, relativePath);
|
|
14279
14426
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
14280
14427
|
return null;
|
|
14281
14428
|
}
|
|
14282
|
-
const raw = await
|
|
14429
|
+
const raw = await fs18.readFile(absolutePath, "utf8");
|
|
14283
14430
|
const parsed = matter9(raw);
|
|
14284
14431
|
return {
|
|
14285
14432
|
path: relativePath,
|
|
14286
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
14433
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path22.basename(relativePath, path22.extname(relativePath)),
|
|
14287
14434
|
frontmatter: parsed.data,
|
|
14288
14435
|
content: parsed.content
|
|
14289
14436
|
};
|
|
@@ -14319,7 +14466,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
14319
14466
|
severity: "warning",
|
|
14320
14467
|
code: "stale_page",
|
|
14321
14468
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
14322
|
-
pagePath:
|
|
14469
|
+
pagePath: path22.join(paths.wikiDir, page.path),
|
|
14323
14470
|
relatedPageIds: [page.id]
|
|
14324
14471
|
});
|
|
14325
14472
|
}
|
|
@@ -14330,7 +14477,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
14330
14477
|
severity: "warning",
|
|
14331
14478
|
code: "stale_page",
|
|
14332
14479
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
14333
|
-
pagePath:
|
|
14480
|
+
pagePath: path22.join(paths.wikiDir, page.path),
|
|
14334
14481
|
relatedSourceIds: [sourceId],
|
|
14335
14482
|
relatedPageIds: [page.id]
|
|
14336
14483
|
});
|
|
@@ -14341,13 +14488,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
14341
14488
|
severity: "info",
|
|
14342
14489
|
code: "orphan_page",
|
|
14343
14490
|
message: `Page ${page.title} has no backlinks.`,
|
|
14344
|
-
pagePath:
|
|
14491
|
+
pagePath: path22.join(paths.wikiDir, page.path),
|
|
14345
14492
|
relatedPageIds: [page.id]
|
|
14346
14493
|
});
|
|
14347
14494
|
}
|
|
14348
|
-
const absolutePath =
|
|
14495
|
+
const absolutePath = path22.join(paths.wikiDir, page.path);
|
|
14349
14496
|
if (await fileExists(absolutePath)) {
|
|
14350
|
-
const content = await
|
|
14497
|
+
const content = await fs18.readFile(absolutePath, "utf8");
|
|
14351
14498
|
if (content.includes("## Claims")) {
|
|
14352
14499
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
14353
14500
|
if (uncited.length) {
|
|
@@ -14464,7 +14611,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
14464
14611
|
}
|
|
14465
14612
|
|
|
14466
14613
|
// src/mcp.ts
|
|
14467
|
-
var SERVER_VERSION = "0.
|
|
14614
|
+
var SERVER_VERSION = "0.2.0";
|
|
14468
14615
|
async function createMcpServer(rootDir) {
|
|
14469
14616
|
const server = new McpServer({
|
|
14470
14617
|
name: "swarmvault",
|
|
@@ -14735,7 +14882,7 @@ async function createMcpServer(rootDir) {
|
|
|
14735
14882
|
},
|
|
14736
14883
|
async () => {
|
|
14737
14884
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14738
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
14885
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
|
|
14739
14886
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
14740
14887
|
}
|
|
14741
14888
|
);
|
|
@@ -14768,8 +14915,8 @@ async function createMcpServer(rootDir) {
|
|
|
14768
14915
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
14769
14916
|
}
|
|
14770
14917
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14771
|
-
const absolutePath =
|
|
14772
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
14918
|
+
const absolutePath = path23.resolve(paths.wikiDir, relativePath);
|
|
14919
|
+
return asTextResource(`swarmvault://pages/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
|
|
14773
14920
|
}
|
|
14774
14921
|
);
|
|
14775
14922
|
server.registerResource(
|
|
@@ -14777,11 +14924,11 @@ async function createMcpServer(rootDir) {
|
|
|
14777
14924
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
14778
14925
|
list: async () => {
|
|
14779
14926
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14780
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
14927
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
|
|
14781
14928
|
return {
|
|
14782
14929
|
resources: files.map((relativePath) => ({
|
|
14783
14930
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
14784
|
-
name:
|
|
14931
|
+
name: path23.basename(relativePath, ".md"),
|
|
14785
14932
|
title: relativePath,
|
|
14786
14933
|
description: "SwarmVault session artifact",
|
|
14787
14934
|
mimeType: "text/markdown"
|
|
@@ -14798,11 +14945,11 @@ async function createMcpServer(rootDir) {
|
|
|
14798
14945
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14799
14946
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
14800
14947
|
const relativePath = decodeURIComponent(encodedPath);
|
|
14801
|
-
const absolutePath =
|
|
14948
|
+
const absolutePath = path23.resolve(paths.sessionsDir, relativePath);
|
|
14802
14949
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
14803
14950
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
14804
14951
|
}
|
|
14805
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
14952
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
|
|
14806
14953
|
}
|
|
14807
14954
|
);
|
|
14808
14955
|
return server;
|
|
@@ -14850,13 +14997,13 @@ function asTextResource(uri, text) {
|
|
|
14850
14997
|
}
|
|
14851
14998
|
|
|
14852
14999
|
// src/schedule.ts
|
|
14853
|
-
import
|
|
14854
|
-
import
|
|
15000
|
+
import fs20 from "fs/promises";
|
|
15001
|
+
import path24 from "path";
|
|
14855
15002
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
14856
|
-
return
|
|
15003
|
+
return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
14857
15004
|
}
|
|
14858
15005
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
14859
|
-
return
|
|
15006
|
+
return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
14860
15007
|
}
|
|
14861
15008
|
function parseEveryDuration(value) {
|
|
14862
15009
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -14959,13 +15106,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
14959
15106
|
const { paths } = await loadVaultConfig(rootDir);
|
|
14960
15107
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
14961
15108
|
await ensureDir(paths.schedulesDir);
|
|
14962
|
-
const handle = await
|
|
15109
|
+
const handle = await fs20.open(leasePath, "wx");
|
|
14963
15110
|
await handle.writeFile(`${process.pid}
|
|
14964
15111
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
14965
15112
|
`);
|
|
14966
15113
|
await handle.close();
|
|
14967
15114
|
return async () => {
|
|
14968
|
-
await
|
|
15115
|
+
await fs20.rm(leasePath, { force: true });
|
|
14969
15116
|
};
|
|
14970
15117
|
}
|
|
14971
15118
|
async function listSchedules(rootDir) {
|
|
@@ -15111,33 +15258,750 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
15111
15258
|
};
|
|
15112
15259
|
}
|
|
15113
15260
|
|
|
15261
|
+
// src/sources.ts
|
|
15262
|
+
import { spawn as spawn2 } from "child_process";
|
|
15263
|
+
import fs21 from "fs/promises";
|
|
15264
|
+
import path25 from "path";
|
|
15265
|
+
import { JSDOM as JSDOM3 } from "jsdom";
|
|
15266
|
+
var DEFAULT_CRAWL_MAX_PAGES = 12;
|
|
15267
|
+
var DEFAULT_CRAWL_MAX_DEPTH = 2;
|
|
15268
|
+
var DOCS_HINT_SEGMENTS = /* @__PURE__ */ new Set([
|
|
15269
|
+
"docs",
|
|
15270
|
+
"documentation",
|
|
15271
|
+
"wiki",
|
|
15272
|
+
"help",
|
|
15273
|
+
"reference",
|
|
15274
|
+
"references",
|
|
15275
|
+
"guide",
|
|
15276
|
+
"guides",
|
|
15277
|
+
"tutorial",
|
|
15278
|
+
"tutorials",
|
|
15279
|
+
"manual",
|
|
15280
|
+
"api",
|
|
15281
|
+
"apis",
|
|
15282
|
+
"getting-started"
|
|
15283
|
+
]);
|
|
15284
|
+
function uniqueStrings4(values) {
|
|
15285
|
+
return uniqueBy(values.filter(Boolean), (value) => value);
|
|
15286
|
+
}
|
|
15287
|
+
function normalizeManagedStatus(value) {
|
|
15288
|
+
return value === "missing" || value === "error" ? value : "ready";
|
|
15289
|
+
}
|
|
15290
|
+
function withinRoot2(rootPath, targetPath) {
|
|
15291
|
+
const relative = path25.relative(rootPath, targetPath);
|
|
15292
|
+
return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
|
|
15293
|
+
}
|
|
15294
|
+
async function findNearestGitRoot3(startPath) {
|
|
15295
|
+
let current = path25.resolve(startPath);
|
|
15296
|
+
try {
|
|
15297
|
+
const stat = await fs21.stat(current);
|
|
15298
|
+
if (!stat.isDirectory()) {
|
|
15299
|
+
current = path25.dirname(current);
|
|
15300
|
+
}
|
|
15301
|
+
} catch {
|
|
15302
|
+
current = path25.dirname(current);
|
|
15303
|
+
}
|
|
15304
|
+
while (true) {
|
|
15305
|
+
if (await fileExists(path25.join(current, ".git"))) {
|
|
15306
|
+
return current;
|
|
15307
|
+
}
|
|
15308
|
+
const parent = path25.dirname(current);
|
|
15309
|
+
if (parent === current) {
|
|
15310
|
+
return null;
|
|
15311
|
+
}
|
|
15312
|
+
current = parent;
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
function normalizeUrlWithoutHash(input) {
|
|
15316
|
+
const url = new URL(input);
|
|
15317
|
+
url.hash = "";
|
|
15318
|
+
if (url.protocol === "http:" && url.port === "80" || url.protocol === "https:" && url.port === "443") {
|
|
15319
|
+
url.port = "";
|
|
15320
|
+
}
|
|
15321
|
+
return url.toString();
|
|
15322
|
+
}
|
|
15323
|
+
function normalizeGitHubRepoRootUrl(input) {
|
|
15324
|
+
let parsed;
|
|
15325
|
+
try {
|
|
15326
|
+
parsed = new URL(input);
|
|
15327
|
+
} catch {
|
|
15328
|
+
return null;
|
|
15329
|
+
}
|
|
15330
|
+
const host = parsed.hostname.toLowerCase();
|
|
15331
|
+
if (host !== "github.com" && host !== "www.github.com") {
|
|
15332
|
+
return null;
|
|
15333
|
+
}
|
|
15334
|
+
const segments = parsed.pathname.split("/").map((segment) => segment.trim()).filter(Boolean);
|
|
15335
|
+
if (segments.length !== 2) {
|
|
15336
|
+
return null;
|
|
15337
|
+
}
|
|
15338
|
+
const [owner, repoSegment] = segments;
|
|
15339
|
+
const repo = repoSegment.replace(/\.git$/i, "");
|
|
15340
|
+
if (!owner || !repo) {
|
|
15341
|
+
return null;
|
|
15342
|
+
}
|
|
15343
|
+
const url = `https://github.com/${owner}/${repo}`;
|
|
15344
|
+
return {
|
|
15345
|
+
url,
|
|
15346
|
+
cloneUrl: `${url}.git`,
|
|
15347
|
+
title: `${owner}/${repo}`
|
|
15348
|
+
};
|
|
15349
|
+
}
|
|
15350
|
+
function looksLikeDocsPathname(pathname) {
|
|
15351
|
+
const segments = pathname.split("/").map((segment) => segment.trim().toLowerCase()).filter(Boolean);
|
|
15352
|
+
return segments.some((segment) => DOCS_HINT_SEGMENTS.has(segment));
|
|
15353
|
+
}
|
|
15354
|
+
function isLikelyDocsStartUrl(url) {
|
|
15355
|
+
return looksLikeDocsPathname(url.pathname) || url.hostname.toLowerCase().startsWith("docs.");
|
|
15356
|
+
}
|
|
15357
|
+
function normalizeCrawlCandidate(href, baseUrl) {
|
|
15358
|
+
try {
|
|
15359
|
+
const url = new URL(href, baseUrl);
|
|
15360
|
+
if (!["http:", "https:"].includes(url.protocol)) {
|
|
15361
|
+
return null;
|
|
15362
|
+
}
|
|
15363
|
+
if (url.hash) {
|
|
15364
|
+
url.hash = "";
|
|
15365
|
+
}
|
|
15366
|
+
return url.toString();
|
|
15367
|
+
} catch {
|
|
15368
|
+
return null;
|
|
15369
|
+
}
|
|
15370
|
+
}
|
|
15371
|
+
function pathPrefix(pathname) {
|
|
15372
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
15373
|
+
if (segments.length === 0) {
|
|
15374
|
+
return "/";
|
|
15375
|
+
}
|
|
15376
|
+
return `/${segments[0]}`;
|
|
15377
|
+
}
|
|
15378
|
+
function isAllowedDocsCandidate(candidate, startUrl) {
|
|
15379
|
+
if (candidate.origin !== startUrl.origin) {
|
|
15380
|
+
return false;
|
|
15381
|
+
}
|
|
15382
|
+
const extension = path25.extname(candidate.pathname).toLowerCase();
|
|
15383
|
+
if (extension && extension !== ".html" && extension !== ".htm" && extension !== ".md") {
|
|
15384
|
+
return false;
|
|
15385
|
+
}
|
|
15386
|
+
if (looksLikeDocsPathname(candidate.pathname)) {
|
|
15387
|
+
return true;
|
|
15388
|
+
}
|
|
15389
|
+
const startPrefix = pathPrefix(startUrl.pathname);
|
|
15390
|
+
const candidatePrefix = pathPrefix(candidate.pathname);
|
|
15391
|
+
return startPrefix !== "/" && candidatePrefix === startPrefix;
|
|
15392
|
+
}
|
|
15393
|
+
async function fetchHtml(url) {
|
|
15394
|
+
await validateUrlSafety(url);
|
|
15395
|
+
const response = await fetch(url);
|
|
15396
|
+
if (!response.ok) {
|
|
15397
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
15398
|
+
}
|
|
15399
|
+
const contentType = response.headers.get("content-type")?.split(";")[0]?.trim() ?? "text/html";
|
|
15400
|
+
if (!contentType.includes("html")) {
|
|
15401
|
+
throw new Error(`Unsupported docs crawl content type at ${url}: ${contentType}`);
|
|
15402
|
+
}
|
|
15403
|
+
const html = await response.text();
|
|
15404
|
+
const dom = new JSDOM3(html, { url });
|
|
15405
|
+
const document = dom.window.document;
|
|
15406
|
+
const title = document.title.trim() || url;
|
|
15407
|
+
const links = [...document.querySelectorAll("a[href]")].map((anchor) => normalizeCrawlCandidate(anchor.getAttribute("href") ?? "", url)).filter((value) => Boolean(value));
|
|
15408
|
+
return { title, links };
|
|
15409
|
+
}
|
|
15410
|
+
async function crawlDocsSource(url, maxPages, maxDepth) {
|
|
15411
|
+
const startUrl = new URL(normalizeUrlWithoutHash(url));
|
|
15412
|
+
const initial = await fetchHtml(startUrl.toString());
|
|
15413
|
+
const sameDomainDocsLinks = uniqueStrings4(
|
|
15414
|
+
initial.links.filter((candidate) => {
|
|
15415
|
+
const parsed = new URL(candidate);
|
|
15416
|
+
return isAllowedDocsCandidate(parsed, startUrl);
|
|
15417
|
+
})
|
|
15418
|
+
);
|
|
15419
|
+
if (!isLikelyDocsStartUrl(startUrl) && sameDomainDocsLinks.length < 3) {
|
|
15420
|
+
throw new Error(
|
|
15421
|
+
"This URL does not look like a docs hub. Use `swarmvault add` for single articles or `swarmvault ingest` for direct files."
|
|
15422
|
+
);
|
|
15423
|
+
}
|
|
15424
|
+
const visited = /* @__PURE__ */ new Set();
|
|
15425
|
+
const queued = /* @__PURE__ */ new Set();
|
|
15426
|
+
const pages = [];
|
|
15427
|
+
const queue = [{ url: startUrl.toString(), depth: 0 }];
|
|
15428
|
+
queued.add(startUrl.toString());
|
|
15429
|
+
while (queue.length > 0 && pages.length < maxPages) {
|
|
15430
|
+
const current = queue.shift();
|
|
15431
|
+
if (!current) {
|
|
15432
|
+
continue;
|
|
15433
|
+
}
|
|
15434
|
+
if (visited.has(current.url)) {
|
|
15435
|
+
continue;
|
|
15436
|
+
}
|
|
15437
|
+
visited.add(current.url);
|
|
15438
|
+
pages.push(current.url);
|
|
15439
|
+
if (current.depth >= maxDepth) {
|
|
15440
|
+
continue;
|
|
15441
|
+
}
|
|
15442
|
+
const { links } = await fetchHtml(current.url);
|
|
15443
|
+
for (const candidate of links) {
|
|
15444
|
+
if (pages.length + queue.length >= maxPages) {
|
|
15445
|
+
break;
|
|
15446
|
+
}
|
|
15447
|
+
if (queued.has(candidate) || visited.has(candidate)) {
|
|
15448
|
+
continue;
|
|
15449
|
+
}
|
|
15450
|
+
const parsed = new URL(candidate);
|
|
15451
|
+
if (!isAllowedDocsCandidate(parsed, startUrl)) {
|
|
15452
|
+
continue;
|
|
15453
|
+
}
|
|
15454
|
+
queued.add(candidate);
|
|
15455
|
+
queue.push({ url: candidate, depth: current.depth + 1 });
|
|
15456
|
+
}
|
|
15457
|
+
}
|
|
15458
|
+
return {
|
|
15459
|
+
title: initial.title,
|
|
15460
|
+
pages
|
|
15461
|
+
};
|
|
15462
|
+
}
|
|
15463
|
+
function stableManagedSourceId(kind, raw, fallbackTitle) {
|
|
15464
|
+
return `${kind}-${slugify(fallbackTitle)}-${sha256(raw).slice(0, 8)}`;
|
|
15465
|
+
}
|
|
15466
|
+
function matchesManagedSourceSpec(existing, input) {
|
|
15467
|
+
if (existing.kind !== input.kind) {
|
|
15468
|
+
return false;
|
|
15469
|
+
}
|
|
15470
|
+
if (input.kind === "directory") {
|
|
15471
|
+
return path25.resolve(existing.path ?? "") === path25.resolve(input.path);
|
|
15472
|
+
}
|
|
15473
|
+
return (existing.url ?? "") === input.url;
|
|
15474
|
+
}
|
|
15475
|
+
async function resolveManagedSourceInput(rootDir, input) {
|
|
15476
|
+
const absoluteInput = path25.resolve(rootDir, input);
|
|
15477
|
+
if (!(input.startsWith("http://") || input.startsWith("https://"))) {
|
|
15478
|
+
const stat = await fs21.stat(absoluteInput).catch(() => null);
|
|
15479
|
+
if (!stat) {
|
|
15480
|
+
throw new Error(`Source not found: ${input}`);
|
|
15481
|
+
}
|
|
15482
|
+
if (!stat.isDirectory()) {
|
|
15483
|
+
throw new Error(
|
|
15484
|
+
"`swarmvault source add` supports directories, public GitHub repo root URLs, and docs hubs. Use `swarmvault ingest` for single files."
|
|
15485
|
+
);
|
|
15486
|
+
}
|
|
15487
|
+
const detectedRepoRoot = await findNearestGitRoot3(absoluteInput);
|
|
15488
|
+
const repoRoot = detectedRepoRoot && !(withinRoot2(rootDir, absoluteInput) && !withinRoot2(rootDir, detectedRepoRoot)) ? detectedRepoRoot : absoluteInput;
|
|
15489
|
+
return {
|
|
15490
|
+
kind: "directory",
|
|
15491
|
+
path: absoluteInput,
|
|
15492
|
+
repoRoot,
|
|
15493
|
+
title: path25.basename(absoluteInput) || absoluteInput
|
|
15494
|
+
};
|
|
15495
|
+
}
|
|
15496
|
+
const github = normalizeGitHubRepoRootUrl(input);
|
|
15497
|
+
if (github) {
|
|
15498
|
+
return {
|
|
15499
|
+
kind: "github_repo",
|
|
15500
|
+
...github
|
|
15501
|
+
};
|
|
15502
|
+
}
|
|
15503
|
+
const parsed = new URL(input);
|
|
15504
|
+
if (parsed.hostname.toLowerCase().includes("github.com")) {
|
|
15505
|
+
throw new Error(
|
|
15506
|
+
"`swarmvault source add` only supports public GitHub repo root URLs. Use a repo root like https://github.com/owner/repo."
|
|
15507
|
+
);
|
|
15508
|
+
}
|
|
15509
|
+
return {
|
|
15510
|
+
kind: "crawl_url",
|
|
15511
|
+
url: normalizeUrlWithoutHash(input),
|
|
15512
|
+
title: parsed.hostname
|
|
15513
|
+
};
|
|
15514
|
+
}
|
|
15515
|
+
function directorySourceIdsFor(manifests, inputPath) {
|
|
15516
|
+
return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
|
|
15517
|
+
}
|
|
15518
|
+
async function syncDirectorySource(rootDir, inputPath, repoRoot) {
|
|
15519
|
+
const manifestsBefore = await listManifests(rootDir);
|
|
15520
|
+
const previousInScope = manifestsBefore.filter(
|
|
15521
|
+
(manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))
|
|
15522
|
+
);
|
|
15523
|
+
const result = await ingestDirectory(rootDir, inputPath, { repoRoot });
|
|
15524
|
+
const removed = [];
|
|
15525
|
+
for (const manifest of previousInScope) {
|
|
15526
|
+
if (!manifest.originalPath) {
|
|
15527
|
+
continue;
|
|
15528
|
+
}
|
|
15529
|
+
if (await fileExists(path25.resolve(manifest.originalPath))) {
|
|
15530
|
+
continue;
|
|
15531
|
+
}
|
|
15532
|
+
const removedManifest = await removeManifestBySourceId(rootDir, manifest.sourceId);
|
|
15533
|
+
if (removedManifest) {
|
|
15534
|
+
removed.push(removedManifest.sourceId);
|
|
15535
|
+
}
|
|
15536
|
+
}
|
|
15537
|
+
const manifestsAfter = await listManifests(rootDir);
|
|
15538
|
+
return {
|
|
15539
|
+
title: path25.basename(inputPath) || inputPath,
|
|
15540
|
+
sourceIds: directorySourceIdsFor(manifestsAfter, inputPath),
|
|
15541
|
+
counts: {
|
|
15542
|
+
scannedCount: result.scannedCount,
|
|
15543
|
+
importedCount: result.imported.length,
|
|
15544
|
+
updatedCount: result.updated.length,
|
|
15545
|
+
removedCount: removed.length,
|
|
15546
|
+
skippedCount: result.skipped.length
|
|
15547
|
+
},
|
|
15548
|
+
changed: result.imported.length + result.updated.length + removed.length > 0
|
|
15549
|
+
};
|
|
15550
|
+
}
|
|
15551
|
+
async function runGitCommand(cwd, args) {
|
|
15552
|
+
await new Promise((resolve, reject) => {
|
|
15553
|
+
const child = spawn2("git", args, {
|
|
15554
|
+
cwd,
|
|
15555
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
15556
|
+
});
|
|
15557
|
+
let stderr = "";
|
|
15558
|
+
child.stderr.on("data", (chunk) => {
|
|
15559
|
+
stderr += chunk.toString("utf8");
|
|
15560
|
+
});
|
|
15561
|
+
child.on("error", reject);
|
|
15562
|
+
child.on("close", (code) => {
|
|
15563
|
+
if (code === 0) {
|
|
15564
|
+
resolve();
|
|
15565
|
+
return;
|
|
15566
|
+
}
|
|
15567
|
+
reject(new Error(stderr.trim() || `git ${args.join(" ")} failed with exit code ${code ?? 1}`));
|
|
15568
|
+
});
|
|
15569
|
+
});
|
|
15570
|
+
}
|
|
15571
|
+
async function syncGitHubRepoSource(rootDir, entry) {
|
|
15572
|
+
const workingDir = await managedSourceWorkingDir(rootDir, entry.id);
|
|
15573
|
+
const checkoutDir = path25.join(workingDir, "checkout");
|
|
15574
|
+
await fs21.rm(checkoutDir, { recursive: true, force: true });
|
|
15575
|
+
await ensureDir(workingDir);
|
|
15576
|
+
if (!entry.url) {
|
|
15577
|
+
throw new Error(`Managed source ${entry.id} is missing its repository URL.`);
|
|
15578
|
+
}
|
|
15579
|
+
const github = normalizeGitHubRepoRootUrl(entry.url);
|
|
15580
|
+
if (!github) {
|
|
15581
|
+
throw new Error(`Managed source ${entry.id} has an invalid GitHub repo URL.`);
|
|
15582
|
+
}
|
|
15583
|
+
await runGitCommand(workingDir, ["clone", "--depth", "1", github.cloneUrl, "checkout"]);
|
|
15584
|
+
return await syncDirectorySource(rootDir, checkoutDir, checkoutDir);
|
|
15585
|
+
}
|
|
15586
|
+
async function syncCrawlSource(rootDir, entry, options) {
|
|
15587
|
+
if (!entry.url) {
|
|
15588
|
+
throw new Error(`Managed source ${entry.id} is missing its URL.`);
|
|
15589
|
+
}
|
|
15590
|
+
const crawl = await crawlDocsSource(entry.url, options.maxPages ?? DEFAULT_CRAWL_MAX_PAGES, options.maxDepth ?? DEFAULT_CRAWL_MAX_DEPTH);
|
|
15591
|
+
const previousSourceIds = [...entry.sourceIds];
|
|
15592
|
+
const currentSourceIds = [];
|
|
15593
|
+
let importedCount = 0;
|
|
15594
|
+
let updatedCount = 0;
|
|
15595
|
+
for (const pageUrl of crawl.pages) {
|
|
15596
|
+
const persisted = await ingestInputDetailed(rootDir, pageUrl);
|
|
15597
|
+
currentSourceIds.push(persisted.manifest.sourceId);
|
|
15598
|
+
if (persisted.isNew) {
|
|
15599
|
+
importedCount += 1;
|
|
15600
|
+
} else if (persisted.wasUpdated) {
|
|
15601
|
+
updatedCount += 1;
|
|
15602
|
+
}
|
|
15603
|
+
}
|
|
15604
|
+
let removedCount = 0;
|
|
15605
|
+
for (const sourceId of previousSourceIds) {
|
|
15606
|
+
if (currentSourceIds.includes(sourceId)) {
|
|
15607
|
+
continue;
|
|
15608
|
+
}
|
|
15609
|
+
if (await removeManifestBySourceId(rootDir, sourceId)) {
|
|
15610
|
+
removedCount += 1;
|
|
15611
|
+
}
|
|
15612
|
+
}
|
|
15613
|
+
return {
|
|
15614
|
+
title: crawl.title,
|
|
15615
|
+
sourceIds: uniqueStrings4(currentSourceIds).sort((left, right) => left.localeCompare(right)),
|
|
15616
|
+
counts: {
|
|
15617
|
+
scannedCount: crawl.pages.length,
|
|
15618
|
+
importedCount,
|
|
15619
|
+
updatedCount,
|
|
15620
|
+
removedCount,
|
|
15621
|
+
skippedCount: 0
|
|
15622
|
+
},
|
|
15623
|
+
changed: importedCount + updatedCount + removedCount > 0
|
|
15624
|
+
};
|
|
15625
|
+
}
|
|
15626
|
+
async function syncManagedSource(rootDir, entry, options) {
|
|
15627
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15628
|
+
try {
|
|
15629
|
+
let sync;
|
|
15630
|
+
if (entry.kind === "directory") {
|
|
15631
|
+
if (!entry.path || !entry.repoRoot) {
|
|
15632
|
+
throw new Error(`Managed source ${entry.id} is missing its directory path.`);
|
|
15633
|
+
}
|
|
15634
|
+
if (!await fileExists(entry.path)) {
|
|
15635
|
+
return {
|
|
15636
|
+
...entry,
|
|
15637
|
+
status: "missing",
|
|
15638
|
+
updatedAt: now,
|
|
15639
|
+
lastSyncAt: now,
|
|
15640
|
+
lastSyncStatus: "error",
|
|
15641
|
+
lastError: `Directory not found: ${entry.path}`,
|
|
15642
|
+
changed: false
|
|
15643
|
+
};
|
|
15644
|
+
}
|
|
15645
|
+
sync = await syncDirectorySource(rootDir, entry.path, entry.repoRoot);
|
|
15646
|
+
} else if (entry.kind === "github_repo") {
|
|
15647
|
+
sync = await syncGitHubRepoSource(rootDir, entry);
|
|
15648
|
+
} else {
|
|
15649
|
+
sync = await syncCrawlSource(rootDir, entry, options);
|
|
15650
|
+
}
|
|
15651
|
+
return {
|
|
15652
|
+
...entry,
|
|
15653
|
+
title: sync.title || entry.title,
|
|
15654
|
+
sourceIds: sync.sourceIds,
|
|
15655
|
+
status: "ready",
|
|
15656
|
+
updatedAt: now,
|
|
15657
|
+
lastSyncAt: now,
|
|
15658
|
+
lastSyncStatus: "success",
|
|
15659
|
+
lastSyncCounts: sync.counts,
|
|
15660
|
+
lastError: void 0,
|
|
15661
|
+
changed: sync.changed
|
|
15662
|
+
};
|
|
15663
|
+
} catch (error) {
|
|
15664
|
+
return {
|
|
15665
|
+
...entry,
|
|
15666
|
+
status: normalizeManagedStatus(entry.status),
|
|
15667
|
+
updatedAt: now,
|
|
15668
|
+
lastSyncAt: now,
|
|
15669
|
+
lastSyncStatus: "error",
|
|
15670
|
+
lastError: error instanceof Error ? error.message : String(error),
|
|
15671
|
+
changed: false
|
|
15672
|
+
};
|
|
15673
|
+
}
|
|
15674
|
+
}
|
|
15675
|
+
function scopedSourcePages(graph, sourceIds) {
|
|
15676
|
+
const scopedSet = new Set(sourceIds);
|
|
15677
|
+
return graph.pages.filter((page) => page.sourceIds.some((sourceId) => scopedSet.has(sourceId)));
|
|
15678
|
+
}
|
|
15679
|
+
function scopedNodeIds(graph, sourceIds) {
|
|
15680
|
+
const scopedSet = new Set(sourceIds);
|
|
15681
|
+
return graph.nodes.filter((node) => node.sourceIds.some((sourceId) => scopedSet.has(sourceId))).map((node) => node.id);
|
|
15682
|
+
}
|
|
15683
|
+
async function loadSourceAnalyses(rootDir, sourceIds) {
|
|
15684
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
15685
|
+
const analyses = await Promise.all(
|
|
15686
|
+
sourceIds.map(async (sourceId) => await readJsonFile(path25.join(paths.analysesDir, `${sourceId}.json`)))
|
|
15687
|
+
);
|
|
15688
|
+
return analyses.filter((analysis) => Boolean(analysis?.sourceId));
|
|
15689
|
+
}
|
|
15690
|
+
function renderDeterministicSourceBrief(input) {
|
|
15691
|
+
const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 6);
|
|
15692
|
+
const sourcePages = input.sourcePages.filter((page) => page.kind === "source").slice(0, 6);
|
|
15693
|
+
const conceptPages = input.sourcePages.filter((page) => page.kind === "concept").slice(0, 6);
|
|
15694
|
+
const entityPages = input.sourcePages.filter((page) => page.kind === "entity").slice(0, 6);
|
|
15695
|
+
const questions = uniqueStrings4(input.analyses.flatMap((analysis) => analysis.questions)).slice(0, 5);
|
|
15696
|
+
const summary = truncate(
|
|
15697
|
+
normalizeWhitespace(
|
|
15698
|
+
uniqueStrings4(input.analyses.map((analysis) => analysis.summary).filter(Boolean)).join(" ") || `${input.source.title} has been compiled into a local source graph.`
|
|
15699
|
+
),
|
|
15700
|
+
320
|
|
15701
|
+
);
|
|
15702
|
+
const scopedNodeIdSet = new Set(scopedNodeIds(input.graph, input.source.sourceIds));
|
|
15703
|
+
const surprises = input.report?.surprisingConnections.filter((connection) => scopedNodeIdSet.has(connection.sourceNodeId) || scopedNodeIdSet.has(connection.targetNodeId)).slice(0, 4) ?? [];
|
|
15704
|
+
const contradictions = input.report?.contradictions.filter(
|
|
15705
|
+
(contradiction) => input.source.sourceIds.includes(contradiction.sourceIdA) || input.source.sourceIds.includes(contradiction.sourceIdB)
|
|
15706
|
+
) ?? [];
|
|
15707
|
+
return [
|
|
15708
|
+
`# Source Brief: ${input.source.title}`,
|
|
15709
|
+
"",
|
|
15710
|
+
"## What This Source Is",
|
|
15711
|
+
"",
|
|
15712
|
+
summary,
|
|
15713
|
+
"",
|
|
15714
|
+
"## Read First",
|
|
15715
|
+
"",
|
|
15716
|
+
...sourcePages.length ? sourcePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No source page links are available yet."],
|
|
15717
|
+
"",
|
|
15718
|
+
"## Core Pages",
|
|
15719
|
+
"",
|
|
15720
|
+
...modulePages.length ? modulePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No module pages are available yet."],
|
|
15721
|
+
...conceptPages.length ? ["", "Concept pages:", ...conceptPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
|
|
15722
|
+
...entityPages.length ? ["", "Entity pages:", ...entityPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
|
|
15723
|
+
"",
|
|
15724
|
+
"## How The Important Parts Fit Together",
|
|
15725
|
+
"",
|
|
15726
|
+
`- Compiled source pages: ${sourcePages.length}`,
|
|
15727
|
+
`- Module pages: ${modulePages.length}`,
|
|
15728
|
+
`- Graph nodes touching this source: ${scopedNodeIdSet.size}`,
|
|
15729
|
+
`- Current tracked source ids: ${input.source.sourceIds.length}`,
|
|
15730
|
+
"",
|
|
15731
|
+
"## Surprises",
|
|
15732
|
+
"",
|
|
15733
|
+
...surprises.length ? surprises.map((surprise) => `- ${surprise.explanation}`) : ["- No surprising cross-source connections were highlighted for this source yet."],
|
|
15734
|
+
"",
|
|
15735
|
+
"## Contradictions",
|
|
15736
|
+
"",
|
|
15737
|
+
...contradictions.length ? contradictions.map(
|
|
15738
|
+
(contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB} (sources: ${contradiction.sourceIdA}, ${contradiction.sourceIdB})`
|
|
15739
|
+
) : ["- No contradictions were detected for this source."],
|
|
15740
|
+
"",
|
|
15741
|
+
"## Open Questions",
|
|
15742
|
+
"",
|
|
15743
|
+
...questions.length ? questions.map((question) => `- ${question}`) : ["- No extracted open questions yet."],
|
|
15744
|
+
"",
|
|
15745
|
+
"## Suggested Next Questions",
|
|
15746
|
+
"",
|
|
15747
|
+
...(input.report?.suggestedQuestions ?? []).slice(0, 5).map((question) => `- ${question}`) || [
|
|
15748
|
+
"- Ask `swarmvault query` about the main modules or sections in this source."
|
|
15749
|
+
],
|
|
15750
|
+
""
|
|
15751
|
+
].join("\n");
|
|
15752
|
+
}
|
|
15753
|
+
async function generateSourceBriefMarkdown(rootDir, source) {
|
|
15754
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
15755
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
15756
|
+
if (!graph) {
|
|
15757
|
+
return null;
|
|
15758
|
+
}
|
|
15759
|
+
const sourcePages = scopedSourcePages(graph, source.sourceIds);
|
|
15760
|
+
const analyses = await loadSourceAnalyses(rootDir, source.sourceIds);
|
|
15761
|
+
const report = await readGraphReport(rootDir);
|
|
15762
|
+
const fallback = renderDeterministicSourceBrief({
|
|
15763
|
+
source,
|
|
15764
|
+
sourcePages,
|
|
15765
|
+
graph,
|
|
15766
|
+
analyses,
|
|
15767
|
+
report
|
|
15768
|
+
});
|
|
15769
|
+
const provider = await getProviderForTask(rootDir, "queryProvider");
|
|
15770
|
+
if (provider.type === "heuristic") {
|
|
15771
|
+
return fallback;
|
|
15772
|
+
}
|
|
15773
|
+
try {
|
|
15774
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
15775
|
+
const pageContext = sourcePages.slice(0, 10).map((page) => `- ${page.title} (${page.kind}) -> ${page.path}`).join("\n");
|
|
15776
|
+
const analysisContext = analyses.slice(0, 6).map(
|
|
15777
|
+
(analysis) => `# ${analysis.title}
|
|
15778
|
+
Summary: ${analysis.summary}
|
|
15779
|
+
Questions: ${analysis.questions.join(" | ") || "none"}
|
|
15780
|
+
Concepts: ${analysis.concepts.map((concept) => concept.name).join(", ") || "none"}
|
|
15781
|
+
Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}`
|
|
15782
|
+
).join("\n\n---\n\n");
|
|
15783
|
+
const response = await provider.generateText({
|
|
15784
|
+
system: buildSchemaPrompt(
|
|
15785
|
+
schemas.effective.global,
|
|
15786
|
+
"Write a concise markdown source brief with sections: What This Source Is, Read First, Core Pages, How The Important Parts Fit Together, Surprises, Contradictions, Open Questions, Suggested Next Questions. Ground every claim in the provided context."
|
|
15787
|
+
),
|
|
15788
|
+
prompt: [
|
|
15789
|
+
`Source title: ${source.title}`,
|
|
15790
|
+
`Source kind: ${source.kind}`,
|
|
15791
|
+
`Tracked source ids: ${source.sourceIds.join(", ") || "none"}`,
|
|
15792
|
+
"",
|
|
15793
|
+
"Pages:",
|
|
15794
|
+
pageContext || "- none",
|
|
15795
|
+
"",
|
|
15796
|
+
"Analyses:",
|
|
15797
|
+
analysisContext || "No analysis context available.",
|
|
15798
|
+
"",
|
|
15799
|
+
"Deterministic fallback draft:",
|
|
15800
|
+
fallback
|
|
15801
|
+
].join("\n")
|
|
15802
|
+
});
|
|
15803
|
+
return response.text?.trim() ? response.text.trim() : fallback;
|
|
15804
|
+
} catch {
|
|
15805
|
+
return fallback;
|
|
15806
|
+
}
|
|
15807
|
+
}
|
|
15808
|
+
async function writeSourceBrief(rootDir, source) {
|
|
15809
|
+
if (!source.sourceIds.length) {
|
|
15810
|
+
return null;
|
|
15811
|
+
}
|
|
15812
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
15813
|
+
const markdown = await generateSourceBriefMarkdown(rootDir, source);
|
|
15814
|
+
if (!markdown) {
|
|
15815
|
+
return null;
|
|
15816
|
+
}
|
|
15817
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
15818
|
+
const relatedPages = graph ? scopedSourcePages(graph, source.sourceIds) : [];
|
|
15819
|
+
const relatedPageIds = relatedPages.slice(0, 12).map((page) => page.id);
|
|
15820
|
+
const relatedNodeIds = graph ? scopedNodeIds(graph, source.sourceIds).slice(0, 20) : [];
|
|
15821
|
+
const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
|
|
15822
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15823
|
+
const output = buildOutputPage({
|
|
15824
|
+
title: `Source Brief: ${source.title}`,
|
|
15825
|
+
question: `Brief ${source.title}`,
|
|
15826
|
+
answer: markdown,
|
|
15827
|
+
citations: source.sourceIds,
|
|
15828
|
+
schemaHash: graph?.generatedAt ?? "",
|
|
15829
|
+
outputFormat: "report",
|
|
15830
|
+
relatedPageIds,
|
|
15831
|
+
relatedNodeIds,
|
|
15832
|
+
relatedSourceIds: source.sourceIds,
|
|
15833
|
+
projectIds,
|
|
15834
|
+
extraTags: ["source-brief"],
|
|
15835
|
+
origin: "query",
|
|
15836
|
+
slug: `source-briefs/${source.id}`,
|
|
15837
|
+
metadata: {
|
|
15838
|
+
status: "active",
|
|
15839
|
+
createdAt: now,
|
|
15840
|
+
updatedAt: now,
|
|
15841
|
+
compiledFrom: source.sourceIds,
|
|
15842
|
+
managedBy: "system",
|
|
15843
|
+
confidence: 0.82
|
|
15844
|
+
}
|
|
15845
|
+
});
|
|
15846
|
+
const absolutePath = path25.join(paths.wikiDir, output.page.path);
|
|
15847
|
+
await ensureDir(path25.dirname(absolutePath));
|
|
15848
|
+
await fs21.writeFile(absolutePath, output.content, "utf8");
|
|
15849
|
+
return absolutePath;
|
|
15850
|
+
}
|
|
15851
|
+
async function generateBriefsForSources(rootDir, sources) {
|
|
15852
|
+
const briefPaths = /* @__PURE__ */ new Map();
|
|
15853
|
+
for (const source of sources) {
|
|
15854
|
+
const briefPath = await writeSourceBrief(rootDir, source);
|
|
15855
|
+
if (briefPath) {
|
|
15856
|
+
briefPaths.set(source.id, briefPath);
|
|
15857
|
+
}
|
|
15858
|
+
}
|
|
15859
|
+
if (briefPaths.size > 0) {
|
|
15860
|
+
await refreshVaultAfterOutputSave(rootDir);
|
|
15861
|
+
}
|
|
15862
|
+
return briefPaths;
|
|
15863
|
+
}
|
|
15864
|
+
function shouldCompile(changedSources, graphExists, compileRequested) {
|
|
15865
|
+
return compileRequested && (!graphExists || changedSources.length > 0);
|
|
15866
|
+
}
|
|
15867
|
+
async function listManagedSourceRecords(rootDir) {
|
|
15868
|
+
await ensureManagedSourcesArtifact(rootDir);
|
|
15869
|
+
return await loadManagedSources(rootDir);
|
|
15870
|
+
}
|
|
15871
|
+
async function addManagedSource(rootDir, input, options = {}) {
|
|
15872
|
+
const compileRequested = options.compile ?? true;
|
|
15873
|
+
const briefRequested = options.brief ?? true;
|
|
15874
|
+
const sources = await loadManagedSources(rootDir);
|
|
15875
|
+
const resolved = await resolveManagedSourceInput(rootDir, input);
|
|
15876
|
+
const existing = sources.find((candidate) => matchesManagedSourceSpec(candidate, resolved));
|
|
15877
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15878
|
+
const source = existing ?? {
|
|
15879
|
+
id: resolved.kind === "directory" ? stableManagedSourceId("directory", path25.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
|
|
15880
|
+
kind: resolved.kind,
|
|
15881
|
+
title: resolved.title,
|
|
15882
|
+
path: resolved.kind === "directory" ? resolved.path : void 0,
|
|
15883
|
+
repoRoot: resolved.kind === "directory" ? resolved.repoRoot : void 0,
|
|
15884
|
+
url: resolved.kind === "directory" ? void 0 : resolved.url,
|
|
15885
|
+
createdAt: now,
|
|
15886
|
+
updatedAt: now,
|
|
15887
|
+
status: "ready",
|
|
15888
|
+
sourceIds: []
|
|
15889
|
+
};
|
|
15890
|
+
const synced = await syncManagedSource(rootDir, source, options);
|
|
15891
|
+
if (synced.lastSyncStatus === "error") {
|
|
15892
|
+
throw new Error(synced.lastError ?? `Failed to add managed source ${synced.id}.`);
|
|
15893
|
+
}
|
|
15894
|
+
const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
|
|
15895
|
+
let compile;
|
|
15896
|
+
if (shouldCompile([synced], graphExists, compileRequested)) {
|
|
15897
|
+
compile = await compileVault(rootDir, {});
|
|
15898
|
+
}
|
|
15899
|
+
let briefGenerated = false;
|
|
15900
|
+
let briefPath;
|
|
15901
|
+
if (compileRequested && briefRequested && synced.status === "ready") {
|
|
15902
|
+
const briefs = await generateBriefsForSources(rootDir, [synced]);
|
|
15903
|
+
briefPath = briefs.get(synced.id);
|
|
15904
|
+
briefGenerated = Boolean(briefPath);
|
|
15905
|
+
}
|
|
15906
|
+
const nextSource = {
|
|
15907
|
+
...synced,
|
|
15908
|
+
briefPath: briefPath ?? synced.briefPath,
|
|
15909
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15910
|
+
};
|
|
15911
|
+
const nextSources = existing ? sources.map((candidate) => candidate.id === nextSource.id ? nextSource : candidate) : [...sources, nextSource];
|
|
15912
|
+
await saveManagedSources(rootDir, nextSources);
|
|
15913
|
+
return {
|
|
15914
|
+
source: nextSource,
|
|
15915
|
+
compile,
|
|
15916
|
+
briefGenerated
|
|
15917
|
+
};
|
|
15918
|
+
}
|
|
15919
|
+
async function reloadManagedSources(rootDir, options = {}) {
|
|
15920
|
+
const compileRequested = options.compile ?? true;
|
|
15921
|
+
const briefRequested = options.brief ?? true;
|
|
15922
|
+
const sources = await loadManagedSources(rootDir);
|
|
15923
|
+
const selected = options.all || !options.id ? sources : sources.filter((source) => source.id === options.id);
|
|
15924
|
+
if (!selected.length) {
|
|
15925
|
+
throw new Error(options.id ? `Managed source not found: ${options.id}` : "No managed sources registered.");
|
|
15926
|
+
}
|
|
15927
|
+
const syncedSources = [];
|
|
15928
|
+
const changedSources = [];
|
|
15929
|
+
for (const source of selected) {
|
|
15930
|
+
const synced = await syncManagedSource(rootDir, source, options);
|
|
15931
|
+
syncedSources.push(synced);
|
|
15932
|
+
if (synced.changed) {
|
|
15933
|
+
changedSources.push(synced);
|
|
15934
|
+
}
|
|
15935
|
+
}
|
|
15936
|
+
const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
|
|
15937
|
+
let compile;
|
|
15938
|
+
if (shouldCompile(changedSources, graphExists, compileRequested)) {
|
|
15939
|
+
compile = await compileVault(rootDir, {});
|
|
15940
|
+
}
|
|
15941
|
+
const briefPaths = compileRequested && briefRequested ? await generateBriefsForSources(
|
|
15942
|
+
rootDir,
|
|
15943
|
+
syncedSources.filter((source) => source.status === "ready")
|
|
15944
|
+
) : /* @__PURE__ */ new Map();
|
|
15945
|
+
const nextSources = sources.map((source) => {
|
|
15946
|
+
const synced = syncedSources.find((candidate) => candidate.id === source.id);
|
|
15947
|
+
if (!synced) {
|
|
15948
|
+
return source;
|
|
15949
|
+
}
|
|
15950
|
+
return {
|
|
15951
|
+
...synced,
|
|
15952
|
+
briefPath: briefPaths.get(synced.id) ?? synced.briefPath,
|
|
15953
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15954
|
+
};
|
|
15955
|
+
});
|
|
15956
|
+
await saveManagedSources(rootDir, nextSources);
|
|
15957
|
+
return {
|
|
15958
|
+
sources: nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)),
|
|
15959
|
+
compile,
|
|
15960
|
+
briefPaths: [...briefPaths.values()]
|
|
15961
|
+
};
|
|
15962
|
+
}
|
|
15963
|
+
async function deleteManagedSource(rootDir, id) {
|
|
15964
|
+
const sources = await loadManagedSources(rootDir);
|
|
15965
|
+
const target = sources.find((source) => source.id === id);
|
|
15966
|
+
if (!target) {
|
|
15967
|
+
throw new Error(`Managed source not found: ${id}`);
|
|
15968
|
+
}
|
|
15969
|
+
await saveManagedSources(
|
|
15970
|
+
rootDir,
|
|
15971
|
+
sources.filter((source) => source.id !== id)
|
|
15972
|
+
);
|
|
15973
|
+
const workingDir = await managedSourceWorkingDir(rootDir, id);
|
|
15974
|
+
await fs21.rm(workingDir, { recursive: true, force: true });
|
|
15975
|
+
return { removed: target };
|
|
15976
|
+
}
|
|
15977
|
+
|
|
15114
15978
|
// src/viewer.ts
|
|
15115
15979
|
import { execFile } from "child_process";
|
|
15116
|
-
import
|
|
15980
|
+
import fs22 from "fs/promises";
|
|
15117
15981
|
import http from "http";
|
|
15118
|
-
import
|
|
15982
|
+
import path27 from "path";
|
|
15119
15983
|
import { promisify } from "util";
|
|
15120
15984
|
import matter10 from "gray-matter";
|
|
15121
15985
|
import mime2 from "mime-types";
|
|
15122
15986
|
|
|
15123
15987
|
// src/watch.ts
|
|
15124
|
-
import
|
|
15988
|
+
import path26 from "path";
|
|
15125
15989
|
import process3 from "process";
|
|
15126
15990
|
import chokidar from "chokidar";
|
|
15127
15991
|
var MAX_BACKOFF_MS = 3e4;
|
|
15128
15992
|
var BACKOFF_THRESHOLD = 3;
|
|
15129
15993
|
var CRITICAL_THRESHOLD = 10;
|
|
15130
15994
|
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
15131
|
-
function
|
|
15132
|
-
const relative =
|
|
15133
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
15995
|
+
function withinRoot3(rootPath, targetPath) {
|
|
15996
|
+
const relative = path26.relative(rootPath, targetPath);
|
|
15997
|
+
return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
|
|
15134
15998
|
}
|
|
15135
15999
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
15136
|
-
const relativePath =
|
|
16000
|
+
const relativePath = path26.relative(baseDir, targetPath);
|
|
15137
16001
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
15138
16002
|
return false;
|
|
15139
16003
|
}
|
|
15140
|
-
return relativePath.split(
|
|
16004
|
+
return relativePath.split(path26.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
|
|
15141
16005
|
}
|
|
15142
16006
|
function workspaceIgnoreRoots(rootDir, paths) {
|
|
15143
16007
|
return [
|
|
@@ -15146,16 +16010,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
|
|
|
15146
16010
|
paths.stateDir,
|
|
15147
16011
|
paths.agentDir,
|
|
15148
16012
|
paths.inboxDir,
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
].map((candidate) =>
|
|
16013
|
+
path26.join(rootDir, ".claude"),
|
|
16014
|
+
path26.join(rootDir, ".cursor"),
|
|
16015
|
+
path26.join(rootDir, ".obsidian")
|
|
16016
|
+
].map((candidate) => path26.resolve(candidate));
|
|
15153
16017
|
}
|
|
15154
16018
|
async function resolveWatchTargets(rootDir, paths, options) {
|
|
15155
|
-
const targets = /* @__PURE__ */ new Set([
|
|
16019
|
+
const targets = /* @__PURE__ */ new Set([path26.resolve(paths.inboxDir)]);
|
|
15156
16020
|
if (options.repo) {
|
|
15157
16021
|
for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
|
|
15158
|
-
targets.add(
|
|
16022
|
+
targets.add(path26.resolve(repoRoot));
|
|
15159
16023
|
}
|
|
15160
16024
|
}
|
|
15161
16025
|
return [...targets].sort((left, right) => left.localeCompare(right));
|
|
@@ -15285,7 +16149,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
15285
16149
|
const { paths } = await initWorkspace(rootDir);
|
|
15286
16150
|
const baseDebounceMs = options.debounceMs ?? 900;
|
|
15287
16151
|
const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
|
|
15288
|
-
const inboxWatchRoot =
|
|
16152
|
+
const inboxWatchRoot = path26.resolve(paths.inboxDir);
|
|
15289
16153
|
let watchTargets = await resolveWatchTargets(rootDir, paths, options);
|
|
15290
16154
|
let timer;
|
|
15291
16155
|
let running = false;
|
|
@@ -15300,12 +16164,12 @@ async function watchVault(rootDir, options = {}) {
|
|
|
15300
16164
|
usePolling: true,
|
|
15301
16165
|
interval: 100,
|
|
15302
16166
|
ignored: (targetPath) => {
|
|
15303
|
-
const absolutePath =
|
|
15304
|
-
const primaryTarget = watchTargets.filter((watchTarget) =>
|
|
16167
|
+
const absolutePath = path26.resolve(targetPath);
|
|
16168
|
+
const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
15305
16169
|
if (!primaryTarget) {
|
|
15306
16170
|
return false;
|
|
15307
16171
|
}
|
|
15308
|
-
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) =>
|
|
16172
|
+
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot3(ignoreRoot, absolutePath))) {
|
|
15309
16173
|
return true;
|
|
15310
16174
|
}
|
|
15311
16175
|
return hasIgnoredRepoSegment(primaryTarget, absolutePath);
|
|
@@ -15489,8 +16353,8 @@ async function watchVault(rootDir, options = {}) {
|
|
|
15489
16353
|
}
|
|
15490
16354
|
};
|
|
15491
16355
|
const reasonForPath = (targetPath) => {
|
|
15492
|
-
const baseDir = watchTargets.filter((watchTarget) =>
|
|
15493
|
-
return
|
|
16356
|
+
const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path26.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
|
|
16357
|
+
return path26.relative(baseDir, targetPath) || ".";
|
|
15494
16358
|
};
|
|
15495
16359
|
watcher.on("add", (filePath) => schedule(`add:${reasonForPath(filePath)}`)).on("change", (filePath) => schedule(`change:${reasonForPath(filePath)}`)).on("unlink", (filePath) => schedule(`unlink:${reasonForPath(filePath)}`)).on("addDir", (dirPath) => schedule(`addDir:${reasonForPath(dirPath)}`)).on("unlinkDir", (dirPath) => schedule(`unlinkDir:${reasonForPath(dirPath)}`)).on("error", (caught) => schedule(`error:${caught instanceof Error ? caught.message : String(caught)}`));
|
|
15496
16360
|
await new Promise((resolve, reject) => {
|
|
@@ -15532,15 +16396,15 @@ async function getWatchStatus(rootDir) {
|
|
|
15532
16396
|
var execFileAsync = promisify(execFile);
|
|
15533
16397
|
async function readViewerPage(rootDir, relativePath) {
|
|
15534
16398
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15535
|
-
const absolutePath =
|
|
16399
|
+
const absolutePath = path27.resolve(paths.wikiDir, relativePath);
|
|
15536
16400
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
15537
16401
|
return null;
|
|
15538
16402
|
}
|
|
15539
|
-
const raw = await
|
|
16403
|
+
const raw = await fs22.readFile(absolutePath, "utf8");
|
|
15540
16404
|
const parsed = matter10(raw);
|
|
15541
16405
|
return {
|
|
15542
16406
|
path: relativePath,
|
|
15543
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
16407
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path27.basename(relativePath, path27.extname(relativePath)),
|
|
15544
16408
|
frontmatter: parsed.data,
|
|
15545
16409
|
content: parsed.content,
|
|
15546
16410
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -15548,12 +16412,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
15548
16412
|
}
|
|
15549
16413
|
async function readViewerAsset(rootDir, relativePath) {
|
|
15550
16414
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15551
|
-
const absolutePath =
|
|
16415
|
+
const absolutePath = path27.resolve(paths.wikiDir, relativePath);
|
|
15552
16416
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
15553
16417
|
return null;
|
|
15554
16418
|
}
|
|
15555
16419
|
return {
|
|
15556
|
-
buffer: await
|
|
16420
|
+
buffer: await fs22.readFile(absolutePath),
|
|
15557
16421
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
15558
16422
|
};
|
|
15559
16423
|
}
|
|
@@ -15576,12 +16440,12 @@ async function readJsonBody(request) {
|
|
|
15576
16440
|
return JSON.parse(raw);
|
|
15577
16441
|
}
|
|
15578
16442
|
async function ensureViewerDist(viewerDistDir) {
|
|
15579
|
-
const indexPath =
|
|
16443
|
+
const indexPath = path27.join(viewerDistDir, "index.html");
|
|
15580
16444
|
if (await fileExists(indexPath)) {
|
|
15581
16445
|
return;
|
|
15582
16446
|
}
|
|
15583
|
-
const viewerProjectDir =
|
|
15584
|
-
if (await fileExists(
|
|
16447
|
+
const viewerProjectDir = path27.dirname(viewerDistDir);
|
|
16448
|
+
if (await fileExists(path27.join(viewerProjectDir, "package.json"))) {
|
|
15585
16449
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
15586
16450
|
}
|
|
15587
16451
|
}
|
|
@@ -15598,7 +16462,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
15598
16462
|
return;
|
|
15599
16463
|
}
|
|
15600
16464
|
response.writeHead(200, { "content-type": "application/json" });
|
|
15601
|
-
response.end(await
|
|
16465
|
+
response.end(await fs22.readFile(paths.graphPath, "utf8"));
|
|
15602
16466
|
return;
|
|
15603
16467
|
}
|
|
15604
16468
|
if (url.pathname === "/api/graph/query") {
|
|
@@ -15655,14 +16519,14 @@ async function startGraphServer(rootDir, port) {
|
|
|
15655
16519
|
return;
|
|
15656
16520
|
}
|
|
15657
16521
|
if (url.pathname === "/api/graph-report") {
|
|
15658
|
-
const reportPath =
|
|
16522
|
+
const reportPath = path27.join(paths.wikiDir, "graph", "report.json");
|
|
15659
16523
|
if (!await fileExists(reportPath)) {
|
|
15660
16524
|
response.writeHead(404, { "content-type": "application/json" });
|
|
15661
16525
|
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
15662
16526
|
return;
|
|
15663
16527
|
}
|
|
15664
16528
|
response.writeHead(200, { "content-type": "application/json" });
|
|
15665
|
-
response.end(await
|
|
16529
|
+
response.end(await fs22.readFile(reportPath, "utf8"));
|
|
15666
16530
|
return;
|
|
15667
16531
|
}
|
|
15668
16532
|
if (url.pathname === "/api/watch-status") {
|
|
@@ -15745,8 +16609,8 @@ async function startGraphServer(rootDir, port) {
|
|
|
15745
16609
|
return;
|
|
15746
16610
|
}
|
|
15747
16611
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
15748
|
-
const target =
|
|
15749
|
-
const fallback =
|
|
16612
|
+
const target = path27.join(paths.viewerDistDir, relativePath);
|
|
16613
|
+
const fallback = path27.join(paths.viewerDistDir, "index.html");
|
|
15750
16614
|
const filePath = await fileExists(target) ? target : fallback;
|
|
15751
16615
|
if (!await fileExists(filePath)) {
|
|
15752
16616
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -15754,7 +16618,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
15754
16618
|
return;
|
|
15755
16619
|
}
|
|
15756
16620
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
15757
|
-
response.end(await
|
|
16621
|
+
response.end(await fs22.readFile(filePath));
|
|
15758
16622
|
});
|
|
15759
16623
|
await new Promise((resolve) => {
|
|
15760
16624
|
server.listen(effectivePort, resolve);
|
|
@@ -15781,7 +16645,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
15781
16645
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
15782
16646
|
}
|
|
15783
16647
|
await ensureViewerDist(paths.viewerDistDir);
|
|
15784
|
-
const indexPath =
|
|
16648
|
+
const indexPath = path27.join(paths.viewerDistDir, "index.html");
|
|
15785
16649
|
if (!await fileExists(indexPath)) {
|
|
15786
16650
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
15787
16651
|
}
|
|
@@ -15807,17 +16671,17 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
15807
16671
|
} : null;
|
|
15808
16672
|
})
|
|
15809
16673
|
);
|
|
15810
|
-
const rawHtml = await
|
|
16674
|
+
const rawHtml = await fs22.readFile(indexPath, "utf8");
|
|
15811
16675
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
15812
16676
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
15813
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
15814
|
-
const stylePath = styleMatch?.[1] ?
|
|
16677
|
+
const scriptPath = scriptMatch?.[1] ? path27.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
|
|
16678
|
+
const stylePath = styleMatch?.[1] ? path27.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
|
|
15815
16679
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
15816
16680
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
15817
16681
|
}
|
|
15818
|
-
const script = await
|
|
15819
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
15820
|
-
const report = await readJsonFile(
|
|
16682
|
+
const script = await fs22.readFile(scriptPath, "utf8");
|
|
16683
|
+
const style = stylePath && await fileExists(stylePath) ? await fs22.readFile(stylePath, "utf8") : "";
|
|
16684
|
+
const report = await readJsonFile(path27.join(paths.wikiDir, "graph", "report.json"));
|
|
15821
16685
|
const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean), report }, null, 2).replace(/</g, "\\u003c");
|
|
15822
16686
|
const html = [
|
|
15823
16687
|
"<!doctype html>",
|
|
@@ -15836,13 +16700,14 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
15836
16700
|
"</html>",
|
|
15837
16701
|
""
|
|
15838
16702
|
].filter(Boolean).join("\n");
|
|
15839
|
-
await
|
|
15840
|
-
await
|
|
15841
|
-
return
|
|
16703
|
+
await fs22.mkdir(path27.dirname(outputPath), { recursive: true });
|
|
16704
|
+
await fs22.writeFile(outputPath, html, "utf8");
|
|
16705
|
+
return path27.resolve(outputPath);
|
|
15842
16706
|
}
|
|
15843
16707
|
export {
|
|
15844
16708
|
acceptApproval,
|
|
15845
16709
|
addInput,
|
|
16710
|
+
addManagedSource,
|
|
15846
16711
|
archiveCandidate,
|
|
15847
16712
|
assertProviderCapability,
|
|
15848
16713
|
benchmarkVault,
|
|
@@ -15853,6 +16718,7 @@ export {
|
|
|
15853
16718
|
createWebSearchAdapter,
|
|
15854
16719
|
defaultVaultConfig,
|
|
15855
16720
|
defaultVaultSchema,
|
|
16721
|
+
deleteManagedSource,
|
|
15856
16722
|
explainGraphVault,
|
|
15857
16723
|
exploreVault,
|
|
15858
16724
|
exportGraphFormat,
|
|
@@ -15875,6 +16741,7 @@ export {
|
|
|
15875
16741
|
listCandidates,
|
|
15876
16742
|
listGodNodes,
|
|
15877
16743
|
listGraphHyperedges,
|
|
16744
|
+
listManagedSourceRecords,
|
|
15878
16745
|
listManifests,
|
|
15879
16746
|
listPages,
|
|
15880
16747
|
listSchedules,
|
|
@@ -15892,6 +16759,7 @@ export {
|
|
|
15892
16759
|
readGraphReport,
|
|
15893
16760
|
readPage,
|
|
15894
16761
|
rejectApproval,
|
|
16762
|
+
reloadManagedSources,
|
|
15895
16763
|
resolvePaths,
|
|
15896
16764
|
runSchedule,
|
|
15897
16765
|
runWatchCycle,
|