@swarmvaultai/engine 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/chunk-LEUV6TWJ.js +1131 -0
- package/dist/index.d.ts +41 -1
- package/dist/index.js +949 -409
- package/dist/registry-YGVTLIZH.js +12 -0
- package/dist/viewer/assets/index-4JhL4iIB.js +330 -0
- package/dist/viewer/index.html +1 -1
- package/dist/viewer/lib.d.ts +17 -0
- package/dist/viewer/lib.js +8 -4
- package/package.json +1 -1
- package/dist/viewer/assets/index-CmEm2Pd_.js +0 -330
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-LEUV6TWJ.js";
|
|
25
25
|
|
|
26
26
|
// src/agents.ts
|
|
27
27
|
import fs from "fs/promises";
|
|
@@ -662,7 +662,7 @@ async function uninstallGitHooks(rootDir) {
|
|
|
662
662
|
|
|
663
663
|
// src/ingest.ts
|
|
664
664
|
import fs9 from "fs/promises";
|
|
665
|
-
import
|
|
665
|
+
import path10 from "path";
|
|
666
666
|
import { Readability } from "@mozilla/readability";
|
|
667
667
|
import matter3 from "gray-matter";
|
|
668
668
|
import ignore from "ignore";
|
|
@@ -3143,9 +3143,68 @@ async function appendWatchRun(rootDir, run) {
|
|
|
3143
3143
|
await appendJsonLine(paths.jobsLogPath, run);
|
|
3144
3144
|
}
|
|
3145
3145
|
|
|
3146
|
+
// src/source-classification.ts
|
|
3147
|
+
import path8 from "path";
|
|
3148
|
+
var ALL_SOURCE_CLASSES = ["first_party", "third_party", "resource", "generated"];
|
|
3149
|
+
var THIRD_PARTY_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "vendor", "Pods"]);
|
|
3150
|
+
var GENERATED_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", ".next", "coverage", "DerivedData", "target"]);
|
|
3151
|
+
function matchesAnyGlob(relativePath, patterns) {
|
|
3152
|
+
return patterns.some(
|
|
3153
|
+
(pattern) => path8.matchesGlob(relativePath, pattern) || path8.matchesGlob(path8.posix.basename(relativePath), pattern)
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
function classifyRepoPath(relativePath, repoAnalysis) {
|
|
3157
|
+
const normalized = relativePath.replace(/\\/g, "/");
|
|
3158
|
+
const custom = repoAnalysis?.classifyGlobs;
|
|
3159
|
+
if (custom?.first_party?.length && matchesAnyGlob(normalized, custom.first_party)) {
|
|
3160
|
+
return "first_party";
|
|
3161
|
+
}
|
|
3162
|
+
for (const sourceClass of ["third_party", "resource", "generated"]) {
|
|
3163
|
+
const patterns = custom?.[sourceClass];
|
|
3164
|
+
if (patterns?.length && matchesAnyGlob(normalized, patterns)) {
|
|
3165
|
+
return sourceClass;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
3169
|
+
if (segments.some((segment) => THIRD_PARTY_SEGMENTS.has(segment))) {
|
|
3170
|
+
return "third_party";
|
|
3171
|
+
}
|
|
3172
|
+
if (segments.some((segment) => GENERATED_SEGMENTS.has(segment))) {
|
|
3173
|
+
return "generated";
|
|
3174
|
+
}
|
|
3175
|
+
if (segments.some((segment) => segment.endsWith(".xcassets") || segment.endsWith(".imageset"))) {
|
|
3176
|
+
return "resource";
|
|
3177
|
+
}
|
|
3178
|
+
return "first_party";
|
|
3179
|
+
}
|
|
3180
|
+
function normalizeExtractClasses(repoAnalysis, extra = []) {
|
|
3181
|
+
const configured = repoAnalysis?.extractClasses?.length ? repoAnalysis.extractClasses : ["first_party"];
|
|
3182
|
+
return ALL_SOURCE_CLASSES.filter((sourceClass) => (/* @__PURE__ */ new Set([...configured, ...extra])).has(sourceClass));
|
|
3183
|
+
}
|
|
3184
|
+
function aggregateSourceClass(values) {
|
|
3185
|
+
const available = ALL_SOURCE_CLASSES.filter((sourceClass) => values.includes(sourceClass));
|
|
3186
|
+
if (!available.length) {
|
|
3187
|
+
return void 0;
|
|
3188
|
+
}
|
|
3189
|
+
if (available.includes("first_party")) {
|
|
3190
|
+
return "first_party";
|
|
3191
|
+
}
|
|
3192
|
+
if (available.includes("resource")) {
|
|
3193
|
+
return "resource";
|
|
3194
|
+
}
|
|
3195
|
+
if (available.includes("third_party")) {
|
|
3196
|
+
return "third_party";
|
|
3197
|
+
}
|
|
3198
|
+
return "generated";
|
|
3199
|
+
}
|
|
3200
|
+
function aggregateManifestSourceClass(manifests, sourceIds) {
|
|
3201
|
+
const byId = new Map(manifests.map((manifest) => [manifest.sourceId, manifest.sourceClass]));
|
|
3202
|
+
return aggregateSourceClass(sourceIds.map((sourceId) => byId.get(sourceId)));
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3146
3205
|
// src/watch-state.ts
|
|
3147
3206
|
import fs8 from "fs/promises";
|
|
3148
|
-
import
|
|
3207
|
+
import path9 from "path";
|
|
3149
3208
|
import matter2 from "gray-matter";
|
|
3150
3209
|
function pendingEntryKey(entry) {
|
|
3151
3210
|
return entry.path;
|
|
@@ -3159,7 +3218,7 @@ function normalizeRelativePath(rootDir, filePath) {
|
|
|
3159
3218
|
if (!filePath) {
|
|
3160
3219
|
return void 0;
|
|
3161
3220
|
}
|
|
3162
|
-
return toPosix(
|
|
3221
|
+
return toPosix(path9.relative(rootDir, path9.resolve(filePath)));
|
|
3163
3222
|
}
|
|
3164
3223
|
async function readPendingSemanticRefresh(rootDir) {
|
|
3165
3224
|
const { paths } = await initWorkspace(rootDir);
|
|
@@ -3253,7 +3312,7 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
3253
3312
|
if (page.freshness !== "stale" || !page.sourceIds.some((sourceId) => affectedSourceIds.has(sourceId))) {
|
|
3254
3313
|
continue;
|
|
3255
3314
|
}
|
|
3256
|
-
const absolutePath =
|
|
3315
|
+
const absolutePath = path9.join(paths.wikiDir, page.path);
|
|
3257
3316
|
if (!await fileExists(absolutePath)) {
|
|
3258
3317
|
continue;
|
|
3259
3318
|
}
|
|
@@ -3272,7 +3331,7 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
3272
3331
|
// src/ingest.ts
|
|
3273
3332
|
var DEFAULT_MAX_ASSET_SIZE = 10 * 1024 * 1024;
|
|
3274
3333
|
var DEFAULT_MAX_DIRECTORY_FILES = 5e3;
|
|
3275
|
-
var
|
|
3334
|
+
var HARD_REPO_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
3276
3335
|
function uniqueStrings(values) {
|
|
3277
3336
|
return [...new Set(values.filter(Boolean))];
|
|
3278
3337
|
}
|
|
@@ -3308,36 +3367,47 @@ function normalizeIngestOptions(options) {
|
|
|
3308
3367
|
return {
|
|
3309
3368
|
includeAssets: options?.includeAssets ?? true,
|
|
3310
3369
|
maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
|
|
3311
|
-
repoRoot: options?.repoRoot ?
|
|
3370
|
+
repoRoot: options?.repoRoot ? path10.resolve(options.repoRoot) : void 0,
|
|
3312
3371
|
include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
3313
3372
|
exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
3314
3373
|
maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
|
|
3315
|
-
gitignore: options?.gitignore ?? true
|
|
3374
|
+
gitignore: options?.gitignore ?? true,
|
|
3375
|
+
extractClasses: options?.extractClasses ?? ["first_party"]
|
|
3316
3376
|
};
|
|
3317
3377
|
}
|
|
3318
|
-
function
|
|
3378
|
+
async function resolveRepoIngestOptions(rootDir, options) {
|
|
3379
|
+
const normalized = normalizeIngestOptions(options);
|
|
3380
|
+
const { config } = await loadVaultConfig(rootDir);
|
|
3381
|
+
const repoAnalysis = config.repoAnalysis;
|
|
3382
|
+
return {
|
|
3383
|
+
...normalized,
|
|
3384
|
+
extractClasses: normalizeExtractClasses(repoAnalysis, normalized.extractClasses),
|
|
3385
|
+
repoAnalysis
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
function matchesAnyGlob2(relativePath, patterns) {
|
|
3319
3389
|
return patterns.some(
|
|
3320
|
-
(pattern) =>
|
|
3390
|
+
(pattern) => path10.matchesGlob(relativePath, pattern) || path10.matchesGlob(path10.posix.basename(relativePath), pattern)
|
|
3321
3391
|
);
|
|
3322
3392
|
}
|
|
3323
3393
|
function supportedDirectoryKind(sourceKind) {
|
|
3324
3394
|
return sourceKind !== "binary";
|
|
3325
3395
|
}
|
|
3326
3396
|
async function findNearestGitRoot2(startPath) {
|
|
3327
|
-
let current =
|
|
3397
|
+
let current = path10.resolve(startPath);
|
|
3328
3398
|
try {
|
|
3329
3399
|
const stat = await fs9.stat(current);
|
|
3330
3400
|
if (!stat.isDirectory()) {
|
|
3331
|
-
current =
|
|
3401
|
+
current = path10.dirname(current);
|
|
3332
3402
|
}
|
|
3333
3403
|
} catch {
|
|
3334
|
-
current =
|
|
3404
|
+
current = path10.dirname(current);
|
|
3335
3405
|
}
|
|
3336
3406
|
while (true) {
|
|
3337
|
-
if (await fileExists(
|
|
3407
|
+
if (await fileExists(path10.join(current, ".git"))) {
|
|
3338
3408
|
return current;
|
|
3339
3409
|
}
|
|
3340
|
-
const parent =
|
|
3410
|
+
const parent = path10.dirname(current);
|
|
3341
3411
|
if (parent === current) {
|
|
3342
3412
|
return null;
|
|
3343
3413
|
}
|
|
@@ -3345,26 +3415,26 @@ async function findNearestGitRoot2(startPath) {
|
|
|
3345
3415
|
}
|
|
3346
3416
|
}
|
|
3347
3417
|
function withinRoot(rootPath, targetPath) {
|
|
3348
|
-
const relative =
|
|
3349
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3418
|
+
const relative = path10.relative(rootPath, targetPath);
|
|
3419
|
+
return relative === "" || !relative.startsWith("..") && !path10.isAbsolute(relative);
|
|
3350
3420
|
}
|
|
3351
3421
|
function repoRootFromManifest(manifest) {
|
|
3352
3422
|
if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
|
|
3353
3423
|
return null;
|
|
3354
3424
|
}
|
|
3355
|
-
const repoDir =
|
|
3356
|
-
const fileDir =
|
|
3425
|
+
const repoDir = path10.posix.dirname(manifest.repoRelativePath);
|
|
3426
|
+
const fileDir = path10.dirname(path10.resolve(manifest.originalPath));
|
|
3357
3427
|
if (repoDir === "." || !repoDir) {
|
|
3358
3428
|
return fileDir;
|
|
3359
3429
|
}
|
|
3360
3430
|
const segments = repoDir.split("/").filter(Boolean);
|
|
3361
|
-
return
|
|
3431
|
+
return path10.resolve(fileDir, ...segments.map(() => ".."));
|
|
3362
3432
|
}
|
|
3363
3433
|
function repoRelativePathFor(absolutePath, repoRoot) {
|
|
3364
3434
|
if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
|
|
3365
3435
|
return void 0;
|
|
3366
3436
|
}
|
|
3367
|
-
const relative = toPosix(
|
|
3437
|
+
const relative = toPosix(path10.relative(repoRoot, absolutePath));
|
|
3368
3438
|
return relative && !relative.startsWith("..") ? relative : void 0;
|
|
3369
3439
|
}
|
|
3370
3440
|
function normalizeOriginUrl(input) {
|
|
@@ -3661,7 +3731,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
|
|
|
3661
3731
|
return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
|
|
3662
3732
|
}
|
|
3663
3733
|
function sanitizeAssetRelativePath(value) {
|
|
3664
|
-
const normalized =
|
|
3734
|
+
const normalized = path10.posix.normalize(value.replace(/\\/g, "/"));
|
|
3665
3735
|
const segments = normalized.split("/").filter(Boolean).map((segment) => {
|
|
3666
3736
|
if (segment === ".") {
|
|
3667
3737
|
return "";
|
|
@@ -3681,7 +3751,7 @@ function normalizeLocalReference(value) {
|
|
|
3681
3751
|
return null;
|
|
3682
3752
|
}
|
|
3683
3753
|
const lowered = candidate.toLowerCase();
|
|
3684
|
-
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") ||
|
|
3754
|
+
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path10.isAbsolute(candidate)) {
|
|
3685
3755
|
return null;
|
|
3686
3756
|
}
|
|
3687
3757
|
return candidate.replace(/\\/g, "/");
|
|
@@ -3748,7 +3818,7 @@ async function readManifestByHash(manifestsDir, contentHash) {
|
|
|
3748
3818
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
3749
3819
|
continue;
|
|
3750
3820
|
}
|
|
3751
|
-
const manifest = await readJsonFile(
|
|
3821
|
+
const manifest = await readJsonFile(path10.join(manifestsDir, entry.name));
|
|
3752
3822
|
if (manifest?.contentHash === contentHash) {
|
|
3753
3823
|
return manifest;
|
|
3754
3824
|
}
|
|
@@ -3761,7 +3831,7 @@ async function readManifestByOrigin(manifestsDir, prepared) {
|
|
|
3761
3831
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
3762
3832
|
continue;
|
|
3763
3833
|
}
|
|
3764
|
-
const manifest = await readJsonFile(
|
|
3834
|
+
const manifest = await readJsonFile(path10.join(manifestsDir, entry.name));
|
|
3765
3835
|
if (manifest && manifestMatchesOrigin(manifest, prepared)) {
|
|
3766
3836
|
return manifest;
|
|
3767
3837
|
}
|
|
@@ -3772,7 +3842,7 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
|
3772
3842
|
if (!enabled) {
|
|
3773
3843
|
return null;
|
|
3774
3844
|
}
|
|
3775
|
-
const gitignorePath =
|
|
3845
|
+
const gitignorePath = path10.join(repoRoot, ".gitignore");
|
|
3776
3846
|
if (!await fileExists(gitignorePath)) {
|
|
3777
3847
|
return null;
|
|
3778
3848
|
}
|
|
@@ -3782,12 +3852,15 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
|
3782
3852
|
}
|
|
3783
3853
|
function builtInIgnoreReason(relativePath) {
|
|
3784
3854
|
for (const segment of relativePath.split("/")) {
|
|
3785
|
-
if (
|
|
3855
|
+
if (HARD_REPO_IGNORES.has(segment)) {
|
|
3786
3856
|
return `built_in_ignore:${segment}`;
|
|
3787
3857
|
}
|
|
3788
3858
|
}
|
|
3789
3859
|
return null;
|
|
3790
3860
|
}
|
|
3861
|
+
function sourceClassForRelativePath(relativePath, options) {
|
|
3862
|
+
return classifyRepoPath(relativePath, options.repoAnalysis);
|
|
3863
|
+
}
|
|
3791
3864
|
async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
3792
3865
|
const matcher = await loadGitignoreMatcher(repoRoot, options.gitignore);
|
|
3793
3866
|
const skipped = [];
|
|
@@ -3801,20 +3874,20 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
3801
3874
|
const entries = await fs9.readdir(currentDir, { withFileTypes: true });
|
|
3802
3875
|
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
3803
3876
|
for (const entry of entries) {
|
|
3804
|
-
const absolutePath =
|
|
3805
|
-
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
3877
|
+
const absolutePath = path10.join(currentDir, entry.name);
|
|
3878
|
+
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(inputDir, absolutePath));
|
|
3806
3879
|
const relativePath = relativeToRepo || entry.name;
|
|
3807
3880
|
const builtInReason = builtInIgnoreReason(relativePath);
|
|
3808
3881
|
if (builtInReason) {
|
|
3809
|
-
skipped.push({ path: toPosix(
|
|
3882
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: builtInReason });
|
|
3810
3883
|
continue;
|
|
3811
3884
|
}
|
|
3812
3885
|
if (matcher?.ignores(relativePath)) {
|
|
3813
|
-
skipped.push({ path: toPosix(
|
|
3886
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "gitignore" });
|
|
3814
3887
|
continue;
|
|
3815
3888
|
}
|
|
3816
|
-
if (
|
|
3817
|
-
skipped.push({ path: toPosix(
|
|
3889
|
+
if (matchesAnyGlob2(relativePath, options.exclude)) {
|
|
3890
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "exclude_glob" });
|
|
3818
3891
|
continue;
|
|
3819
3892
|
}
|
|
3820
3893
|
if (entry.isDirectory()) {
|
|
@@ -3822,21 +3895,26 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
3822
3895
|
continue;
|
|
3823
3896
|
}
|
|
3824
3897
|
if (!entry.isFile()) {
|
|
3825
|
-
skipped.push({ path: toPosix(
|
|
3898
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
|
|
3826
3899
|
continue;
|
|
3827
3900
|
}
|
|
3828
|
-
if (options.include.length > 0 && !
|
|
3829
|
-
skipped.push({ path: toPosix(
|
|
3901
|
+
if (options.include.length > 0 && !matchesAnyGlob2(relativePath, options.include)) {
|
|
3902
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "include_glob" });
|
|
3830
3903
|
continue;
|
|
3831
3904
|
}
|
|
3832
3905
|
const mimeType = guessMimeType(absolutePath);
|
|
3833
3906
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
3907
|
+
const sourceClass = sourceClassForRelativePath(relativePath, options);
|
|
3834
3908
|
if (!supportedDirectoryKind(sourceKind)) {
|
|
3835
|
-
skipped.push({ path: toPosix(
|
|
3909
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
3910
|
+
continue;
|
|
3911
|
+
}
|
|
3912
|
+
if (!options.extractClasses.includes(sourceClass)) {
|
|
3913
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
|
|
3836
3914
|
continue;
|
|
3837
3915
|
}
|
|
3838
3916
|
if (files.length >= options.maxFiles) {
|
|
3839
|
-
skipped.push({ path: toPosix(
|
|
3917
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "max_files" });
|
|
3840
3918
|
continue;
|
|
3841
3919
|
}
|
|
3842
3920
|
files.push(absolutePath);
|
|
@@ -3858,12 +3936,12 @@ function resolveUrlMimeType(input, response) {
|
|
|
3858
3936
|
function buildRemoteAssetRelativePath(assetUrl, mimeType) {
|
|
3859
3937
|
const url = new URL(assetUrl);
|
|
3860
3938
|
const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
|
|
3861
|
-
const extension =
|
|
3862
|
-
const directory =
|
|
3863
|
-
const basename = extension ?
|
|
3939
|
+
const extension = path10.posix.extname(normalized);
|
|
3940
|
+
const directory = path10.posix.dirname(normalized);
|
|
3941
|
+
const basename = extension ? path10.posix.basename(normalized, extension) : path10.posix.basename(normalized);
|
|
3864
3942
|
const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
3865
3943
|
const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
|
|
3866
|
-
return directory === "." ? hashedName :
|
|
3944
|
+
return directory === "." ? hashedName : path10.posix.join(directory, hashedName);
|
|
3867
3945
|
}
|
|
3868
3946
|
async function readResponseBytesWithinLimit(response, maxBytes) {
|
|
3869
3947
|
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
@@ -3990,7 +4068,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
3990
4068
|
const extractionHash = prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact);
|
|
3991
4069
|
const existingByOrigin = await readManifestByOrigin(paths.manifestsDir, prepared);
|
|
3992
4070
|
const existingByHash = existingByOrigin ? null : await readManifestByHash(paths.manifestsDir, contentHash);
|
|
3993
|
-
if (existingByOrigin && existingByOrigin.contentHash === contentHash && existingByOrigin.extractionHash === extractionHash && existingByOrigin.title === prepared.title && existingByOrigin.sourceKind === prepared.sourceKind && existingByOrigin.sourceType === prepared.sourceType && existingByOrigin.language === prepared.language && existingByOrigin.mimeType === prepared.mimeType && existingByOrigin.repoRelativePath === prepared.repoRelativePath) {
|
|
4071
|
+
if (existingByOrigin && existingByOrigin.contentHash === contentHash && existingByOrigin.extractionHash === extractionHash && existingByOrigin.title === prepared.title && existingByOrigin.sourceKind === prepared.sourceKind && existingByOrigin.sourceType === prepared.sourceType && existingByOrigin.sourceClass === prepared.sourceClass && existingByOrigin.language === prepared.language && existingByOrigin.mimeType === prepared.mimeType && existingByOrigin.repoRelativePath === prepared.repoRelativePath) {
|
|
3994
4072
|
return { manifest: existingByOrigin, isNew: false, wasUpdated: false };
|
|
3995
4073
|
}
|
|
3996
4074
|
if (existingByHash) {
|
|
@@ -3999,18 +4077,18 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
3999
4077
|
const previous = existingByOrigin ?? void 0;
|
|
4000
4078
|
const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
|
|
4001
4079
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4002
|
-
const storedPath =
|
|
4003
|
-
const extractedTextPath = prepared.extractedText ?
|
|
4004
|
-
const extractedMetadataPath = prepared.extractionArtifact ?
|
|
4005
|
-
const attachmentsDir =
|
|
4080
|
+
const storedPath = path10.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
|
|
4081
|
+
const extractedTextPath = prepared.extractedText ? path10.join(paths.extractsDir, `${sourceId}.md`) : void 0;
|
|
4082
|
+
const extractedMetadataPath = prepared.extractionArtifact ? path10.join(paths.extractsDir, `${sourceId}.json`) : void 0;
|
|
4083
|
+
const attachmentsDir = path10.join(paths.rawAssetsDir, sourceId);
|
|
4006
4084
|
if (previous?.storedPath) {
|
|
4007
|
-
await fs9.rm(
|
|
4085
|
+
await fs9.rm(path10.resolve(rootDir, previous.storedPath), { force: true });
|
|
4008
4086
|
}
|
|
4009
4087
|
if (previous?.extractedTextPath) {
|
|
4010
|
-
await fs9.rm(
|
|
4088
|
+
await fs9.rm(path10.resolve(rootDir, previous.extractedTextPath), { force: true });
|
|
4011
4089
|
}
|
|
4012
4090
|
if (previous?.extractedMetadataPath) {
|
|
4013
|
-
await fs9.rm(
|
|
4091
|
+
await fs9.rm(path10.resolve(rootDir, previous.extractedMetadataPath), { force: true });
|
|
4014
4092
|
}
|
|
4015
4093
|
await fs9.rm(attachmentsDir, { recursive: true, force: true });
|
|
4016
4094
|
await fs9.writeFile(storedPath, prepared.payloadBytes);
|
|
@@ -4022,11 +4100,11 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4022
4100
|
}
|
|
4023
4101
|
const manifestAttachments = [];
|
|
4024
4102
|
for (const attachment of attachments) {
|
|
4025
|
-
const absoluteAttachmentPath =
|
|
4026
|
-
await ensureDir(
|
|
4103
|
+
const absoluteAttachmentPath = path10.join(attachmentsDir, attachment.relativePath);
|
|
4104
|
+
await ensureDir(path10.dirname(absoluteAttachmentPath));
|
|
4027
4105
|
await fs9.writeFile(absoluteAttachmentPath, attachment.bytes);
|
|
4028
4106
|
manifestAttachments.push({
|
|
4029
|
-
path: toPosix(
|
|
4107
|
+
path: toPosix(path10.relative(rootDir, absoluteAttachmentPath)),
|
|
4030
4108
|
mimeType: attachment.mimeType,
|
|
4031
4109
|
originalPath: attachment.originalPath
|
|
4032
4110
|
});
|
|
@@ -4037,13 +4115,14 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4037
4115
|
originType: prepared.originType,
|
|
4038
4116
|
sourceKind: prepared.sourceKind,
|
|
4039
4117
|
sourceType: prepared.sourceType,
|
|
4118
|
+
sourceClass: prepared.sourceClass,
|
|
4040
4119
|
language: prepared.language,
|
|
4041
4120
|
originalPath: prepared.originalPath,
|
|
4042
4121
|
repoRelativePath: prepared.repoRelativePath,
|
|
4043
4122
|
url: prepared.url,
|
|
4044
|
-
storedPath: toPosix(
|
|
4045
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
4046
|
-
extractedMetadataPath: extractedMetadataPath ? toPosix(
|
|
4123
|
+
storedPath: toPosix(path10.relative(rootDir, storedPath)),
|
|
4124
|
+
extractedTextPath: extractedTextPath ? toPosix(path10.relative(rootDir, extractedTextPath)) : void 0,
|
|
4125
|
+
extractedMetadataPath: extractedMetadataPath ? toPosix(path10.relative(rootDir, extractedMetadataPath)) : void 0,
|
|
4047
4126
|
extractionHash,
|
|
4048
4127
|
mimeType: prepared.mimeType,
|
|
4049
4128
|
contentHash,
|
|
@@ -4051,7 +4130,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4051
4130
|
updatedAt: now,
|
|
4052
4131
|
attachments: manifestAttachments.length ? manifestAttachments : void 0
|
|
4053
4132
|
};
|
|
4054
|
-
await writeJsonFile(
|
|
4133
|
+
await writeJsonFile(path10.join(paths.manifestsDir, `${sourceId}.json`), manifest);
|
|
4055
4134
|
await appendLogEntry(rootDir, "ingest", prepared.title, [
|
|
4056
4135
|
`source_id=${sourceId}`,
|
|
4057
4136
|
`kind=${prepared.sourceKind}`,
|
|
@@ -4069,16 +4148,16 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4069
4148
|
return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
|
|
4070
4149
|
}
|
|
4071
4150
|
async function removeManifestArtifacts(rootDir, manifest, paths) {
|
|
4072
|
-
await fs9.rm(
|
|
4073
|
-
await fs9.rm(
|
|
4151
|
+
await fs9.rm(path10.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
|
|
4152
|
+
await fs9.rm(path10.resolve(rootDir, manifest.storedPath), { force: true });
|
|
4074
4153
|
if (manifest.extractedTextPath) {
|
|
4075
|
-
await fs9.rm(
|
|
4154
|
+
await fs9.rm(path10.resolve(rootDir, manifest.extractedTextPath), { force: true });
|
|
4076
4155
|
}
|
|
4077
4156
|
if (manifest.extractedMetadataPath) {
|
|
4078
|
-
await fs9.rm(
|
|
4157
|
+
await fs9.rm(path10.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
|
|
4079
4158
|
}
|
|
4080
|
-
await fs9.rm(
|
|
4081
|
-
await fs9.rm(
|
|
4159
|
+
await fs9.rm(path10.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
|
|
4160
|
+
await fs9.rm(path10.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
|
|
4082
4161
|
}
|
|
4083
4162
|
function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
4084
4163
|
const candidates = [
|
|
@@ -4087,14 +4166,14 @@ function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
|
4087
4166
|
paths.stateDir,
|
|
4088
4167
|
paths.agentDir,
|
|
4089
4168
|
paths.inboxDir,
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4169
|
+
path10.join(rootDir, ".claude"),
|
|
4170
|
+
path10.join(rootDir, ".cursor"),
|
|
4171
|
+
path10.join(rootDir, ".obsidian")
|
|
4093
4172
|
];
|
|
4094
|
-
return candidates.map((candidate) =>
|
|
4173
|
+
return candidates.map((candidate) => path10.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
|
|
4095
4174
|
}
|
|
4096
4175
|
function preparedMatchesManifest(manifest, prepared, contentHash) {
|
|
4097
|
-
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.language === prepared.language && manifest.mimeType === prepared.mimeType && manifest.repoRelativePath === prepared.repoRelativePath;
|
|
4176
|
+
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;
|
|
4098
4177
|
}
|
|
4099
4178
|
function shouldDeferWatchSemanticRefresh(sourceKind) {
|
|
4100
4179
|
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "image";
|
|
@@ -4110,19 +4189,19 @@ async function listTrackedRepoRoots(rootDir) {
|
|
|
4110
4189
|
}
|
|
4111
4190
|
async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
4112
4191
|
const { paths } = await initWorkspace(rootDir);
|
|
4113
|
-
const normalizedOptions =
|
|
4192
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4114
4193
|
const manifests = await listManifests(rootDir);
|
|
4115
4194
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
4116
|
-
(item) =>
|
|
4195
|
+
(item) => path10.resolve(item)
|
|
4117
4196
|
);
|
|
4118
4197
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
4119
4198
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
4120
4199
|
for (const manifest of manifests) {
|
|
4121
4200
|
const repoRoot = repoRootFromManifest(manifest);
|
|
4122
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
4201
|
+
if (!repoRoot || !uniqueRoots.includes(path10.resolve(repoRoot))) {
|
|
4123
4202
|
continue;
|
|
4124
4203
|
}
|
|
4125
|
-
const key =
|
|
4204
|
+
const key = path10.resolve(repoRoot);
|
|
4126
4205
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
4127
4206
|
bucket.push(manifest);
|
|
4128
4207
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -4147,14 +4226,15 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4147
4226
|
skipped.push(
|
|
4148
4227
|
...collected.skipped,
|
|
4149
4228
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
4150
|
-
path: toPosix(
|
|
4229
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4151
4230
|
reason: "workspace_generated"
|
|
4152
4231
|
}))
|
|
4153
4232
|
);
|
|
4154
4233
|
scannedCount += files.length;
|
|
4155
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
4234
|
+
const currentPaths = new Set(files.map((absolutePath) => path10.resolve(absolutePath)));
|
|
4156
4235
|
for (const absolutePath of files) {
|
|
4157
|
-
const
|
|
4236
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4237
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4158
4238
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4159
4239
|
if (result.isNew) {
|
|
4160
4240
|
imported.push(result.manifest);
|
|
@@ -4163,7 +4243,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4163
4243
|
}
|
|
4164
4244
|
}
|
|
4165
4245
|
for (const manifest of repoManifests) {
|
|
4166
|
-
const originalPath = manifest.originalPath ?
|
|
4246
|
+
const originalPath = manifest.originalPath ? path10.resolve(manifest.originalPath) : null;
|
|
4167
4247
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
4168
4248
|
await removeManifestArtifacts(rootDir, manifest, paths);
|
|
4169
4249
|
removed.push(manifest);
|
|
@@ -4171,7 +4251,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4171
4251
|
}
|
|
4172
4252
|
}
|
|
4173
4253
|
if (uniqueRoots.length > 0) {
|
|
4174
|
-
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(
|
|
4254
|
+
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path10.relative(rootDir, repoRoot)) || ".").join(","), [
|
|
4175
4255
|
`repo_roots=${uniqueRoots.length}`,
|
|
4176
4256
|
`scanned=${scannedCount}`,
|
|
4177
4257
|
`imported=${imported.length}`,
|
|
@@ -4191,19 +4271,19 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4191
4271
|
}
|
|
4192
4272
|
async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
4193
4273
|
const { paths } = await initWorkspace(rootDir);
|
|
4194
|
-
const normalizedOptions =
|
|
4274
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4195
4275
|
const manifests = await listManifests(rootDir);
|
|
4196
4276
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
4197
|
-
(item) =>
|
|
4277
|
+
(item) => path10.resolve(item)
|
|
4198
4278
|
);
|
|
4199
4279
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
4200
4280
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
4201
4281
|
for (const manifest of manifests) {
|
|
4202
4282
|
const repoRoot = repoRootFromManifest(manifest);
|
|
4203
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
4283
|
+
if (!repoRoot || !uniqueRoots.includes(path10.resolve(repoRoot))) {
|
|
4204
4284
|
continue;
|
|
4205
4285
|
}
|
|
4206
|
-
const key =
|
|
4286
|
+
const key = path10.resolve(repoRoot);
|
|
4207
4287
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
4208
4288
|
bucket.push(manifest);
|
|
4209
4289
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -4218,7 +4298,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4218
4298
|
for (const repoRoot of uniqueRoots) {
|
|
4219
4299
|
const repoManifests = manifestsByRepoRoot.get(repoRoot) ?? [];
|
|
4220
4300
|
const manifestsByOriginalPath = new Map(
|
|
4221
|
-
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [
|
|
4301
|
+
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path10.resolve(manifest.originalPath), manifest])
|
|
4222
4302
|
);
|
|
4223
4303
|
if (!await fileExists(repoRoot)) {
|
|
4224
4304
|
for (const manifest of repoManifests) {
|
|
@@ -4226,7 +4306,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4226
4306
|
pendingSemanticRefresh.push({
|
|
4227
4307
|
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? manifest.storedPath),
|
|
4228
4308
|
repoRoot,
|
|
4229
|
-
path: toPosix(
|
|
4309
|
+
path: toPosix(path10.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
|
|
4230
4310
|
changeType: "removed",
|
|
4231
4311
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4232
4312
|
sourceId: manifest.sourceId,
|
|
@@ -4246,16 +4326,17 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4246
4326
|
skipped.push(
|
|
4247
4327
|
...collected.skipped,
|
|
4248
4328
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
4249
|
-
path: toPosix(
|
|
4329
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4250
4330
|
reason: "workspace_generated"
|
|
4251
4331
|
}))
|
|
4252
4332
|
);
|
|
4253
4333
|
scannedCount += files.length;
|
|
4254
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
4334
|
+
const currentPaths = new Set(files.map((absolutePath) => path10.resolve(absolutePath)));
|
|
4255
4335
|
for (const absolutePath of files) {
|
|
4256
|
-
const
|
|
4336
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4337
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4257
4338
|
if (shouldDeferWatchSemanticRefresh(prepared.sourceKind)) {
|
|
4258
|
-
const existing = manifestsByOriginalPath.get(
|
|
4339
|
+
const existing = manifestsByOriginalPath.get(path10.resolve(absolutePath));
|
|
4259
4340
|
const contentHash = buildCompositeHash(prepared.payloadBytes, prepared.attachments);
|
|
4260
4341
|
const changed = !existing || !preparedMatchesManifest(existing, prepared, contentHash);
|
|
4261
4342
|
if (changed) {
|
|
@@ -4263,10 +4344,10 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4263
4344
|
id: pendingSemanticRefreshId(
|
|
4264
4345
|
existing ? "modified" : "added",
|
|
4265
4346
|
repoRoot,
|
|
4266
|
-
prepared.repoRelativePath ?? toPosix(
|
|
4347
|
+
prepared.repoRelativePath ?? toPosix(path10.relative(repoRoot, absolutePath))
|
|
4267
4348
|
),
|
|
4268
4349
|
repoRoot,
|
|
4269
|
-
path: toPosix(
|
|
4350
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4270
4351
|
changeType: existing ? "modified" : "added",
|
|
4271
4352
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4272
4353
|
sourceId: existing?.sourceId,
|
|
@@ -4286,13 +4367,13 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4286
4367
|
}
|
|
4287
4368
|
}
|
|
4288
4369
|
for (const manifest of repoManifests) {
|
|
4289
|
-
const originalPath = manifest.originalPath ?
|
|
4370
|
+
const originalPath = manifest.originalPath ? path10.resolve(manifest.originalPath) : null;
|
|
4290
4371
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
4291
4372
|
if (shouldDeferWatchSemanticRefresh(manifest.sourceKind)) {
|
|
4292
4373
|
pendingSemanticRefresh.push({
|
|
4293
|
-
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(
|
|
4374
|
+
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path10.relative(repoRoot, originalPath))),
|
|
4294
4375
|
repoRoot,
|
|
4295
|
-
path: toPosix(
|
|
4376
|
+
path: toPosix(path10.relative(rootDir, originalPath)),
|
|
4296
4377
|
changeType: "removed",
|
|
4297
4378
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4298
4379
|
sourceId: manifest.sourceId,
|
|
@@ -4310,7 +4391,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4310
4391
|
await appendLogEntry(
|
|
4311
4392
|
rootDir,
|
|
4312
4393
|
"sync_repo_watch",
|
|
4313
|
-
uniqueRoots.map((repoRoot) => toPosix(
|
|
4394
|
+
uniqueRoots.map((repoRoot) => toPosix(path10.relative(rootDir, repoRoot)) || ".").join(","),
|
|
4314
4395
|
[
|
|
4315
4396
|
`repo_roots=${uniqueRoots.length}`,
|
|
4316
4397
|
`scanned=${scannedCount}`,
|
|
@@ -4335,26 +4416,26 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4335
4416
|
staleSourceIds: [...staleSourceIds]
|
|
4336
4417
|
};
|
|
4337
4418
|
}
|
|
4338
|
-
async function prepareFileInput(rootDir, absoluteInput, repoRoot) {
|
|
4419
|
+
async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
4339
4420
|
const payloadBytes = await fs9.readFile(absoluteInput);
|
|
4340
4421
|
const mimeType = guessMimeType(absoluteInput);
|
|
4341
4422
|
const sourceKind = inferKind(mimeType, absoluteInput);
|
|
4342
4423
|
const language = inferCodeLanguage(absoluteInput, mimeType);
|
|
4343
|
-
const storedExtension =
|
|
4424
|
+
const storedExtension = path10.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
4344
4425
|
let title;
|
|
4345
4426
|
let extractedText;
|
|
4346
4427
|
let extractionArtifact;
|
|
4347
4428
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
4348
4429
|
extractedText = payloadBytes.toString("utf8");
|
|
4349
|
-
title = titleFromText(
|
|
4430
|
+
title = titleFromText(path10.basename(absoluteInput, path10.extname(absoluteInput)), extractedText);
|
|
4350
4431
|
extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
|
|
4351
4432
|
} else if (sourceKind === "pdf") {
|
|
4352
|
-
title =
|
|
4433
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4353
4434
|
const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
|
|
4354
4435
|
extractedText = extracted.extractedText;
|
|
4355
4436
|
extractionArtifact = extracted.artifact;
|
|
4356
4437
|
} else if (sourceKind === "image") {
|
|
4357
|
-
title =
|
|
4438
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4358
4439
|
const extracted = await extractImageWithVision(rootDir, {
|
|
4359
4440
|
title,
|
|
4360
4441
|
mimeType,
|
|
@@ -4364,12 +4445,13 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot) {
|
|
|
4364
4445
|
extractedText = extracted.extractedText;
|
|
4365
4446
|
extractionArtifact = extracted.artifact;
|
|
4366
4447
|
} else {
|
|
4367
|
-
title =
|
|
4448
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4368
4449
|
}
|
|
4369
4450
|
return {
|
|
4370
4451
|
title,
|
|
4371
4452
|
originType: "file",
|
|
4372
4453
|
sourceKind,
|
|
4454
|
+
sourceClass,
|
|
4373
4455
|
language,
|
|
4374
4456
|
originalPath: toPosix(absoluteInput),
|
|
4375
4457
|
repoRelativePath: repoRelativePathFor(absoluteInput, repoRoot),
|
|
@@ -4439,7 +4521,7 @@ async function prepareUrlInput(rootDir, input, options) {
|
|
|
4439
4521
|
sourceKind = "markdown";
|
|
4440
4522
|
storedExtension = ".md";
|
|
4441
4523
|
} else {
|
|
4442
|
-
const extension =
|
|
4524
|
+
const extension = path10.extname(inputUrl.pathname);
|
|
4443
4525
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
4444
4526
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
4445
4527
|
extractedText = payloadBytes.toString("utf8");
|
|
@@ -4512,7 +4594,7 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
4512
4594
|
}
|
|
4513
4595
|
const sourceRefs = [];
|
|
4514
4596
|
for (const ref of refs) {
|
|
4515
|
-
const resolved =
|
|
4597
|
+
const resolved = path10.resolve(path10.dirname(absolutePath), ref);
|
|
4516
4598
|
if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
|
|
4517
4599
|
continue;
|
|
4518
4600
|
}
|
|
@@ -4548,7 +4630,7 @@ function rewriteMarkdownReferences(content, replacements) {
|
|
|
4548
4630
|
async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
4549
4631
|
const originalBytes = await fs9.readFile(absolutePath);
|
|
4550
4632
|
const originalText = originalBytes.toString("utf8");
|
|
4551
|
-
const title = titleFromText(
|
|
4633
|
+
const title = titleFromText(path10.basename(absolutePath, path10.extname(absolutePath)), originalText);
|
|
4552
4634
|
const attachments = [];
|
|
4553
4635
|
for (const attachmentRef of attachmentRefs) {
|
|
4554
4636
|
const bytes = await fs9.readFile(attachmentRef.absolutePath);
|
|
@@ -4575,7 +4657,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
4575
4657
|
sourceKind: "markdown",
|
|
4576
4658
|
originalPath: toPosix(absolutePath),
|
|
4577
4659
|
mimeType: "text/markdown",
|
|
4578
|
-
storedExtension:
|
|
4660
|
+
storedExtension: path10.extname(absolutePath) || ".md",
|
|
4579
4661
|
payloadBytes: Buffer.from(rewrittenText, "utf8"),
|
|
4580
4662
|
extractedText: rewrittenText,
|
|
4581
4663
|
extractionArtifact,
|
|
@@ -4590,8 +4672,8 @@ function isSupportedInboxKind(sourceKind) {
|
|
|
4590
4672
|
async function ingestInput(rootDir, input, options) {
|
|
4591
4673
|
const { paths } = await initWorkspace(rootDir);
|
|
4592
4674
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
4593
|
-
const absoluteInput =
|
|
4594
|
-
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ??
|
|
4675
|
+
const absoluteInput = path10.resolve(rootDir, input);
|
|
4676
|
+
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path10.dirname(absoluteInput));
|
|
4595
4677
|
const prepared = isHttpUrl(input) ? await prepareUrlInput(rootDir, input, normalizedOptions) : await prepareFileInput(rootDir, absoluteInput, repoRoot);
|
|
4596
4678
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4597
4679
|
return result.manifest;
|
|
@@ -4680,8 +4762,8 @@ async function addInput(rootDir, input, options = {}) {
|
|
|
4680
4762
|
}
|
|
4681
4763
|
async function ingestDirectory(rootDir, inputDir, options) {
|
|
4682
4764
|
const { paths } = await initWorkspace(rootDir);
|
|
4683
|
-
const normalizedOptions =
|
|
4684
|
-
const absoluteInputDir =
|
|
4765
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4766
|
+
const absoluteInputDir = path10.resolve(rootDir, inputDir);
|
|
4685
4767
|
const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot2(absoluteInputDir) ?? absoluteInputDir;
|
|
4686
4768
|
if (!await fileExists(absoluteInputDir)) {
|
|
4687
4769
|
throw new Error(`Directory not found: ${absoluteInputDir}`);
|
|
@@ -4690,18 +4772,19 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
4690
4772
|
const imported = [];
|
|
4691
4773
|
const updated = [];
|
|
4692
4774
|
for (const absolutePath of files) {
|
|
4693
|
-
const
|
|
4775
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4776
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4694
4777
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4695
4778
|
if (result.isNew) {
|
|
4696
4779
|
imported.push(result.manifest);
|
|
4697
4780
|
} else if (result.wasUpdated) {
|
|
4698
4781
|
updated.push(result.manifest);
|
|
4699
4782
|
} else {
|
|
4700
|
-
skipped.push({ path: toPosix(
|
|
4783
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
4701
4784
|
}
|
|
4702
4785
|
}
|
|
4703
|
-
await appendLogEntry(rootDir, "ingest_directory", toPosix(
|
|
4704
|
-
`repo_root=${toPosix(
|
|
4786
|
+
await appendLogEntry(rootDir, "ingest_directory", toPosix(path10.relative(rootDir, absoluteInputDir)) || ".", [
|
|
4787
|
+
`repo_root=${toPosix(path10.relative(rootDir, repoRoot)) || "."}`,
|
|
4705
4788
|
`scanned=${files.length}`,
|
|
4706
4789
|
`imported=${imported.length}`,
|
|
4707
4790
|
`updated=${updated.length}`,
|
|
@@ -4718,7 +4801,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
4718
4801
|
}
|
|
4719
4802
|
async function importInbox(rootDir, inputDir) {
|
|
4720
4803
|
const { paths } = await initWorkspace(rootDir);
|
|
4721
|
-
const effectiveInputDir =
|
|
4804
|
+
const effectiveInputDir = path10.resolve(rootDir, inputDir ?? paths.inboxDir);
|
|
4722
4805
|
if (!await fileExists(effectiveInputDir)) {
|
|
4723
4806
|
throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
|
|
4724
4807
|
}
|
|
@@ -4729,31 +4812,31 @@ async function importInbox(rootDir, inputDir) {
|
|
|
4729
4812
|
const skipped = [];
|
|
4730
4813
|
let attachmentCount = 0;
|
|
4731
4814
|
for (const absolutePath of files) {
|
|
4732
|
-
const basename =
|
|
4815
|
+
const basename = path10.basename(absolutePath);
|
|
4733
4816
|
if (basename.startsWith(".")) {
|
|
4734
|
-
skipped.push({ path: toPosix(
|
|
4817
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "hidden_file" });
|
|
4735
4818
|
continue;
|
|
4736
4819
|
}
|
|
4737
4820
|
if (claimedAttachments.has(absolutePath)) {
|
|
4738
|
-
skipped.push({ path: toPosix(
|
|
4821
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
|
|
4739
4822
|
continue;
|
|
4740
4823
|
}
|
|
4741
4824
|
const mimeType = guessMimeType(absolutePath);
|
|
4742
4825
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
4743
4826
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
4744
|
-
skipped.push({ path: toPosix(
|
|
4827
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
4745
4828
|
continue;
|
|
4746
4829
|
}
|
|
4747
4830
|
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
4748
4831
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4749
4832
|
if (!result.isNew) {
|
|
4750
|
-
skipped.push({ path: toPosix(
|
|
4833
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
4751
4834
|
continue;
|
|
4752
4835
|
}
|
|
4753
4836
|
attachmentCount += result.manifest.attachments?.length ?? 0;
|
|
4754
4837
|
imported.push(result.manifest);
|
|
4755
4838
|
}
|
|
4756
|
-
await appendLogEntry(rootDir, "inbox_import", toPosix(
|
|
4839
|
+
await appendLogEntry(rootDir, "inbox_import", toPosix(path10.relative(rootDir, effectiveInputDir)) || ".", [
|
|
4757
4840
|
`scanned=${files.length}`,
|
|
4758
4841
|
`imported=${imported.length}`,
|
|
4759
4842
|
`attachments=${attachmentCount}`,
|
|
@@ -4774,7 +4857,7 @@ async function listManifests(rootDir) {
|
|
|
4774
4857
|
}
|
|
4775
4858
|
const entries = await fs9.readdir(paths.manifestsDir);
|
|
4776
4859
|
const manifests = await Promise.all(
|
|
4777
|
-
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(
|
|
4860
|
+
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path10.join(paths.manifestsDir, entry)))
|
|
4778
4861
|
);
|
|
4779
4862
|
return manifests.filter((manifest) => Boolean(manifest));
|
|
4780
4863
|
}
|
|
@@ -4782,7 +4865,7 @@ async function readExtractedText(rootDir, manifest) {
|
|
|
4782
4865
|
if (!manifest.extractedTextPath) {
|
|
4783
4866
|
return void 0;
|
|
4784
4867
|
}
|
|
4785
|
-
const absolutePath =
|
|
4868
|
+
const absolutePath = path10.resolve(rootDir, manifest.extractedTextPath);
|
|
4786
4869
|
if (!await fileExists(absolutePath)) {
|
|
4787
4870
|
return void 0;
|
|
4788
4871
|
}
|
|
@@ -4792,7 +4875,7 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
4792
4875
|
if (!manifest.extractedMetadataPath) {
|
|
4793
4876
|
return void 0;
|
|
4794
4877
|
}
|
|
4795
|
-
const absolutePath =
|
|
4878
|
+
const absolutePath = path10.resolve(rootDir, manifest.extractedMetadataPath);
|
|
4796
4879
|
if (!await fileExists(absolutePath)) {
|
|
4797
4880
|
return void 0;
|
|
4798
4881
|
}
|
|
@@ -4800,15 +4883,15 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
4800
4883
|
}
|
|
4801
4884
|
|
|
4802
4885
|
// src/mcp.ts
|
|
4803
|
-
import
|
|
4804
|
-
import
|
|
4886
|
+
import fs17 from "fs/promises";
|
|
4887
|
+
import path21 from "path";
|
|
4805
4888
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4806
4889
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4807
4890
|
import { z as z8 } from "zod";
|
|
4808
4891
|
|
|
4809
4892
|
// src/schema.ts
|
|
4810
4893
|
import fs10 from "fs/promises";
|
|
4811
|
-
import
|
|
4894
|
+
import path11 from "path";
|
|
4812
4895
|
function normalizeSchemaContent(content) {
|
|
4813
4896
|
return content.trim() ? content.trim() : defaultVaultSchema().trim();
|
|
4814
4897
|
}
|
|
@@ -4822,7 +4905,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
|
4822
4905
|
};
|
|
4823
4906
|
}
|
|
4824
4907
|
function resolveProjectSchemaPath(rootDir, schemaPath) {
|
|
4825
|
-
return
|
|
4908
|
+
return path11.resolve(rootDir, schemaPath);
|
|
4826
4909
|
}
|
|
4827
4910
|
function composeVaultSchema(root, projectSchemas = []) {
|
|
4828
4911
|
if (!projectSchemas.length) {
|
|
@@ -4838,7 +4921,7 @@ function composeVaultSchema(root, projectSchemas = []) {
|
|
|
4838
4921
|
(schema) => [
|
|
4839
4922
|
`## Project Schema`,
|
|
4840
4923
|
"",
|
|
4841
|
-
`Path: ${toPosix(
|
|
4924
|
+
`Path: ${toPosix(path11.relative(path11.dirname(root.path), schema.path) || schema.path)}`,
|
|
4842
4925
|
"",
|
|
4843
4926
|
schema.content
|
|
4844
4927
|
].join("\n")
|
|
@@ -4914,13 +4997,13 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
4914
4997
|
}
|
|
4915
4998
|
|
|
4916
4999
|
// src/vault.ts
|
|
4917
|
-
import
|
|
4918
|
-
import
|
|
5000
|
+
import fs16 from "fs/promises";
|
|
5001
|
+
import path20 from "path";
|
|
4919
5002
|
import matter9 from "gray-matter";
|
|
4920
5003
|
import { z as z7 } from "zod";
|
|
4921
5004
|
|
|
4922
5005
|
// src/analysis.ts
|
|
4923
|
-
import
|
|
5006
|
+
import path12 from "path";
|
|
4924
5007
|
import { z as z2 } from "zod";
|
|
4925
5008
|
var ANALYSIS_FORMAT_VERSION = 5;
|
|
4926
5009
|
var sourceAnalysisSchema = z2.object({
|
|
@@ -5143,7 +5226,7 @@ function extractionWarningSummary(manifest, extraction) {
|
|
|
5143
5226
|
return `Imported ${manifest.sourceKind} source. Text extraction is not yet available for this source.`;
|
|
5144
5227
|
}
|
|
5145
5228
|
async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
5146
|
-
const cachePath =
|
|
5229
|
+
const cachePath = path12.join(paths.analysesDir, `${manifest.sourceId}.json`);
|
|
5147
5230
|
const cached = await readJsonFile(cachePath);
|
|
5148
5231
|
if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
|
|
5149
5232
|
return cached;
|
|
@@ -5286,6 +5369,7 @@ function graphHash(graph) {
|
|
|
5286
5369
|
type: node.type,
|
|
5287
5370
|
label: node.label,
|
|
5288
5371
|
pageId: node.pageId ?? null,
|
|
5372
|
+
sourceClass: node.sourceClass ?? null,
|
|
5289
5373
|
communityId: node.communityId ?? null,
|
|
5290
5374
|
degree: node.degree ?? null,
|
|
5291
5375
|
bridgeScore: node.bridgeScore ?? null,
|
|
@@ -5300,6 +5384,7 @@ function graphHash(graph) {
|
|
|
5300
5384
|
relation: edge.relation,
|
|
5301
5385
|
status: edge.status,
|
|
5302
5386
|
evidenceClass: edge.evidenceClass,
|
|
5387
|
+
similarityBasis: edge.similarityBasis ?? null,
|
|
5303
5388
|
confidence: edge.confidence,
|
|
5304
5389
|
provenance: [...edge.provenance].sort()
|
|
5305
5390
|
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
@@ -5309,6 +5394,7 @@ function graphHash(graph) {
|
|
|
5309
5394
|
kind: page.kind,
|
|
5310
5395
|
status: page.status,
|
|
5311
5396
|
sourceType: page.sourceType ?? null,
|
|
5397
|
+
sourceClass: page.sourceClass ?? null,
|
|
5312
5398
|
sourceIds: [...page.sourceIds].sort(),
|
|
5313
5399
|
projectIds: [...page.projectIds].sort(),
|
|
5314
5400
|
nodeIds: [...page.nodeIds].sort()
|
|
@@ -5386,7 +5472,7 @@ function conflictConfidence(claimA, claimB) {
|
|
|
5386
5472
|
|
|
5387
5473
|
// src/deep-lint.ts
|
|
5388
5474
|
import fs11 from "fs/promises";
|
|
5389
|
-
import
|
|
5475
|
+
import path15 from "path";
|
|
5390
5476
|
import matter4 from "gray-matter";
|
|
5391
5477
|
import { z as z5 } from "zod";
|
|
5392
5478
|
|
|
@@ -5407,7 +5493,7 @@ function normalizeFindingSeverity(value) {
|
|
|
5407
5493
|
|
|
5408
5494
|
// src/orchestration.ts
|
|
5409
5495
|
import { spawn } from "child_process";
|
|
5410
|
-
import
|
|
5496
|
+
import path13 from "path";
|
|
5411
5497
|
import { z as z3 } from "zod";
|
|
5412
5498
|
var orchestrationRoleResultSchema = z3.object({
|
|
5413
5499
|
summary: z3.string().optional(),
|
|
@@ -5500,7 +5586,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
|
|
|
5500
5586
|
}
|
|
5501
5587
|
async function runCommandRole(rootDir, role, executor, input) {
|
|
5502
5588
|
const [command, ...args] = executor.command;
|
|
5503
|
-
const cwd = executor.cwd ?
|
|
5589
|
+
const cwd = executor.cwd ? path13.resolve(rootDir, executor.cwd) : rootDir;
|
|
5504
5590
|
const child = spawn(command, args, {
|
|
5505
5591
|
cwd,
|
|
5506
5592
|
env: {
|
|
@@ -5594,7 +5680,7 @@ function summarizeRoleQuestions(results) {
|
|
|
5594
5680
|
}
|
|
5595
5681
|
|
|
5596
5682
|
// src/web-search/registry.ts
|
|
5597
|
-
import
|
|
5683
|
+
import path14 from "path";
|
|
5598
5684
|
import { pathToFileURL } from "url";
|
|
5599
5685
|
import { z as z4 } from "zod";
|
|
5600
5686
|
|
|
@@ -5692,7 +5778,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
|
|
|
5692
5778
|
if (!config.module) {
|
|
5693
5779
|
throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
|
|
5694
5780
|
}
|
|
5695
|
-
const resolvedModule =
|
|
5781
|
+
const resolvedModule = path14.isAbsolute(config.module) ? config.module : path14.resolve(rootDir, config.module);
|
|
5696
5782
|
const loaded = await import(pathToFileURL(resolvedModule).href);
|
|
5697
5783
|
const parsed = customWebSearchModuleSchema.parse(loaded);
|
|
5698
5784
|
return parsed.createAdapter(id, config, rootDir);
|
|
@@ -5752,7 +5838,7 @@ async function loadContextPages(rootDir, graph) {
|
|
|
5752
5838
|
);
|
|
5753
5839
|
return Promise.all(
|
|
5754
5840
|
contextPages.slice(0, 18).map(async (page) => {
|
|
5755
|
-
const absolutePath =
|
|
5841
|
+
const absolutePath = path15.join(paths.wikiDir, page.path);
|
|
5756
5842
|
const raw = await fs11.readFile(absolutePath, "utf8").catch(() => "");
|
|
5757
5843
|
const parsed = matter4(raw);
|
|
5758
5844
|
return {
|
|
@@ -5801,7 +5887,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
|
|
|
5801
5887
|
code: "missing_citation",
|
|
5802
5888
|
message: finding.message,
|
|
5803
5889
|
pagePath: finding.pagePath,
|
|
5804
|
-
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${
|
|
5890
|
+
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path15.basename(finding.pagePath, ".md")}?` : void 0
|
|
5805
5891
|
});
|
|
5806
5892
|
}
|
|
5807
5893
|
for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
|
|
@@ -5978,6 +6064,353 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
|
|
|
5978
6064
|
);
|
|
5979
6065
|
}
|
|
5980
6066
|
|
|
6067
|
+
// src/embeddings.ts
|
|
6068
|
+
import fs12 from "fs/promises";
|
|
6069
|
+
import path16 from "path";
|
|
6070
|
+
var MAX_EMBEDDING_BATCH = 32;
|
|
6071
|
+
var MAX_SIMILARITY_NODES = 240;
|
|
6072
|
+
function cosineSimilarity(left, right) {
|
|
6073
|
+
if (!left.length || left.length !== right.length) {
|
|
6074
|
+
return 0;
|
|
6075
|
+
}
|
|
6076
|
+
let dot = 0;
|
|
6077
|
+
let leftNorm = 0;
|
|
6078
|
+
let rightNorm = 0;
|
|
6079
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
6080
|
+
dot += left[index] * right[index];
|
|
6081
|
+
leftNorm += left[index] * left[index];
|
|
6082
|
+
rightNorm += right[index] * right[index];
|
|
6083
|
+
}
|
|
6084
|
+
if (leftNorm <= 0 || rightNorm <= 0) {
|
|
6085
|
+
return 0;
|
|
6086
|
+
}
|
|
6087
|
+
return dot / Math.sqrt(leftNorm * rightNorm);
|
|
6088
|
+
}
|
|
6089
|
+
function appendIfMissing(parts, value) {
|
|
6090
|
+
const normalized = value?.trim();
|
|
6091
|
+
if (!normalized) {
|
|
6092
|
+
return;
|
|
6093
|
+
}
|
|
6094
|
+
if (!parts.includes(normalized)) {
|
|
6095
|
+
parts.push(normalized);
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
async function loadPageContents(rootDir, graph) {
|
|
6099
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6100
|
+
const contents = /* @__PURE__ */ new Map();
|
|
6101
|
+
await Promise.all(
|
|
6102
|
+
graph.pages.map(async (page) => {
|
|
6103
|
+
const absolutePath = path16.join(paths.wikiDir, page.path);
|
|
6104
|
+
const content = await fs12.readFile(absolutePath, "utf8").catch(() => "");
|
|
6105
|
+
contents.set(page.id, content);
|
|
6106
|
+
})
|
|
6107
|
+
);
|
|
6108
|
+
return contents;
|
|
6109
|
+
}
|
|
6110
|
+
function itemTextForNode(node, graph, pageContents) {
|
|
6111
|
+
const page = graph.pages.find((candidate) => candidate.id === node.pageId);
|
|
6112
|
+
const parts = [`node ${node.type}`, node.label];
|
|
6113
|
+
appendIfMissing(parts, node.sourceClass);
|
|
6114
|
+
appendIfMissing(parts, node.language);
|
|
6115
|
+
appendIfMissing(parts, page?.title);
|
|
6116
|
+
appendIfMissing(parts, page?.sourceType);
|
|
6117
|
+
appendIfMissing(parts, page?.sourceClass);
|
|
6118
|
+
if (page) {
|
|
6119
|
+
appendIfMissing(parts, pageContents.get(page.id)?.slice(0, 800));
|
|
6120
|
+
}
|
|
6121
|
+
return parts.join("\n");
|
|
6122
|
+
}
|
|
6123
|
+
function itemTextForPage(page, pageContents) {
|
|
6124
|
+
const parts = [`page ${page.kind}`, page.title, page.path];
|
|
6125
|
+
appendIfMissing(parts, page.sourceType);
|
|
6126
|
+
appendIfMissing(parts, page.sourceClass);
|
|
6127
|
+
appendIfMissing(parts, pageContents.get(page.id)?.slice(0, 1200));
|
|
6128
|
+
return parts.join("\n");
|
|
6129
|
+
}
|
|
6130
|
+
function itemTextForHyperedge(graph, hyperedgeId) {
|
|
6131
|
+
const hyperedge = graph.hyperedges.find((candidate) => candidate.id === hyperedgeId);
|
|
6132
|
+
if (!hyperedge) {
|
|
6133
|
+
return "";
|
|
6134
|
+
}
|
|
6135
|
+
const nodeLabels = hyperedge.nodeIds.map((nodeId) => graph.nodes.find((node) => node.id === nodeId)?.label).filter((value) => Boolean(value));
|
|
6136
|
+
return [hyperedge.label, hyperedge.relation, hyperedge.why, ...nodeLabels].join("\n");
|
|
6137
|
+
}
|
|
6138
|
+
async function buildEmbeddableItems(rootDir, graph) {
|
|
6139
|
+
const pageContents = await loadPageContents(rootDir, graph);
|
|
6140
|
+
return uniqueBy(
|
|
6141
|
+
[
|
|
6142
|
+
...graph.nodes.map(
|
|
6143
|
+
(node) => ({
|
|
6144
|
+
id: node.id,
|
|
6145
|
+
kind: "node",
|
|
6146
|
+
label: node.label,
|
|
6147
|
+
text: itemTextForNode(node, graph, pageContents),
|
|
6148
|
+
match: {
|
|
6149
|
+
type: "node",
|
|
6150
|
+
id: node.id,
|
|
6151
|
+
label: node.label,
|
|
6152
|
+
score: 0
|
|
6153
|
+
}
|
|
6154
|
+
})
|
|
6155
|
+
),
|
|
6156
|
+
...graph.pages.map(
|
|
6157
|
+
(page) => ({
|
|
6158
|
+
id: page.id,
|
|
6159
|
+
kind: "page",
|
|
6160
|
+
label: page.title,
|
|
6161
|
+
text: itemTextForPage(page, pageContents),
|
|
6162
|
+
match: {
|
|
6163
|
+
type: "page",
|
|
6164
|
+
id: page.id,
|
|
6165
|
+
label: page.title,
|
|
6166
|
+
score: 0
|
|
6167
|
+
}
|
|
6168
|
+
})
|
|
6169
|
+
),
|
|
6170
|
+
...(graph.hyperedges ?? []).map(
|
|
6171
|
+
(hyperedge) => ({
|
|
6172
|
+
id: hyperedge.id,
|
|
6173
|
+
kind: "hyperedge",
|
|
6174
|
+
label: hyperedge.label,
|
|
6175
|
+
text: itemTextForHyperedge(graph, hyperedge.id),
|
|
6176
|
+
match: {
|
|
6177
|
+
type: "hyperedge",
|
|
6178
|
+
id: hyperedge.id,
|
|
6179
|
+
label: hyperedge.label,
|
|
6180
|
+
score: 0
|
|
6181
|
+
}
|
|
6182
|
+
})
|
|
6183
|
+
)
|
|
6184
|
+
],
|
|
6185
|
+
(item) => `${item.kind}:${item.id}`
|
|
6186
|
+
).filter((item) => item.text.trim().length > 0);
|
|
6187
|
+
}
|
|
6188
|
+
async function resolveEmbeddingProvider(rootDir) {
|
|
6189
|
+
const { config } = await loadVaultConfig(rootDir);
|
|
6190
|
+
const explicitProviderId = config.tasks.embeddingProvider;
|
|
6191
|
+
if (explicitProviderId) {
|
|
6192
|
+
const providerConfig = config.providers[explicitProviderId];
|
|
6193
|
+
if (!providerConfig) {
|
|
6194
|
+
throw new Error(`No provider configured with id "${explicitProviderId}" for task "embeddingProvider".`);
|
|
6195
|
+
}
|
|
6196
|
+
const provider2 = await createProvider(explicitProviderId, providerConfig, rootDir);
|
|
6197
|
+
if (!provider2.capabilities.has("embeddings") || typeof provider2.embedTexts !== "function") {
|
|
6198
|
+
throw new Error(`Provider ${provider2.id} does not support required capability "embeddings".`);
|
|
6199
|
+
}
|
|
6200
|
+
return provider2;
|
|
6201
|
+
}
|
|
6202
|
+
const queryProviderId = config.tasks.queryProvider;
|
|
6203
|
+
const queryProviderConfig = config.providers[queryProviderId];
|
|
6204
|
+
if (!queryProviderConfig) {
|
|
6205
|
+
return null;
|
|
6206
|
+
}
|
|
6207
|
+
const provider = await createProvider(queryProviderId, queryProviderConfig, rootDir);
|
|
6208
|
+
return provider.capabilities.has("embeddings") && typeof provider.embedTexts === "function" ? provider : null;
|
|
6209
|
+
}
|
|
6210
|
+
async function readEmbeddingCache(rootDir) {
|
|
6211
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6212
|
+
const provider = await resolveEmbeddingProvider(rootDir);
|
|
6213
|
+
if (!provider) {
|
|
6214
|
+
return { artifact: null, provider: null };
|
|
6215
|
+
}
|
|
6216
|
+
const cache = await readJsonFile(paths.embeddingsPath);
|
|
6217
|
+
if (!cache || cache.providerId !== provider.id || cache.providerModel !== provider.model) {
|
|
6218
|
+
return { artifact: null, provider };
|
|
6219
|
+
}
|
|
6220
|
+
return { artifact: cache, provider };
|
|
6221
|
+
}
|
|
6222
|
+
async function writeEmbeddingCache(rootDir, provider, graphHash2, entries) {
|
|
6223
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6224
|
+
await writeJsonFile(paths.embeddingsPath, {
|
|
6225
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6226
|
+
providerId: provider.id,
|
|
6227
|
+
providerModel: provider.model,
|
|
6228
|
+
graphHash: graphHash2,
|
|
6229
|
+
entries: entries.sort((left, right) => `${left.kind}:${left.itemId}`.localeCompare(`${right.kind}:${right.itemId}`))
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
async function embedTexts(provider, texts) {
|
|
6233
|
+
const vectors = [];
|
|
6234
|
+
for (let index = 0; index < texts.length; index += MAX_EMBEDDING_BATCH) {
|
|
6235
|
+
const batch = texts.slice(index, index + MAX_EMBEDDING_BATCH);
|
|
6236
|
+
const nextVectors = await provider.embedTexts(batch);
|
|
6237
|
+
vectors.push(...nextVectors);
|
|
6238
|
+
}
|
|
6239
|
+
return vectors;
|
|
6240
|
+
}
|
|
6241
|
+
async function resolveVectorsForItems(rootDir, graphHash2, items) {
|
|
6242
|
+
const { artifact, provider } = await readEmbeddingCache(rootDir);
|
|
6243
|
+
if (!provider) {
|
|
6244
|
+
return { provider: null, vectors: /* @__PURE__ */ new Map() };
|
|
6245
|
+
}
|
|
6246
|
+
const cachedByKey = new Map(
|
|
6247
|
+
(artifact?.entries ?? []).map((entry) => [`${entry.kind}:${entry.itemId}:${entry.contentHash}`, entry.values])
|
|
6248
|
+
);
|
|
6249
|
+
const vectors = /* @__PURE__ */ new Map();
|
|
6250
|
+
const missing = [];
|
|
6251
|
+
for (const item of items) {
|
|
6252
|
+
const contentHash = sha256(item.text);
|
|
6253
|
+
const cached = cachedByKey.get(`${item.kind}:${item.id}:${contentHash}`);
|
|
6254
|
+
if (cached?.length) {
|
|
6255
|
+
vectors.set(`${item.kind}:${item.id}`, cached);
|
|
6256
|
+
} else {
|
|
6257
|
+
missing.push(item);
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
if (missing.length) {
|
|
6261
|
+
const nextVectors = await embedTexts(
|
|
6262
|
+
provider,
|
|
6263
|
+
missing.map((item) => item.text)
|
|
6264
|
+
);
|
|
6265
|
+
for (let index = 0; index < missing.length; index += 1) {
|
|
6266
|
+
vectors.set(`${missing[index].kind}:${missing[index].id}`, nextVectors[index] ?? []);
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6269
|
+
await writeEmbeddingCache(
|
|
6270
|
+
rootDir,
|
|
6271
|
+
provider,
|
|
6272
|
+
graphHash2,
|
|
6273
|
+
items.map((item) => ({
|
|
6274
|
+
itemId: item.id,
|
|
6275
|
+
kind: item.kind,
|
|
6276
|
+
label: item.label,
|
|
6277
|
+
contentHash: sha256(item.text),
|
|
6278
|
+
values: vectors.get(`${item.kind}:${item.id}`) ?? []
|
|
6279
|
+
}))
|
|
6280
|
+
);
|
|
6281
|
+
return { provider, vectors };
|
|
6282
|
+
}
|
|
6283
|
+
async function semanticGraphMatches(rootDir, graph, question, limit = 12) {
|
|
6284
|
+
const items = await buildEmbeddableItems(rootDir, graph);
|
|
6285
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
6286
|
+
if (!provider) {
|
|
6287
|
+
return [];
|
|
6288
|
+
}
|
|
6289
|
+
const [queryVector] = await provider.embedTexts([question]);
|
|
6290
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) {
|
|
6291
|
+
return [];
|
|
6292
|
+
}
|
|
6293
|
+
return items.map((item) => ({
|
|
6294
|
+
...item.match,
|
|
6295
|
+
score: Math.max(0, Number((cosineSimilarity(queryVector, vectors.get(`${item.kind}:${item.id}`) ?? []) * 100).toFixed(2)))
|
|
6296
|
+
})).filter((match) => match.score >= 18).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, limit);
|
|
6297
|
+
}
|
|
6298
|
+
function distinctScope(left, right) {
|
|
6299
|
+
const leftSources = new Set(left.sourceIds);
|
|
6300
|
+
const rightSources = new Set(right.sourceIds);
|
|
6301
|
+
return [...leftSources].some((sourceId) => !rightSources.has(sourceId)) || [...rightSources].some((sourceId) => !leftSources.has(sourceId));
|
|
6302
|
+
}
|
|
6303
|
+
function nodePairKey(left, right) {
|
|
6304
|
+
return [left, right].sort((a, b) => a.localeCompare(b)).join("|");
|
|
6305
|
+
}
|
|
6306
|
+
function similarityReasonsForNodes(left, right) {
|
|
6307
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
6308
|
+
if (left.sourceClass && right.sourceClass && left.sourceClass === right.sourceClass) {
|
|
6309
|
+
reasons.add("shared_tag");
|
|
6310
|
+
}
|
|
6311
|
+
if (left.language && right.language && left.language === right.language) {
|
|
6312
|
+
reasons.add("shared_symbol");
|
|
6313
|
+
}
|
|
6314
|
+
return [...reasons].sort((a, b) => a.localeCompare(b));
|
|
6315
|
+
}
|
|
6316
|
+
async function embeddingSimilarityEdges(rootDir, graph) {
|
|
6317
|
+
const candidateNodes = graph.nodes.filter(
|
|
6318
|
+
(node) => (node.type === "source" || node.type === "module" || node.type === "rationale") && node.sourceClass !== "generated"
|
|
6319
|
+
);
|
|
6320
|
+
if (candidateNodes.length < 2 || candidateNodes.length > MAX_SIMILARITY_NODES) {
|
|
6321
|
+
return [];
|
|
6322
|
+
}
|
|
6323
|
+
const items = candidateNodes.map(
|
|
6324
|
+
(node) => ({
|
|
6325
|
+
id: node.id,
|
|
6326
|
+
kind: "node",
|
|
6327
|
+
label: node.label,
|
|
6328
|
+
text: [
|
|
6329
|
+
node.label,
|
|
6330
|
+
node.type,
|
|
6331
|
+
node.sourceClass ?? "",
|
|
6332
|
+
node.language ?? "",
|
|
6333
|
+
graph.pages.find((page) => page.id === node.pageId)?.title ?? ""
|
|
6334
|
+
].filter(Boolean).join("\n"),
|
|
6335
|
+
match: { type: "node", id: node.id, label: node.label, score: 0 }
|
|
6336
|
+
})
|
|
6337
|
+
);
|
|
6338
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
6339
|
+
if (!provider) {
|
|
6340
|
+
return [];
|
|
6341
|
+
}
|
|
6342
|
+
const directPairs = new Set(graph.edges.map((edge) => nodePairKey(edge.source, edge.target)));
|
|
6343
|
+
const edges = [];
|
|
6344
|
+
for (let leftIndex = 0; leftIndex < candidateNodes.length; leftIndex += 1) {
|
|
6345
|
+
const left = candidateNodes[leftIndex];
|
|
6346
|
+
const leftVector = vectors.get(`node:${left.id}`) ?? [];
|
|
6347
|
+
const candidates = candidateNodes.slice(leftIndex + 1).filter((right) => distinctScope(left, right) && !directPairs.has(nodePairKey(left.id, right.id))).map((right) => ({
|
|
6348
|
+
right,
|
|
6349
|
+
score: cosineSimilarity(leftVector, vectors.get(`node:${right.id}`) ?? [])
|
|
6350
|
+
})).filter((candidate) => candidate.score >= 0.82).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
6351
|
+
for (const candidate of candidates) {
|
|
6352
|
+
const right = candidate.right;
|
|
6353
|
+
const reasons = similarityReasonsForNodes(left, right) ?? [];
|
|
6354
|
+
edges.push({
|
|
6355
|
+
id: `similar-embed:${sha256(`${left.id}|${right.id}|${provider.id}`).slice(0, 16)}`,
|
|
6356
|
+
source: left.id,
|
|
6357
|
+
target: right.id,
|
|
6358
|
+
relation: "semantically_similar_to",
|
|
6359
|
+
status: "inferred",
|
|
6360
|
+
evidenceClass: "inferred",
|
|
6361
|
+
confidence: Number(candidate.score.toFixed(3)),
|
|
6362
|
+
provenance: uniqueBy(
|
|
6363
|
+
[...left.sourceIds, ...right.sourceIds].sort((a, b) => a.localeCompare(b)),
|
|
6364
|
+
(value) => value
|
|
6365
|
+
),
|
|
6366
|
+
similarityReasons: reasons.length ? reasons : ["shared_tag"],
|
|
6367
|
+
similarityBasis: "embeddings"
|
|
6368
|
+
});
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
6371
|
+
return uniqueBy(edges, (edge) => edge.id).sort((left, right) => right.confidence - left.confidence || left.id.localeCompare(right.id));
|
|
6372
|
+
}
|
|
6373
|
+
function sourceClassBreakdown(graph) {
|
|
6374
|
+
return {
|
|
6375
|
+
first_party: {
|
|
6376
|
+
sources: graph.sources.filter((source) => source.sourceClass === "first_party").length,
|
|
6377
|
+
pages: graph.pages.filter((page) => page.sourceClass === "first_party").length,
|
|
6378
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "first_party").length
|
|
6379
|
+
},
|
|
6380
|
+
third_party: {
|
|
6381
|
+
sources: graph.sources.filter((source) => source.sourceClass === "third_party").length,
|
|
6382
|
+
pages: graph.pages.filter((page) => page.sourceClass === "third_party").length,
|
|
6383
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "third_party").length
|
|
6384
|
+
},
|
|
6385
|
+
resource: {
|
|
6386
|
+
sources: graph.sources.filter((source) => source.sourceClass === "resource").length,
|
|
6387
|
+
pages: graph.pages.filter((page) => page.sourceClass === "resource").length,
|
|
6388
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "resource").length
|
|
6389
|
+
},
|
|
6390
|
+
generated: {
|
|
6391
|
+
sources: graph.sources.filter((source) => source.sourceClass === "generated").length,
|
|
6392
|
+
pages: graph.pages.filter((page) => page.sourceClass === "generated").length,
|
|
6393
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "generated").length
|
|
6394
|
+
}
|
|
6395
|
+
};
|
|
6396
|
+
}
|
|
6397
|
+
function filterGraphBySourceClass(graph, sourceClass) {
|
|
6398
|
+
const nodeIds = new Set(graph.nodes.filter((node) => node.sourceClass === sourceClass).map((node) => node.id));
|
|
6399
|
+
const pageIds = new Set(graph.pages.filter((page) => page.sourceClass === sourceClass).map((page) => page.id));
|
|
6400
|
+
return {
|
|
6401
|
+
...graph,
|
|
6402
|
+
nodes: graph.nodes.filter((node) => nodeIds.has(node.id)),
|
|
6403
|
+
edges: graph.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)),
|
|
6404
|
+
hyperedges: graph.hyperedges.filter((hyperedge) => hyperedge.nodeIds.every((nodeId) => nodeIds.has(nodeId))),
|
|
6405
|
+
communities: (graph.communities ?? []).map((community) => ({
|
|
6406
|
+
...community,
|
|
6407
|
+
nodeIds: community.nodeIds.filter((nodeId) => nodeIds.has(nodeId))
|
|
6408
|
+
})).filter((community) => community.nodeIds.length > 0),
|
|
6409
|
+
sources: graph.sources.filter((source) => source.sourceClass === sourceClass),
|
|
6410
|
+
pages: graph.pages.filter((page) => pageIds.has(page.id))
|
|
6411
|
+
};
|
|
6412
|
+
}
|
|
6413
|
+
|
|
5981
6414
|
// src/graph-enrichment.ts
|
|
5982
6415
|
var STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
5983
6416
|
"about",
|
|
@@ -6204,7 +6637,8 @@ function buildSemanticSimilarityEdges(nodes, edges, manifests, analyses) {
|
|
|
6204
6637
|
[...left.sourceIds, ...right.sourceIds].sort((a, b) => a.localeCompare(b)),
|
|
6205
6638
|
(value) => value
|
|
6206
6639
|
),
|
|
6207
|
-
similarityReasons: [...reasons.keys()].sort((a, b) => a.localeCompare(b))
|
|
6640
|
+
similarityReasons: [...reasons.keys()].sort((a, b) => a.localeCompare(b)),
|
|
6641
|
+
similarityBasis: "feature_overlap"
|
|
6208
6642
|
}
|
|
6209
6643
|
];
|
|
6210
6644
|
}).sort((left, right) => right.confidence - left.confidence || left.id.localeCompare(right.id));
|
|
@@ -6287,9 +6721,11 @@ function buildModuleFormHyperedges(graph) {
|
|
|
6287
6721
|
];
|
|
6288
6722
|
});
|
|
6289
6723
|
}
|
|
6290
|
-
function enrichGraph(graph, manifests, analyses) {
|
|
6724
|
+
function enrichGraph(graph, manifests, analyses, extraSimilarityEdges = []) {
|
|
6291
6725
|
const similarityEdges = buildSemanticSimilarityEdges(graph.nodes, graph.edges, manifests, analyses);
|
|
6292
|
-
const enrichedEdges = [...graph.edges, ...similarityEdges]
|
|
6726
|
+
const enrichedEdges = uniqueBy([...graph.edges, ...similarityEdges, ...extraSimilarityEdges], (edge) => edge.id).sort(
|
|
6727
|
+
(left, right) => left.id.localeCompare(right.id)
|
|
6728
|
+
);
|
|
6293
6729
|
const hyperedges = uniqueBy(
|
|
6294
6730
|
[
|
|
6295
6731
|
...buildTopicHyperedges({ ...graph, edges: enrichedEdges, hyperedges: [] }),
|
|
@@ -6420,13 +6856,19 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6420
6856
|
const traversal = options?.traversal ?? "bfs";
|
|
6421
6857
|
const budget = Math.max(3, Math.min(options?.budget ?? 12, 50));
|
|
6422
6858
|
const matches = uniqueBy(
|
|
6423
|
-
[
|
|
6859
|
+
[
|
|
6860
|
+
...options?.semanticMatches ?? [],
|
|
6861
|
+
...pageSearchMatches(graph, question, searchResults),
|
|
6862
|
+
...nodeMatches(graph, question),
|
|
6863
|
+
...hyperedgeMatches(graph, question)
|
|
6864
|
+
],
|
|
6424
6865
|
(match) => `${match.type}:${match.id}`
|
|
6425
6866
|
).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, 12);
|
|
6426
6867
|
const pages = pageById(graph);
|
|
6427
6868
|
const seeds = uniqueBy(
|
|
6428
6869
|
[
|
|
6429
6870
|
...searchResults.flatMap((result) => pages.get(result.pageId)?.nodeIds ?? []),
|
|
6871
|
+
...matches.filter((match) => match.type === "page").flatMap((match) => pages.get(match.id)?.nodeIds ?? []),
|
|
6430
6872
|
...matches.filter((match) => match.type === "node").map((match) => match.id),
|
|
6431
6873
|
...matches.filter((match) => match.type === "hyperedge").flatMap((match) => graph.hyperedges.find((hyperedge) => hyperedge.id === match.id)?.nodeIds ?? [])
|
|
6432
6874
|
],
|
|
@@ -6458,6 +6900,7 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6458
6900
|
const pageIds = uniqueBy(
|
|
6459
6901
|
[
|
|
6460
6902
|
...searchResults.map((result) => result.pageId),
|
|
6903
|
+
...matches.filter((match) => match.type === "page").map((match) => match.id),
|
|
6461
6904
|
...visitedNodeIds.flatMap((nodeId) => {
|
|
6462
6905
|
const node = nodes.get(nodeId);
|
|
6463
6906
|
return node?.pageId ? [node.pageId] : [];
|
|
@@ -6478,7 +6921,7 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6478
6921
|
traversal,
|
|
6479
6922
|
seedNodeIds: seeds,
|
|
6480
6923
|
seedPageIds: uniqueBy(
|
|
6481
|
-
searchResults.map((result) => result.pageId),
|
|
6924
|
+
[...searchResults.map((result) => result.pageId), ...matches.filter((match) => match.type === "page").map((match) => match.id)],
|
|
6482
6925
|
(item) => item
|
|
6483
6926
|
),
|
|
6484
6927
|
visitedNodeIds,
|
|
@@ -6729,6 +7172,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6729
7172
|
kind: "source",
|
|
6730
7173
|
title: analysis.title,
|
|
6731
7174
|
...manifest.sourceType ? { source_type: manifest.sourceType } : {},
|
|
7175
|
+
...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
|
|
6732
7176
|
tags: decoratedTags(analysis.code ? ["source", "code"] : ["source"], decorations),
|
|
6733
7177
|
source_ids: [manifest.sourceId],
|
|
6734
7178
|
project_ids: decorations?.projectIds ?? [],
|
|
@@ -6752,6 +7196,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6752
7196
|
`Source ID: \`${manifest.sourceId}\``,
|
|
6753
7197
|
manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
6754
7198
|
...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
|
|
7199
|
+
...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
|
|
6755
7200
|
"",
|
|
6756
7201
|
"## Summary",
|
|
6757
7202
|
"",
|
|
@@ -6797,6 +7242,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6797
7242
|
title: analysis.title,
|
|
6798
7243
|
kind: "source",
|
|
6799
7244
|
sourceType: manifest.sourceType,
|
|
7245
|
+
sourceClass: manifest.sourceClass,
|
|
6800
7246
|
sourceIds: [manifest.sourceId],
|
|
6801
7247
|
projectIds: decorations?.projectIds ?? [],
|
|
6802
7248
|
nodeIds,
|
|
@@ -6859,6 +7305,7 @@ function buildModulePage(input) {
|
|
|
6859
7305
|
page_id: pageId,
|
|
6860
7306
|
kind: "module",
|
|
6861
7307
|
title,
|
|
7308
|
+
...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
|
|
6862
7309
|
tags: decoratedTags(["module", "code", code.language], { projectIds: input.projectIds, extraTags: input.extraTags }),
|
|
6863
7310
|
source_ids: [manifest.sourceId],
|
|
6864
7311
|
project_ids: input.projectIds ?? [],
|
|
@@ -6890,6 +7337,7 @@ function buildModulePage(input) {
|
|
|
6890
7337
|
`Source ID: \`${manifest.sourceId}\``,
|
|
6891
7338
|
`Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
6892
7339
|
...manifest.repoRelativePath ? [`Repo Path: \`${manifest.repoRelativePath}\``] : [],
|
|
7340
|
+
...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``] : [],
|
|
6893
7341
|
`Language: \`${code.language}\``,
|
|
6894
7342
|
...code.moduleName ? [`Module Name: \`${code.moduleName}\``] : [],
|
|
6895
7343
|
...code.namespace ? [`Namespace/Package: \`${code.namespace}\``] : [],
|
|
@@ -6940,6 +7388,7 @@ function buildModulePage(input) {
|
|
|
6940
7388
|
path: relativePath,
|
|
6941
7389
|
title,
|
|
6942
7390
|
kind: "module",
|
|
7391
|
+
sourceClass: manifest.sourceClass,
|
|
6943
7392
|
sourceIds: [manifest.sourceId],
|
|
6944
7393
|
projectIds: input.projectIds ?? [],
|
|
6945
7394
|
nodeIds,
|
|
@@ -6974,6 +7423,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
|
|
|
6974
7423
|
page_id: pageId,
|
|
6975
7424
|
kind,
|
|
6976
7425
|
title: name,
|
|
7426
|
+
...decorations?.sourceClass ? { source_class: decorations.sourceClass } : {},
|
|
6977
7427
|
tags: decoratedTags(metadata.status === "candidate" ? [kind, "candidate"] : [kind], decorations),
|
|
6978
7428
|
source_ids: sourceIds,
|
|
6979
7429
|
project_ids: decorations?.projectIds ?? [],
|
|
@@ -7015,6 +7465,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
|
|
|
7015
7465
|
path: relativePath,
|
|
7016
7466
|
title: name,
|
|
7017
7467
|
kind,
|
|
7468
|
+
sourceClass: decorations?.sourceClass,
|
|
7018
7469
|
sourceIds,
|
|
7019
7470
|
projectIds: decorations?.projectIds ?? [],
|
|
7020
7471
|
nodeIds: [pageId],
|
|
@@ -7146,15 +7597,15 @@ function sourceTypeForNode(node, pagesById) {
|
|
|
7146
7597
|
return pagesById.get(node.pageId)?.sourceType;
|
|
7147
7598
|
}
|
|
7148
7599
|
function supportingPathDetails(graph, edge) {
|
|
7149
|
-
const
|
|
7600
|
+
const path25 = shortestGraphPath(graph, edge.source, edge.target);
|
|
7150
7601
|
const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
|
|
7151
|
-
const pathEdges =
|
|
7602
|
+
const pathEdges = path25.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
|
|
7152
7603
|
return {
|
|
7153
|
-
pathNodeIds:
|
|
7154
|
-
pathEdgeIds:
|
|
7604
|
+
pathNodeIds: path25.nodeIds,
|
|
7605
|
+
pathEdgeIds: path25.edgeIds,
|
|
7155
7606
|
pathRelations: pathEdges.map((item) => item.relation),
|
|
7156
7607
|
pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
|
|
7157
|
-
pathSummary:
|
|
7608
|
+
pathSummary: path25.summary
|
|
7158
7609
|
};
|
|
7159
7610
|
}
|
|
7160
7611
|
function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
|
|
@@ -7223,7 +7674,7 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
7223
7674
|
}).map((edge) => {
|
|
7224
7675
|
const source = nodesById.get(edge.source);
|
|
7225
7676
|
const target = nodesById.get(edge.target);
|
|
7226
|
-
const
|
|
7677
|
+
const path25 = supportingPathDetails(graph, edge);
|
|
7227
7678
|
const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
|
|
7228
7679
|
return {
|
|
7229
7680
|
id: edge.id,
|
|
@@ -7234,11 +7685,11 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
7234
7685
|
relation: edge.relation,
|
|
7235
7686
|
evidenceClass: edge.evidenceClass,
|
|
7236
7687
|
confidence: edge.confidence,
|
|
7237
|
-
pathNodeIds:
|
|
7238
|
-
pathEdgeIds:
|
|
7239
|
-
pathRelations:
|
|
7240
|
-
pathEvidenceClasses:
|
|
7241
|
-
pathSummary:
|
|
7688
|
+
pathNodeIds: path25.pathNodeIds,
|
|
7689
|
+
pathEdgeIds: path25.pathEdgeIds,
|
|
7690
|
+
pathRelations: path25.pathRelations,
|
|
7691
|
+
pathEvidenceClasses: path25.pathEvidenceClasses,
|
|
7692
|
+
pathSummary: path25.pathSummary,
|
|
7242
7693
|
why: scored.why,
|
|
7243
7694
|
explanation: scored.explanation,
|
|
7244
7695
|
surpriseScore: scored.score
|
|
@@ -7263,10 +7714,12 @@ function suggestedGraphQuestions(graph) {
|
|
|
7263
7714
|
]).slice(0, 6);
|
|
7264
7715
|
}
|
|
7265
7716
|
function buildGraphReportArtifact(input) {
|
|
7266
|
-
const
|
|
7267
|
-
const
|
|
7268
|
-
const
|
|
7269
|
-
const
|
|
7717
|
+
const firstPartyGraph = filterGraphBySourceClass(input.graph, "first_party");
|
|
7718
|
+
const reportGraph = firstPartyGraph.nodes.length ? firstPartyGraph : input.graph;
|
|
7719
|
+
const pagesById = new Map(reportGraph.pages.map((page) => [page.id, page]));
|
|
7720
|
+
const godNodes = reportGraph.nodes.filter((node) => node.isGodNode).sort((left, right) => (right.degree ?? 0) - (left.degree ?? 0)).slice(0, 8);
|
|
7721
|
+
const bridgeNodes = reportGraph.nodes.filter((node) => (node.bridgeScore ?? 0) > 0).sort((left, right) => (right.bridgeScore ?? 0) - (left.bridgeScore ?? 0)).slice(0, 8);
|
|
7722
|
+
const thinCommunities = (reportGraph.communities ?? []).filter((community) => community.nodeIds.length <= 2).map((community) => {
|
|
7270
7723
|
const page = input.communityPages.find((candidate) => candidate.id === `graph:${community.id}`);
|
|
7271
7724
|
return {
|
|
7272
7725
|
id: community.id,
|
|
@@ -7277,8 +7730,19 @@ function buildGraphReportArtifact(input) {
|
|
|
7277
7730
|
title: page?.title
|
|
7278
7731
|
};
|
|
7279
7732
|
});
|
|
7280
|
-
const surprisingConnections = topSurprisingConnections(
|
|
7281
|
-
const groupPatterns = topGroupPatterns(
|
|
7733
|
+
const surprisingConnections = topSurprisingConnections(reportGraph, pagesById);
|
|
7734
|
+
const groupPatterns = topGroupPatterns(reportGraph);
|
|
7735
|
+
const breakdown = sourceClassBreakdown(input.graph);
|
|
7736
|
+
const warnings = [];
|
|
7737
|
+
const nonFirstPartyNodes = input.graph.nodes.length - breakdown.first_party.nodes;
|
|
7738
|
+
if (input.graph.nodes.length >= 1200) {
|
|
7739
|
+
warnings.push(`Large graph detected (${input.graph.nodes.length} nodes). First-party defaults are applied to report highlights.`);
|
|
7740
|
+
}
|
|
7741
|
+
if (nonFirstPartyNodes > 0 && nonFirstPartyNodes / Math.max(1, input.graph.nodes.length) >= 0.25) {
|
|
7742
|
+
warnings.push(
|
|
7743
|
+
`Non-first-party material accounts for ${(nonFirstPartyNodes / Math.max(1, input.graph.nodes.length) * 100).toFixed(1)}% of graph nodes.`
|
|
7744
|
+
);
|
|
7745
|
+
}
|
|
7282
7746
|
return {
|
|
7283
7747
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7284
7748
|
graphHash: input.graphHash,
|
|
@@ -7288,6 +7752,14 @@ function buildGraphReportArtifact(input) {
|
|
|
7288
7752
|
pages: input.graph.pages.length,
|
|
7289
7753
|
communities: input.graph.communities?.length ?? 0
|
|
7290
7754
|
},
|
|
7755
|
+
firstPartyOverview: {
|
|
7756
|
+
nodes: reportGraph.nodes.length,
|
|
7757
|
+
edges: reportGraph.edges.length,
|
|
7758
|
+
pages: reportGraph.pages.length,
|
|
7759
|
+
communities: reportGraph.communities?.length ?? 0
|
|
7760
|
+
},
|
|
7761
|
+
sourceClassBreakdown: breakdown,
|
|
7762
|
+
warnings,
|
|
7291
7763
|
benchmark: input.benchmark ? {
|
|
7292
7764
|
generatedAt: input.benchmark.generatedAt,
|
|
7293
7765
|
stale: input.benchmarkStale ?? false,
|
|
@@ -7383,6 +7855,18 @@ function buildGraphReportPage(input) {
|
|
|
7383
7855
|
`- Edges: ${input.report.overview.edges}`,
|
|
7384
7856
|
`- Pages: ${input.report.overview.pages}`,
|
|
7385
7857
|
`- Communities: ${input.report.overview.communities}`,
|
|
7858
|
+
`- Default Focus: First-party nodes/pages (${input.report.firstPartyOverview.nodes} nodes, ${input.report.firstPartyOverview.edges} edges, ${input.report.firstPartyOverview.pages} pages).`,
|
|
7859
|
+
"",
|
|
7860
|
+
"## Repo Quality Warnings",
|
|
7861
|
+
"",
|
|
7862
|
+
...input.report.warnings.length ? input.report.warnings.map((warning) => `- ${warning}`) : ["- No large-repo warnings."],
|
|
7863
|
+
"",
|
|
7864
|
+
"## Source Class Breakdown",
|
|
7865
|
+
"",
|
|
7866
|
+
`- First-party: ${input.report.sourceClassBreakdown.first_party.sources} sources, ${input.report.sourceClassBreakdown.first_party.pages} pages, ${input.report.sourceClassBreakdown.first_party.nodes} nodes`,
|
|
7867
|
+
`- Third-party: ${input.report.sourceClassBreakdown.third_party.sources} sources, ${input.report.sourceClassBreakdown.third_party.pages} pages, ${input.report.sourceClassBreakdown.third_party.nodes} nodes`,
|
|
7868
|
+
`- Resources: ${input.report.sourceClassBreakdown.resource.sources} sources, ${input.report.sourceClassBreakdown.resource.pages} pages, ${input.report.sourceClassBreakdown.resource.nodes} nodes`,
|
|
7869
|
+
`- Generated: ${input.report.sourceClassBreakdown.generated.sources} sources, ${input.report.sourceClassBreakdown.generated.pages} pages, ${input.report.sourceClassBreakdown.generated.nodes} nodes`,
|
|
7386
7870
|
"",
|
|
7387
7871
|
"## Benchmark Summary",
|
|
7388
7872
|
"",
|
|
@@ -8090,13 +8574,13 @@ function buildOutputAssetManifest(input) {
|
|
|
8090
8574
|
}
|
|
8091
8575
|
|
|
8092
8576
|
// src/outputs.ts
|
|
8093
|
-
import
|
|
8094
|
-
import
|
|
8577
|
+
import fs14 from "fs/promises";
|
|
8578
|
+
import path18 from "path";
|
|
8095
8579
|
import matter7 from "gray-matter";
|
|
8096
8580
|
|
|
8097
8581
|
// src/pages.ts
|
|
8098
|
-
import
|
|
8099
|
-
import
|
|
8582
|
+
import fs13 from "fs/promises";
|
|
8583
|
+
import path17 from "path";
|
|
8100
8584
|
import matter6 from "gray-matter";
|
|
8101
8585
|
function normalizeStringArray(value) {
|
|
8102
8586
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -8121,6 +8605,9 @@ function normalizePageManager(value, fallback = "system") {
|
|
|
8121
8605
|
function normalizeSourceType(value) {
|
|
8122
8606
|
return value === "arxiv" || value === "doi" || value === "tweet" || value === "article" || value === "url" ? value : void 0;
|
|
8123
8607
|
}
|
|
8608
|
+
function normalizeSourceClass(value) {
|
|
8609
|
+
return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
|
|
8610
|
+
}
|
|
8124
8611
|
function normalizeOutputFormat(value, fallback = "markdown") {
|
|
8125
8612
|
return value === "report" || value === "slides" || value === "chart" || value === "image" ? value : fallback;
|
|
8126
8613
|
}
|
|
@@ -8171,7 +8658,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
8171
8658
|
updatedAt: updatedFallback
|
|
8172
8659
|
};
|
|
8173
8660
|
}
|
|
8174
|
-
const content = await
|
|
8661
|
+
const content = await fs13.readFile(absolutePath, "utf8");
|
|
8175
8662
|
const parsed = matter6(content);
|
|
8176
8663
|
return {
|
|
8177
8664
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
@@ -8210,7 +8697,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8210
8697
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8211
8698
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
8212
8699
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
8213
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8700
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path17.basename(relativePath, ".md");
|
|
8214
8701
|
const kind = inferPageKind(relativePath, parsed.data.kind);
|
|
8215
8702
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
8216
8703
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
@@ -8227,6 +8714,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8227
8714
|
title,
|
|
8228
8715
|
kind,
|
|
8229
8716
|
sourceType: normalizeSourceType(parsed.data.source_type),
|
|
8717
|
+
sourceClass: normalizeSourceClass(parsed.data.source_class),
|
|
8230
8718
|
sourceIds,
|
|
8231
8719
|
projectIds,
|
|
8232
8720
|
nodeIds,
|
|
@@ -8250,18 +8738,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8250
8738
|
};
|
|
8251
8739
|
}
|
|
8252
8740
|
async function loadInsightPages(wikiDir) {
|
|
8253
|
-
const insightsDir =
|
|
8741
|
+
const insightsDir = path17.join(wikiDir, "insights");
|
|
8254
8742
|
if (!await fileExists(insightsDir)) {
|
|
8255
8743
|
return [];
|
|
8256
8744
|
}
|
|
8257
|
-
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) =>
|
|
8745
|
+
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path17.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
|
|
8258
8746
|
const insights = [];
|
|
8259
8747
|
for (const absolutePath of files) {
|
|
8260
|
-
const relativePath = toPosix(
|
|
8261
|
-
const content = await
|
|
8748
|
+
const relativePath = toPosix(path17.relative(wikiDir, absolutePath));
|
|
8749
|
+
const content = await fs13.readFile(absolutePath, "utf8");
|
|
8262
8750
|
const parsed = matter6(content);
|
|
8263
|
-
const stats = await
|
|
8264
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8751
|
+
const stats = await fs13.stat(absolutePath);
|
|
8752
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path17.basename(absolutePath, ".md");
|
|
8265
8753
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
8266
8754
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
8267
8755
|
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
@@ -8279,6 +8767,7 @@ async function loadInsightPages(wikiDir) {
|
|
|
8279
8767
|
path: relativePath,
|
|
8280
8768
|
title,
|
|
8281
8769
|
kind: "insight",
|
|
8770
|
+
sourceClass: normalizeSourceClass(parsed.data.source_class),
|
|
8282
8771
|
sourceIds,
|
|
8283
8772
|
projectIds,
|
|
8284
8773
|
nodeIds,
|
|
@@ -8323,27 +8812,27 @@ function relatedOutputsForPage(targetPage, outputPages) {
|
|
|
8323
8812
|
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);
|
|
8324
8813
|
}
|
|
8325
8814
|
async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
|
|
8326
|
-
const outputsDir =
|
|
8815
|
+
const outputsDir = path18.join(wikiDir, "outputs");
|
|
8327
8816
|
const root = baseSlug || "output";
|
|
8328
8817
|
let candidate = root;
|
|
8329
8818
|
let counter = 2;
|
|
8330
|
-
while (await fileExists(
|
|
8819
|
+
while (await fileExists(path18.join(outputsDir, `${candidate}.md`))) {
|
|
8331
8820
|
candidate = `${root}-${counter}`;
|
|
8332
8821
|
counter++;
|
|
8333
8822
|
}
|
|
8334
8823
|
return candidate;
|
|
8335
8824
|
}
|
|
8336
8825
|
async function loadSavedOutputPages(wikiDir) {
|
|
8337
|
-
const outputsDir =
|
|
8338
|
-
const entries = await
|
|
8826
|
+
const outputsDir = path18.join(wikiDir, "outputs");
|
|
8827
|
+
const entries = await fs14.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
8339
8828
|
const outputs = [];
|
|
8340
8829
|
for (const entry of entries) {
|
|
8341
8830
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
|
|
8342
8831
|
continue;
|
|
8343
8832
|
}
|
|
8344
|
-
const relativePath =
|
|
8345
|
-
const absolutePath =
|
|
8346
|
-
const content = await
|
|
8833
|
+
const relativePath = path18.posix.join("outputs", entry.name);
|
|
8834
|
+
const absolutePath = path18.join(outputsDir, entry.name);
|
|
8835
|
+
const content = await fs14.readFile(absolutePath, "utf8");
|
|
8347
8836
|
const parsed = matter7(content);
|
|
8348
8837
|
const slug = entry.name.replace(/\.md$/, "");
|
|
8349
8838
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : slug;
|
|
@@ -8356,7 +8845,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
8356
8845
|
const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
|
|
8357
8846
|
const backlinks = normalizeStringArray(parsed.data.backlinks);
|
|
8358
8847
|
const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
|
|
8359
|
-
const stats = await
|
|
8848
|
+
const stats = await fs14.stat(absolutePath);
|
|
8360
8849
|
const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
|
|
8361
8850
|
const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
|
|
8362
8851
|
outputs.push({
|
|
@@ -8394,8 +8883,8 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
8394
8883
|
}
|
|
8395
8884
|
|
|
8396
8885
|
// src/search.ts
|
|
8397
|
-
import
|
|
8398
|
-
import
|
|
8886
|
+
import fs15 from "fs/promises";
|
|
8887
|
+
import path19 from "path";
|
|
8399
8888
|
import matter8 from "gray-matter";
|
|
8400
8889
|
function getDatabaseSync() {
|
|
8401
8890
|
const builtin = process.getBuiltinModule?.("node:sqlite");
|
|
@@ -8417,8 +8906,11 @@ function normalizeStatus(value) {
|
|
|
8417
8906
|
function normalizeSourceType2(value) {
|
|
8418
8907
|
return value === "arxiv" || value === "doi" || value === "tweet" || value === "article" || value === "url" ? value : void 0;
|
|
8419
8908
|
}
|
|
8909
|
+
function normalizeSourceClass2(value) {
|
|
8910
|
+
return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
|
|
8911
|
+
}
|
|
8420
8912
|
async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
8421
|
-
await ensureDir(
|
|
8913
|
+
await ensureDir(path19.dirname(dbPath));
|
|
8422
8914
|
const DatabaseSync = getDatabaseSync();
|
|
8423
8915
|
const db = new DatabaseSync(dbPath);
|
|
8424
8916
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -8433,6 +8925,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8433
8925
|
kind TEXT NOT NULL,
|
|
8434
8926
|
status TEXT NOT NULL,
|
|
8435
8927
|
source_type TEXT NOT NULL,
|
|
8928
|
+
source_class TEXT NOT NULL,
|
|
8436
8929
|
project_ids TEXT NOT NULL,
|
|
8437
8930
|
project_key TEXT NOT NULL
|
|
8438
8931
|
);
|
|
@@ -8446,11 +8939,11 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8446
8939
|
DELETE FROM pages;
|
|
8447
8940
|
`);
|
|
8448
8941
|
const insertPage = db.prepare(
|
|
8449
|
-
"INSERT INTO pages (id, path, title, body, kind, status, source_type, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8942
|
+
"INSERT INTO pages (id, path, title, body, kind, status, source_type, source_class, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8450
8943
|
);
|
|
8451
8944
|
for (const page of pages) {
|
|
8452
|
-
const absolutePath =
|
|
8453
|
-
const content = await
|
|
8945
|
+
const absolutePath = path19.join(wikiDir, page.path);
|
|
8946
|
+
const content = await fs15.readFile(absolutePath, "utf8");
|
|
8454
8947
|
const parsed = matter8(content);
|
|
8455
8948
|
insertPage.run(
|
|
8456
8949
|
page.id,
|
|
@@ -8460,6 +8953,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8460
8953
|
page.kind,
|
|
8461
8954
|
page.status,
|
|
8462
8955
|
typeof parsed.data.source_type === "string" ? parsed.data.source_type : "",
|
|
8956
|
+
typeof parsed.data.source_class === "string" ? parsed.data.source_class : "",
|
|
8463
8957
|
JSON.stringify(page.projectIds),
|
|
8464
8958
|
page.projectIds.map((projectId) => `|${projectId}|`).join("")
|
|
8465
8959
|
);
|
|
@@ -8497,6 +8991,10 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8497
8991
|
clauses.push("pages.source_type = ?");
|
|
8498
8992
|
params.push(options.sourceType);
|
|
8499
8993
|
}
|
|
8994
|
+
if (options.sourceClass && options.sourceClass !== "all") {
|
|
8995
|
+
clauses.push("pages.source_class = ?");
|
|
8996
|
+
params.push(options.sourceClass);
|
|
8997
|
+
}
|
|
8500
8998
|
const statement = db.prepare(`
|
|
8501
8999
|
SELECT
|
|
8502
9000
|
pages.id AS pageId,
|
|
@@ -8505,6 +9003,7 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8505
9003
|
pages.kind AS kind,
|
|
8506
9004
|
pages.status AS status,
|
|
8507
9005
|
pages.source_type AS sourceType,
|
|
9006
|
+
pages.source_class AS sourceClass,
|
|
8508
9007
|
pages.project_ids AS projectIds,
|
|
8509
9008
|
snippet(page_search, 1, '[', ']', '...', 16) AS snippet,
|
|
8510
9009
|
bm25(page_search) AS rank
|
|
@@ -8533,6 +9032,7 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8533
9032
|
kind: normalizeKind(row.kind),
|
|
8534
9033
|
status: normalizeStatus(row.status),
|
|
8535
9034
|
sourceType: normalizeSourceType2(row.sourceType),
|
|
9035
|
+
sourceClass: normalizeSourceClass2(row.sourceClass),
|
|
8536
9036
|
snippet: String(row.snippet ?? ""),
|
|
8537
9037
|
rank: Number(row.rank ?? 0)
|
|
8538
9038
|
}));
|
|
@@ -8560,7 +9060,7 @@ function outputFormatInstruction(format) {
|
|
|
8560
9060
|
}
|
|
8561
9061
|
}
|
|
8562
9062
|
function outputAssetPath(slug, fileName) {
|
|
8563
|
-
return toPosix(
|
|
9063
|
+
return toPosix(path20.join("outputs", "assets", slug, fileName));
|
|
8564
9064
|
}
|
|
8565
9065
|
function outputAssetId(slug, role) {
|
|
8566
9066
|
return `output:${slug}:asset:${role}`;
|
|
@@ -8700,7 +9200,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
8700
9200
|
if (!providerConfig) {
|
|
8701
9201
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
8702
9202
|
}
|
|
8703
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
9203
|
+
const { createProvider: createProvider2 } = await import("./registry-YGVTLIZH.js");
|
|
8704
9204
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
8705
9205
|
}
|
|
8706
9206
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -8898,7 +9398,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
8898
9398
|
};
|
|
8899
9399
|
}
|
|
8900
9400
|
function normalizeProjectRoot(root) {
|
|
8901
|
-
const normalized = toPosix(
|
|
9401
|
+
const normalized = toPosix(path20.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
8902
9402
|
return normalized;
|
|
8903
9403
|
}
|
|
8904
9404
|
function projectEntries(config) {
|
|
@@ -8924,10 +9424,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
8924
9424
|
if (!rawPath) {
|
|
8925
9425
|
return toPosix(manifest.storedPath);
|
|
8926
9426
|
}
|
|
8927
|
-
if (!
|
|
9427
|
+
if (!path20.isAbsolute(rawPath)) {
|
|
8928
9428
|
return normalizeProjectRoot(rawPath);
|
|
8929
9429
|
}
|
|
8930
|
-
const relative = toPosix(
|
|
9430
|
+
const relative = toPosix(path20.relative(rootDir, rawPath));
|
|
8931
9431
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
8932
9432
|
}
|
|
8933
9433
|
function prefixMatches(value, prefix) {
|
|
@@ -9101,7 +9601,7 @@ function pageHashes(pages) {
|
|
|
9101
9601
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
9102
9602
|
}
|
|
9103
9603
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
9104
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
9604
|
+
const existingContent = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
9105
9605
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
9106
9606
|
status: defaults.status ?? "active",
|
|
9107
9607
|
managedBy: defaults.managedBy
|
|
@@ -9139,7 +9639,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
9139
9639
|
return built;
|
|
9140
9640
|
}
|
|
9141
9641
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
9142
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
9642
|
+
const existingContent = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
9143
9643
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
9144
9644
|
status: defaults.status ?? "active",
|
|
9145
9645
|
managedBy: defaults.managedBy
|
|
@@ -9258,7 +9758,11 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
9258
9758
|
communities
|
|
9259
9759
|
};
|
|
9260
9760
|
}
|
|
9761
|
+
function resetGraphNodeMetrics(nodes) {
|
|
9762
|
+
return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
|
|
9763
|
+
}
|
|
9261
9764
|
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
9765
|
+
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
9262
9766
|
const sourceNodes = manifests.map((manifest) => ({
|
|
9263
9767
|
id: `source:${manifest.sourceId}`,
|
|
9264
9768
|
type: "source",
|
|
@@ -9268,6 +9772,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9268
9772
|
confidence: 1,
|
|
9269
9773
|
sourceIds: [manifest.sourceId],
|
|
9270
9774
|
projectIds: scopedProjectIdsFromSources([manifest.sourceId], sourceProjects),
|
|
9775
|
+
sourceClass: manifest.sourceClass,
|
|
9271
9776
|
language: manifest.language
|
|
9272
9777
|
}));
|
|
9273
9778
|
const conceptMap = /* @__PURE__ */ new Map();
|
|
@@ -9284,7 +9789,6 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9284
9789
|
edgesById.add(edge.id);
|
|
9285
9790
|
edges.push(edge);
|
|
9286
9791
|
};
|
|
9287
|
-
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
9288
9792
|
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
9289
9793
|
for (const analysis of analyses) {
|
|
9290
9794
|
for (const concept of analysis.concepts) {
|
|
@@ -9298,7 +9802,8 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9298
9802
|
freshness: "fresh",
|
|
9299
9803
|
confidence: nodeConfidence(sourceIds.length),
|
|
9300
9804
|
sourceIds,
|
|
9301
|
-
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects)
|
|
9805
|
+
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects),
|
|
9806
|
+
sourceClass: aggregateManifestSourceClass(manifests, sourceIds)
|
|
9302
9807
|
});
|
|
9303
9808
|
pushEdge({
|
|
9304
9809
|
id: `${analysis.sourceId}->${concept.id}`,
|
|
@@ -9322,7 +9827,8 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9322
9827
|
freshness: "fresh",
|
|
9323
9828
|
confidence: nodeConfidence(sourceIds.length),
|
|
9324
9829
|
sourceIds,
|
|
9325
|
-
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects)
|
|
9830
|
+
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects),
|
|
9831
|
+
sourceClass: aggregateManifestSourceClass(manifests, sourceIds)
|
|
9326
9832
|
});
|
|
9327
9833
|
pushEdge({
|
|
9328
9834
|
id: `${analysis.sourceId}->${entity.id}`,
|
|
@@ -9350,6 +9856,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9350
9856
|
confidence: 1,
|
|
9351
9857
|
sourceIds: [analysis.sourceId],
|
|
9352
9858
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9859
|
+
sourceClass: manifest.sourceClass,
|
|
9353
9860
|
language: analysis.code.language,
|
|
9354
9861
|
moduleId
|
|
9355
9862
|
});
|
|
@@ -9373,6 +9880,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9373
9880
|
confidence: symbol.exported ? 0.88 : 0.74,
|
|
9374
9881
|
sourceIds: [analysis.sourceId],
|
|
9375
9882
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9883
|
+
sourceClass: manifest.sourceClass,
|
|
9376
9884
|
language: analysis.code.language,
|
|
9377
9885
|
moduleId,
|
|
9378
9886
|
symbolKind: symbol.kind
|
|
@@ -9413,6 +9921,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9413
9921
|
confidence: 1,
|
|
9414
9922
|
sourceIds: [analysis.sourceId],
|
|
9415
9923
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9924
|
+
sourceClass: manifest.sourceClass,
|
|
9416
9925
|
language: analysis.code.language,
|
|
9417
9926
|
moduleId
|
|
9418
9927
|
});
|
|
@@ -9608,7 +10117,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9608
10117
|
const benchmark = await readJsonFile(paths.benchmarkPath);
|
|
9609
10118
|
const communityRecords = [];
|
|
9610
10119
|
for (const community of graph.communities ?? []) {
|
|
9611
|
-
const absolutePath =
|
|
10120
|
+
const absolutePath = path20.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
|
|
9612
10121
|
communityRecords.push(
|
|
9613
10122
|
await buildManagedGraphPage(
|
|
9614
10123
|
absolutePath,
|
|
@@ -9636,7 +10145,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9636
10145
|
recentResearchSources: recentResearchSourcePages(graph, previousCompiledAt),
|
|
9637
10146
|
graphHash: graphHash(graph)
|
|
9638
10147
|
});
|
|
9639
|
-
const reportAbsolutePath =
|
|
10148
|
+
const reportAbsolutePath = path20.join(paths.wikiDir, "graph", "report.md");
|
|
9640
10149
|
const reportRecord = await buildManagedGraphPage(
|
|
9641
10150
|
reportAbsolutePath,
|
|
9642
10151
|
{
|
|
@@ -9657,7 +10166,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9657
10166
|
};
|
|
9658
10167
|
}
|
|
9659
10168
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
9660
|
-
const absolutePath =
|
|
10169
|
+
const absolutePath = path20.resolve(wikiDir, relativePath);
|
|
9661
10170
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
9662
10171
|
if (changed) {
|
|
9663
10172
|
changedPages.push(relativePath);
|
|
@@ -9688,6 +10197,7 @@ function emptyGraphPage(input) {
|
|
|
9688
10197
|
path: input.path,
|
|
9689
10198
|
title: input.title,
|
|
9690
10199
|
kind: input.kind,
|
|
10200
|
+
sourceClass: input.sourceClass,
|
|
9691
10201
|
sourceIds: input.sourceIds,
|
|
9692
10202
|
projectIds: input.projectIds ?? [],
|
|
9693
10203
|
nodeIds: input.nodeIds,
|
|
@@ -9719,29 +10229,29 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
9719
10229
|
paths.graphPath,
|
|
9720
10230
|
paths.codeIndexPath,
|
|
9721
10231
|
paths.searchDbPath,
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
10232
|
+
path20.join(paths.wikiDir, "index.md"),
|
|
10233
|
+
path20.join(paths.wikiDir, "sources", "index.md"),
|
|
10234
|
+
path20.join(paths.wikiDir, "code", "index.md"),
|
|
10235
|
+
path20.join(paths.wikiDir, "concepts", "index.md"),
|
|
10236
|
+
path20.join(paths.wikiDir, "entities", "index.md"),
|
|
10237
|
+
path20.join(paths.wikiDir, "outputs", "index.md"),
|
|
10238
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
10239
|
+
path20.join(paths.wikiDir, "candidates", "index.md")
|
|
9730
10240
|
];
|
|
9731
10241
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
9732
10242
|
return checks.every(Boolean);
|
|
9733
10243
|
}
|
|
9734
10244
|
async function loadAvailableCachedAnalyses(paths, manifests) {
|
|
9735
10245
|
const analyses = await Promise.all(
|
|
9736
|
-
manifests.map(async (manifest) => readJsonFile(
|
|
10246
|
+
manifests.map(async (manifest) => readJsonFile(path20.join(paths.analysesDir, `${manifest.sourceId}.json`)))
|
|
9737
10247
|
);
|
|
9738
10248
|
return analyses.filter((analysis) => Boolean(analysis));
|
|
9739
10249
|
}
|
|
9740
10250
|
function approvalManifestPath(paths, approvalId) {
|
|
9741
|
-
return
|
|
10251
|
+
return path20.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
9742
10252
|
}
|
|
9743
10253
|
function approvalGraphPath(paths, approvalId) {
|
|
9744
|
-
return
|
|
10254
|
+
return path20.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
9745
10255
|
}
|
|
9746
10256
|
async function readApprovalManifest(paths, approvalId) {
|
|
9747
10257
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -9751,7 +10261,7 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
9751
10261
|
return manifest;
|
|
9752
10262
|
}
|
|
9753
10263
|
async function writeApprovalManifest(paths, manifest) {
|
|
9754
|
-
await
|
|
10264
|
+
await fs16.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
9755
10265
|
`, "utf8");
|
|
9756
10266
|
}
|
|
9757
10267
|
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
@@ -9766,7 +10276,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9766
10276
|
continue;
|
|
9767
10277
|
}
|
|
9768
10278
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
9769
|
-
const currentExists = await fileExists(
|
|
10279
|
+
const currentExists = await fileExists(path20.join(paths.wikiDir, file.relativePath));
|
|
9770
10280
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
9771
10281
|
entries.push({
|
|
9772
10282
|
pageId: nextPage.id,
|
|
@@ -9799,7 +10309,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9799
10309
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
9800
10310
|
entries.push({
|
|
9801
10311
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
9802
|
-
title: previousPage?.title ??
|
|
10312
|
+
title: previousPage?.title ?? path20.basename(deletedPath, ".md"),
|
|
9803
10313
|
kind: previousPage?.kind ?? "index",
|
|
9804
10314
|
changeType: "delete",
|
|
9805
10315
|
status: "pending",
|
|
@@ -9811,16 +10321,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9811
10321
|
}
|
|
9812
10322
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
9813
10323
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
9814
|
-
const approvalDir =
|
|
10324
|
+
const approvalDir = path20.join(paths.approvalsDir, approvalId);
|
|
9815
10325
|
await ensureDir(approvalDir);
|
|
9816
|
-
await ensureDir(
|
|
9817
|
-
await ensureDir(
|
|
10326
|
+
await ensureDir(path20.join(approvalDir, "wiki"));
|
|
10327
|
+
await ensureDir(path20.join(approvalDir, "state"));
|
|
9818
10328
|
for (const file of changedFiles) {
|
|
9819
|
-
const targetPath =
|
|
9820
|
-
await ensureDir(
|
|
9821
|
-
await
|
|
10329
|
+
const targetPath = path20.join(approvalDir, "wiki", file.relativePath);
|
|
10330
|
+
await ensureDir(path20.dirname(targetPath));
|
|
10331
|
+
await fs16.writeFile(targetPath, file.content, "utf8");
|
|
9822
10332
|
}
|
|
9823
|
-
await
|
|
10333
|
+
await fs16.writeFile(path20.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
9824
10334
|
await writeApprovalManifest(paths, {
|
|
9825
10335
|
approvalId,
|
|
9826
10336
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9856,6 +10366,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9856
10366
|
title: modulePageTitle(manifest),
|
|
9857
10367
|
kind: "module",
|
|
9858
10368
|
sourceIds: [manifest.sourceId],
|
|
10369
|
+
sourceClass: manifest.sourceClass,
|
|
9859
10370
|
projectIds: sourceProjectIds,
|
|
9860
10371
|
nodeIds: [analysis.code.moduleId, ...analysis.code.symbols.map((symbol) => symbol.id)],
|
|
9861
10372
|
schemaHash: sourceSchemaHash,
|
|
@@ -9868,6 +10379,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9868
10379
|
title: analysis.title,
|
|
9869
10380
|
kind: "source",
|
|
9870
10381
|
sourceIds: [manifest.sourceId],
|
|
10382
|
+
sourceClass: manifest.sourceClass,
|
|
9871
10383
|
projectIds: sourceProjectIds,
|
|
9872
10384
|
nodeIds: [
|
|
9873
10385
|
`source:${manifest.sourceId}`,
|
|
@@ -9880,7 +10392,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9880
10392
|
confidence: 1
|
|
9881
10393
|
});
|
|
9882
10394
|
const sourceRecord = await buildManagedGraphPage(
|
|
9883
|
-
|
|
10395
|
+
path20.join(paths.wikiDir, preview.path),
|
|
9884
10396
|
{
|
|
9885
10397
|
managedBy: "system",
|
|
9886
10398
|
confidence: 1,
|
|
@@ -9895,7 +10407,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9895
10407
|
modulePreview ?? void 0,
|
|
9896
10408
|
{
|
|
9897
10409
|
projectIds: sourceProjectIds,
|
|
9898
|
-
extraTags: sourceCategoryTags
|
|
10410
|
+
extraTags: sourceCategoryTags,
|
|
10411
|
+
sourceClass: manifest.sourceClass
|
|
9899
10412
|
}
|
|
9900
10413
|
)
|
|
9901
10414
|
);
|
|
@@ -9925,7 +10438,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9925
10438
|
);
|
|
9926
10439
|
records.push(
|
|
9927
10440
|
await buildManagedGraphPage(
|
|
9928
|
-
|
|
10441
|
+
path20.join(paths.wikiDir, modulePreview.path),
|
|
9929
10442
|
{
|
|
9930
10443
|
managedBy: "system",
|
|
9931
10444
|
confidence: 1,
|
|
@@ -9957,9 +10470,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9957
10470
|
const previousEntry = input.previousState?.candidateHistory?.[pageId];
|
|
9958
10471
|
const promoted = previousEntry?.status === "active" || promoteCandidates && shouldPromoteCandidate(previousEntry, sourceIds);
|
|
9959
10472
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
10473
|
+
const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
|
|
9960
10474
|
const fallbackPaths = [
|
|
9961
|
-
|
|
9962
|
-
|
|
10475
|
+
path20.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
10476
|
+
path20.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
9963
10477
|
];
|
|
9964
10478
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
9965
10479
|
const preview = emptyGraphPage({
|
|
@@ -9968,6 +10482,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9968
10482
|
title: aggregate.name,
|
|
9969
10483
|
kind: itemKind,
|
|
9970
10484
|
sourceIds,
|
|
10485
|
+
sourceClass: aggregateSourceClass2,
|
|
9971
10486
|
projectIds,
|
|
9972
10487
|
nodeIds: [pageId],
|
|
9973
10488
|
schemaHash,
|
|
@@ -9976,7 +10491,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9976
10491
|
status: promoted ? "active" : "candidate"
|
|
9977
10492
|
});
|
|
9978
10493
|
const pageRecord = await buildManagedGraphPage(
|
|
9979
|
-
|
|
10494
|
+
path20.join(paths.wikiDir, relativePath),
|
|
9980
10495
|
{
|
|
9981
10496
|
status: promoted ? "active" : "candidate",
|
|
9982
10497
|
managedBy: "system",
|
|
@@ -10000,7 +10515,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10000
10515
|
aggregate.name,
|
|
10001
10516
|
...aggregate.descriptions,
|
|
10002
10517
|
...aggregate.sourceAnalyses.map((item) => item.summary)
|
|
10003
|
-
])
|
|
10518
|
+
]),
|
|
10519
|
+
sourceClass: aggregateSourceClass2
|
|
10004
10520
|
}
|
|
10005
10521
|
)
|
|
10006
10522
|
);
|
|
@@ -10016,7 +10532,20 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10016
10532
|
}
|
|
10017
10533
|
const compiledPages = records.map((record) => record.page);
|
|
10018
10534
|
const basePages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
10019
|
-
const
|
|
10535
|
+
const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex);
|
|
10536
|
+
const embeddingEdges = await embeddingSimilarityEdges(rootDir, structuralGraph).catch(() => []);
|
|
10537
|
+
const baseGraph = embeddingEdges.length > 0 ? (() => {
|
|
10538
|
+
const edges = uniqueBy([...structuralGraph.edges, ...embeddingEdges], (edge) => edge.id).sort(
|
|
10539
|
+
(left, right) => left.id.localeCompare(right.id)
|
|
10540
|
+
);
|
|
10541
|
+
const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges);
|
|
10542
|
+
return {
|
|
10543
|
+
...structuralGraph,
|
|
10544
|
+
nodes: metrics.nodes,
|
|
10545
|
+
edges,
|
|
10546
|
+
communities: metrics.communities
|
|
10547
|
+
};
|
|
10548
|
+
})() : structuralGraph;
|
|
10020
10549
|
const graphOrientation = await buildGraphOrientationPages(baseGraph, paths, globalSchemaHash, input.previousState?.generatedAt);
|
|
10021
10550
|
records.push(...graphOrientation.records);
|
|
10022
10551
|
const allPages = [...basePages, ...graphOrientation.records.map((record) => record.page)];
|
|
@@ -10057,7 +10586,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10057
10586
|
confidence: 1
|
|
10058
10587
|
}),
|
|
10059
10588
|
content: await buildManagedContent(
|
|
10060
|
-
|
|
10589
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
10061
10590
|
{
|
|
10062
10591
|
managedBy: "system",
|
|
10063
10592
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -10081,7 +10610,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10081
10610
|
records.push({
|
|
10082
10611
|
page: projectIndexRef,
|
|
10083
10612
|
content: await buildManagedContent(
|
|
10084
|
-
|
|
10613
|
+
path20.join(paths.wikiDir, projectIndexRef.path),
|
|
10085
10614
|
{
|
|
10086
10615
|
managedBy: "system",
|
|
10087
10616
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -10109,7 +10638,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10109
10638
|
confidence: 1
|
|
10110
10639
|
}),
|
|
10111
10640
|
content: await buildManagedContent(
|
|
10112
|
-
|
|
10641
|
+
path20.join(paths.wikiDir, "index.md"),
|
|
10113
10642
|
{
|
|
10114
10643
|
managedBy: "system",
|
|
10115
10644
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -10140,7 +10669,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10140
10669
|
confidence: 1
|
|
10141
10670
|
}),
|
|
10142
10671
|
content: await buildManagedContent(
|
|
10143
|
-
|
|
10672
|
+
path20.join(paths.wikiDir, relativePath),
|
|
10144
10673
|
{
|
|
10145
10674
|
managedBy: "system",
|
|
10146
10675
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -10151,12 +10680,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10151
10680
|
}
|
|
10152
10681
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
10153
10682
|
const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
10154
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
10683
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path20.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
10155
10684
|
const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
10156
10685
|
const changedFiles = [];
|
|
10157
10686
|
for (const record of records) {
|
|
10158
|
-
const absolutePath =
|
|
10159
|
-
const current = await fileExists(absolutePath) ? await
|
|
10687
|
+
const absolutePath = path20.join(paths.wikiDir, record.page.path);
|
|
10688
|
+
const current = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
10160
10689
|
if (current !== record.content) {
|
|
10161
10690
|
changedPages.push(record.page.path);
|
|
10162
10691
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -10181,10 +10710,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10181
10710
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
10182
10711
|
}
|
|
10183
10712
|
for (const relativePath of obsoletePaths) {
|
|
10184
|
-
await
|
|
10713
|
+
await fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true });
|
|
10185
10714
|
}
|
|
10186
10715
|
await writeJsonFile(paths.graphPath, graph);
|
|
10187
|
-
await writeJsonFile(
|
|
10716
|
+
await writeJsonFile(path20.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
10188
10717
|
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
10189
10718
|
await writeJsonFile(paths.compileStatePath, {
|
|
10190
10719
|
generatedAt: graph.generatedAt,
|
|
@@ -10255,17 +10784,17 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10255
10784
|
})
|
|
10256
10785
|
);
|
|
10257
10786
|
await Promise.all([
|
|
10258
|
-
ensureDir(
|
|
10259
|
-
ensureDir(
|
|
10260
|
-
ensureDir(
|
|
10261
|
-
ensureDir(
|
|
10262
|
-
ensureDir(
|
|
10263
|
-
ensureDir(
|
|
10264
|
-
ensureDir(
|
|
10265
|
-
ensureDir(
|
|
10266
|
-
ensureDir(
|
|
10787
|
+
ensureDir(path20.join(paths.wikiDir, "sources")),
|
|
10788
|
+
ensureDir(path20.join(paths.wikiDir, "code")),
|
|
10789
|
+
ensureDir(path20.join(paths.wikiDir, "concepts")),
|
|
10790
|
+
ensureDir(path20.join(paths.wikiDir, "entities")),
|
|
10791
|
+
ensureDir(path20.join(paths.wikiDir, "outputs")),
|
|
10792
|
+
ensureDir(path20.join(paths.wikiDir, "graph")),
|
|
10793
|
+
ensureDir(path20.join(paths.wikiDir, "graph", "communities")),
|
|
10794
|
+
ensureDir(path20.join(paths.wikiDir, "projects")),
|
|
10795
|
+
ensureDir(path20.join(paths.wikiDir, "candidates"))
|
|
10267
10796
|
]);
|
|
10268
|
-
const projectsIndexPath =
|
|
10797
|
+
const projectsIndexPath = path20.join(paths.wikiDir, "projects", "index.md");
|
|
10269
10798
|
await writeFileIfChanged(
|
|
10270
10799
|
projectsIndexPath,
|
|
10271
10800
|
await buildManagedContent(
|
|
@@ -10286,7 +10815,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10286
10815
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
10287
10816
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
10288
10817
|
};
|
|
10289
|
-
const absolutePath =
|
|
10818
|
+
const absolutePath = path20.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
10290
10819
|
await writeFileIfChanged(
|
|
10291
10820
|
absolutePath,
|
|
10292
10821
|
await buildManagedContent(
|
|
@@ -10304,7 +10833,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10304
10833
|
)
|
|
10305
10834
|
);
|
|
10306
10835
|
}
|
|
10307
|
-
const rootIndexPath =
|
|
10836
|
+
const rootIndexPath = path20.join(paths.wikiDir, "index.md");
|
|
10308
10837
|
await writeFileIfChanged(
|
|
10309
10838
|
rootIndexPath,
|
|
10310
10839
|
await buildManagedContent(
|
|
@@ -10325,7 +10854,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10325
10854
|
["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
|
|
10326
10855
|
["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
|
|
10327
10856
|
]) {
|
|
10328
|
-
const absolutePath =
|
|
10857
|
+
const absolutePath = path20.join(paths.wikiDir, relativePath);
|
|
10329
10858
|
await writeFileIfChanged(
|
|
10330
10859
|
absolutePath,
|
|
10331
10860
|
await buildManagedContent(
|
|
@@ -10339,23 +10868,23 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10339
10868
|
);
|
|
10340
10869
|
}
|
|
10341
10870
|
for (const record of graphOrientation.records) {
|
|
10342
|
-
await writeFileIfChanged(
|
|
10871
|
+
await writeFileIfChanged(path20.join(paths.wikiDir, record.page.path), record.content);
|
|
10343
10872
|
}
|
|
10344
10873
|
if (graphOrientation.report) {
|
|
10345
|
-
await writeJsonFile(
|
|
10874
|
+
await writeJsonFile(path20.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
10346
10875
|
}
|
|
10347
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
10876
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path20.relative(paths.wikiDir, absolutePath)));
|
|
10348
10877
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
10349
10878
|
"projects/index.md",
|
|
10350
10879
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
10351
10880
|
]);
|
|
10352
10881
|
await Promise.all(
|
|
10353
|
-
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) =>
|
|
10882
|
+
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true }))
|
|
10354
10883
|
);
|
|
10355
|
-
const existingGraphPages = (await listFilesRecursive(
|
|
10884
|
+
const existingGraphPages = (await listFilesRecursive(path20.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path20.relative(paths.wikiDir, absolutePath)));
|
|
10356
10885
|
const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
|
|
10357
10886
|
await Promise.all(
|
|
10358
|
-
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) =>
|
|
10887
|
+
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true }))
|
|
10359
10888
|
);
|
|
10360
10889
|
await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
|
|
10361
10890
|
}
|
|
@@ -10375,7 +10904,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
10375
10904
|
confidence: 0.74
|
|
10376
10905
|
}
|
|
10377
10906
|
});
|
|
10378
|
-
const absolutePath =
|
|
10907
|
+
const absolutePath = path20.join(paths.wikiDir, output.page.path);
|
|
10379
10908
|
return {
|
|
10380
10909
|
page: output.page,
|
|
10381
10910
|
savedPath: absolutePath,
|
|
@@ -10387,15 +10916,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
10387
10916
|
async function persistOutputPage(rootDir, input) {
|
|
10388
10917
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10389
10918
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
10390
|
-
await ensureDir(
|
|
10391
|
-
await
|
|
10919
|
+
await ensureDir(path20.dirname(prepared.savedPath));
|
|
10920
|
+
await fs16.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
10392
10921
|
for (const assetFile of prepared.assetFiles) {
|
|
10393
|
-
const assetPath =
|
|
10394
|
-
await ensureDir(
|
|
10922
|
+
const assetPath = path20.join(paths.wikiDir, assetFile.relativePath);
|
|
10923
|
+
await ensureDir(path20.dirname(assetPath));
|
|
10395
10924
|
if (typeof assetFile.content === "string") {
|
|
10396
|
-
await
|
|
10925
|
+
await fs16.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
10397
10926
|
} else {
|
|
10398
|
-
await
|
|
10927
|
+
await fs16.writeFile(assetPath, assetFile.content);
|
|
10399
10928
|
}
|
|
10400
10929
|
}
|
|
10401
10930
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -10416,7 +10945,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
10416
10945
|
confidence: 0.76
|
|
10417
10946
|
}
|
|
10418
10947
|
});
|
|
10419
|
-
const absolutePath =
|
|
10948
|
+
const absolutePath = path20.join(paths.wikiDir, hub.page.path);
|
|
10420
10949
|
return {
|
|
10421
10950
|
page: hub.page,
|
|
10422
10951
|
savedPath: absolutePath,
|
|
@@ -10428,15 +10957,15 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
10428
10957
|
async function persistExploreHub(rootDir, input) {
|
|
10429
10958
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10430
10959
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
10431
|
-
await ensureDir(
|
|
10432
|
-
await
|
|
10960
|
+
await ensureDir(path20.dirname(prepared.savedPath));
|
|
10961
|
+
await fs16.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
10433
10962
|
for (const assetFile of prepared.assetFiles) {
|
|
10434
|
-
const assetPath =
|
|
10435
|
-
await ensureDir(
|
|
10963
|
+
const assetPath = path20.join(paths.wikiDir, assetFile.relativePath);
|
|
10964
|
+
await ensureDir(path20.dirname(assetPath));
|
|
10436
10965
|
if (typeof assetFile.content === "string") {
|
|
10437
|
-
await
|
|
10966
|
+
await fs16.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
10438
10967
|
} else {
|
|
10439
|
-
await
|
|
10968
|
+
await fs16.writeFile(assetPath, assetFile.content);
|
|
10440
10969
|
}
|
|
10441
10970
|
}
|
|
10442
10971
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -10453,17 +10982,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
10453
10982
|
}))
|
|
10454
10983
|
]);
|
|
10455
10984
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
10456
|
-
const approvalDir =
|
|
10985
|
+
const approvalDir = path20.join(paths.approvalsDir, approvalId);
|
|
10457
10986
|
await ensureDir(approvalDir);
|
|
10458
|
-
await ensureDir(
|
|
10459
|
-
await ensureDir(
|
|
10987
|
+
await ensureDir(path20.join(approvalDir, "wiki"));
|
|
10988
|
+
await ensureDir(path20.join(approvalDir, "state"));
|
|
10460
10989
|
for (const file of changedFiles) {
|
|
10461
|
-
const targetPath =
|
|
10462
|
-
await ensureDir(
|
|
10990
|
+
const targetPath = path20.join(approvalDir, "wiki", file.relativePath);
|
|
10991
|
+
await ensureDir(path20.dirname(targetPath));
|
|
10463
10992
|
if ("binary" in file && file.binary) {
|
|
10464
|
-
await
|
|
10993
|
+
await fs16.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
10465
10994
|
} else {
|
|
10466
|
-
await
|
|
10995
|
+
await fs16.writeFile(targetPath, file.content, "utf8");
|
|
10467
10996
|
}
|
|
10468
10997
|
}
|
|
10469
10998
|
const nextPages = sortGraphPages([
|
|
@@ -10478,7 +11007,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
10478
11007
|
sources: previousGraph?.sources ?? [],
|
|
10479
11008
|
pages: nextPages
|
|
10480
11009
|
};
|
|
10481
|
-
await
|
|
11010
|
+
await fs16.writeFile(path20.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
10482
11011
|
await writeApprovalManifest(paths, {
|
|
10483
11012
|
approvalId,
|
|
10484
11013
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10507,9 +11036,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
10507
11036
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
10508
11037
|
const excerpts = await Promise.all(
|
|
10509
11038
|
searchResults.map(async (result) => {
|
|
10510
|
-
const absolutePath =
|
|
11039
|
+
const absolutePath = path20.join(paths.wikiDir, result.path);
|
|
10511
11040
|
try {
|
|
10512
|
-
const content = await
|
|
11041
|
+
const content = await fs16.readFile(absolutePath, "utf8");
|
|
10513
11042
|
const parsed = matter9(content);
|
|
10514
11043
|
return `# ${result.title}
|
|
10515
11044
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -10691,7 +11220,7 @@ function sortGraphPages(pages) {
|
|
|
10691
11220
|
async function listApprovals(rootDir) {
|
|
10692
11221
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10693
11222
|
const manifests = await Promise.all(
|
|
10694
|
-
(await
|
|
11223
|
+
(await fs16.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
10695
11224
|
try {
|
|
10696
11225
|
return await readApprovalManifest(paths, entry.name);
|
|
10697
11226
|
} catch {
|
|
@@ -10707,8 +11236,8 @@ async function readApproval(rootDir, approvalId) {
|
|
|
10707
11236
|
const details = await Promise.all(
|
|
10708
11237
|
manifest.entries.map(async (entry) => {
|
|
10709
11238
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
10710
|
-
const currentContent = currentPath ? await
|
|
10711
|
-
const stagedContent = entry.nextPath ? await
|
|
11239
|
+
const currentContent = currentPath ? await fs16.readFile(path20.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
11240
|
+
const stagedContent = entry.nextPath ? await fs16.readFile(path20.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
10712
11241
|
return {
|
|
10713
11242
|
...entry,
|
|
10714
11243
|
currentContent,
|
|
@@ -10736,26 +11265,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
10736
11265
|
if (!entry.nextPath) {
|
|
10737
11266
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
10738
11267
|
}
|
|
10739
|
-
const stagedAbsolutePath =
|
|
10740
|
-
const stagedContent = await
|
|
10741
|
-
const targetAbsolutePath =
|
|
10742
|
-
await ensureDir(
|
|
10743
|
-
await
|
|
11268
|
+
const stagedAbsolutePath = path20.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
|
|
11269
|
+
const stagedContent = await fs16.readFile(stagedAbsolutePath, "utf8");
|
|
11270
|
+
const targetAbsolutePath = path20.join(paths.wikiDir, entry.nextPath);
|
|
11271
|
+
await ensureDir(path20.dirname(targetAbsolutePath));
|
|
11272
|
+
await fs16.writeFile(targetAbsolutePath, stagedContent, "utf8");
|
|
10744
11273
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
10745
|
-
await
|
|
11274
|
+
await fs16.rm(path20.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
10746
11275
|
}
|
|
10747
11276
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
10748
11277
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
10749
|
-
const outputAssetDir =
|
|
10750
|
-
await
|
|
11278
|
+
const outputAssetDir = path20.join(paths.wikiDir, "outputs", "assets", path20.basename(nextPage.path, ".md"));
|
|
11279
|
+
await fs16.rm(outputAssetDir, { recursive: true, force: true });
|
|
10751
11280
|
for (const asset of nextPage.outputAssets) {
|
|
10752
|
-
const stagedAssetPath =
|
|
11281
|
+
const stagedAssetPath = path20.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
10753
11282
|
if (!await fileExists(stagedAssetPath)) {
|
|
10754
11283
|
continue;
|
|
10755
11284
|
}
|
|
10756
|
-
const targetAssetPath =
|
|
10757
|
-
await ensureDir(
|
|
10758
|
-
await
|
|
11285
|
+
const targetAssetPath = path20.join(paths.wikiDir, asset.path);
|
|
11286
|
+
await ensureDir(path20.dirname(targetAssetPath));
|
|
11287
|
+
await fs16.copyFile(stagedAssetPath, targetAssetPath);
|
|
10759
11288
|
}
|
|
10760
11289
|
}
|
|
10761
11290
|
nextPages = nextPages.filter(
|
|
@@ -10766,10 +11295,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
10766
11295
|
} else {
|
|
10767
11296
|
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;
|
|
10768
11297
|
if (entry.previousPath) {
|
|
10769
|
-
await
|
|
11298
|
+
await fs16.rm(path20.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
10770
11299
|
}
|
|
10771
11300
|
if (deletedPage?.kind === "output") {
|
|
10772
|
-
await
|
|
11301
|
+
await fs16.rm(path20.join(paths.wikiDir, "outputs", "assets", path20.basename(deletedPage.path, ".md")), {
|
|
10773
11302
|
recursive: true,
|
|
10774
11303
|
force: true
|
|
10775
11304
|
});
|
|
@@ -10860,7 +11389,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
10860
11389
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10861
11390
|
const graph = await readJsonFile(paths.graphPath);
|
|
10862
11391
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
10863
|
-
const raw = await
|
|
11392
|
+
const raw = await fs16.readFile(path20.join(paths.wikiDir, candidate.path), "utf8");
|
|
10864
11393
|
const parsed = matter9(raw);
|
|
10865
11394
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10866
11395
|
const nextContent = matter9.stringify(parsed.content, {
|
|
@@ -10872,10 +11401,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
10872
11401
|
)
|
|
10873
11402
|
});
|
|
10874
11403
|
const nextPath = candidateActivePath(candidate);
|
|
10875
|
-
const nextAbsolutePath =
|
|
10876
|
-
await ensureDir(
|
|
10877
|
-
await
|
|
10878
|
-
await
|
|
11404
|
+
const nextAbsolutePath = path20.join(paths.wikiDir, nextPath);
|
|
11405
|
+
await ensureDir(path20.dirname(nextAbsolutePath));
|
|
11406
|
+
await fs16.writeFile(nextAbsolutePath, nextContent, "utf8");
|
|
11407
|
+
await fs16.rm(path20.join(paths.wikiDir, candidate.path), { force: true });
|
|
10879
11408
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
10880
11409
|
const nextPages = sortGraphPages(
|
|
10881
11410
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -10920,7 +11449,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
10920
11449
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10921
11450
|
const graph = await readJsonFile(paths.graphPath);
|
|
10922
11451
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
10923
|
-
await
|
|
11452
|
+
await fs16.rm(path20.join(paths.wikiDir, candidate.path), { force: true });
|
|
10924
11453
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
10925
11454
|
const nextGraph = {
|
|
10926
11455
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10959,18 +11488,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
10959
11488
|
}
|
|
10960
11489
|
async function ensureObsidianWorkspace(rootDir) {
|
|
10961
11490
|
const { config } = await loadVaultConfig(rootDir);
|
|
10962
|
-
const obsidianDir =
|
|
11491
|
+
const obsidianDir = path20.join(rootDir, ".obsidian");
|
|
10963
11492
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
10964
11493
|
await ensureDir(obsidianDir);
|
|
10965
11494
|
await Promise.all([
|
|
10966
|
-
writeJsonFile(
|
|
11495
|
+
writeJsonFile(path20.join(obsidianDir, "app.json"), {
|
|
10967
11496
|
alwaysUpdateLinks: true,
|
|
10968
11497
|
newFileLocation: "folder",
|
|
10969
11498
|
newFileFolderPath: "wiki/insights",
|
|
10970
11499
|
useMarkdownLinks: false,
|
|
10971
11500
|
attachmentFolderPath: "raw/assets"
|
|
10972
11501
|
}),
|
|
10973
|
-
writeJsonFile(
|
|
11502
|
+
writeJsonFile(path20.join(obsidianDir, "core-plugins.json"), [
|
|
10974
11503
|
"file-explorer",
|
|
10975
11504
|
"global-search",
|
|
10976
11505
|
"switcher",
|
|
@@ -10980,7 +11509,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
10980
11509
|
"tag-pane",
|
|
10981
11510
|
"page-preview"
|
|
10982
11511
|
]),
|
|
10983
|
-
writeJsonFile(
|
|
11512
|
+
writeJsonFile(path20.join(obsidianDir, "graph.json"), {
|
|
10984
11513
|
"collapse-filter": false,
|
|
10985
11514
|
search: "",
|
|
10986
11515
|
showTags: true,
|
|
@@ -10992,7 +11521,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
10992
11521
|
})),
|
|
10993
11522
|
localJumps: false
|
|
10994
11523
|
}),
|
|
10995
|
-
writeJsonFile(
|
|
11524
|
+
writeJsonFile(path20.join(obsidianDir, "workspace.json"), {
|
|
10996
11525
|
active: "root",
|
|
10997
11526
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
10998
11527
|
left: {
|
|
@@ -11007,7 +11536,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
11007
11536
|
async function initVault(rootDir, options = {}) {
|
|
11008
11537
|
const { paths } = await initWorkspace(rootDir);
|
|
11009
11538
|
await installConfiguredAgents(rootDir);
|
|
11010
|
-
const insightsIndexPath =
|
|
11539
|
+
const insightsIndexPath = path20.join(paths.wikiDir, "insights", "index.md");
|
|
11011
11540
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11012
11541
|
await writeFileIfChanged(
|
|
11013
11542
|
insightsIndexPath,
|
|
@@ -11043,7 +11572,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
11043
11572
|
)
|
|
11044
11573
|
);
|
|
11045
11574
|
await writeFileIfChanged(
|
|
11046
|
-
|
|
11575
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
11047
11576
|
matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
11048
11577
|
page_id: "projects:index",
|
|
11049
11578
|
kind: "index",
|
|
@@ -11065,7 +11594,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
11065
11594
|
})
|
|
11066
11595
|
);
|
|
11067
11596
|
await writeFileIfChanged(
|
|
11068
|
-
|
|
11597
|
+
path20.join(paths.wikiDir, "candidates", "index.md"),
|
|
11069
11598
|
matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
11070
11599
|
page_id: "candidates:index",
|
|
11071
11600
|
kind: "index",
|
|
@@ -11201,7 +11730,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
11201
11730
|
),
|
|
11202
11731
|
Promise.all(
|
|
11203
11732
|
clean.map(async (manifest) => {
|
|
11204
|
-
const cached = await readJsonFile(
|
|
11733
|
+
const cached = await readJsonFile(path20.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
11205
11734
|
if (cached) {
|
|
11206
11735
|
return cached;
|
|
11207
11736
|
}
|
|
@@ -11225,22 +11754,22 @@ async function compileVault(rootDir, options = {}) {
|
|
|
11225
11754
|
}
|
|
11226
11755
|
const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
|
|
11227
11756
|
if (analysisSignature(enriched) !== analysisSignature(analysis)) {
|
|
11228
|
-
await writeJsonFile(
|
|
11757
|
+
await writeJsonFile(path20.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
|
|
11229
11758
|
}
|
|
11230
11759
|
return enriched;
|
|
11231
11760
|
})
|
|
11232
11761
|
);
|
|
11233
11762
|
await Promise.all([
|
|
11234
|
-
ensureDir(
|
|
11235
|
-
ensureDir(
|
|
11236
|
-
ensureDir(
|
|
11237
|
-
ensureDir(
|
|
11238
|
-
ensureDir(
|
|
11239
|
-
ensureDir(
|
|
11240
|
-
ensureDir(
|
|
11241
|
-
ensureDir(
|
|
11242
|
-
ensureDir(
|
|
11243
|
-
ensureDir(
|
|
11763
|
+
ensureDir(path20.join(paths.wikiDir, "sources")),
|
|
11764
|
+
ensureDir(path20.join(paths.wikiDir, "code")),
|
|
11765
|
+
ensureDir(path20.join(paths.wikiDir, "concepts")),
|
|
11766
|
+
ensureDir(path20.join(paths.wikiDir, "entities")),
|
|
11767
|
+
ensureDir(path20.join(paths.wikiDir, "outputs")),
|
|
11768
|
+
ensureDir(path20.join(paths.wikiDir, "projects")),
|
|
11769
|
+
ensureDir(path20.join(paths.wikiDir, "insights")),
|
|
11770
|
+
ensureDir(path20.join(paths.wikiDir, "candidates")),
|
|
11771
|
+
ensureDir(path20.join(paths.wikiDir, "candidates", "concepts")),
|
|
11772
|
+
ensureDir(path20.join(paths.wikiDir, "candidates", "entities"))
|
|
11244
11773
|
]);
|
|
11245
11774
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
11246
11775
|
schemas,
|
|
@@ -11387,7 +11916,7 @@ async function queryVault(rootDir, options) {
|
|
|
11387
11916
|
assetFiles: staged.assetFiles
|
|
11388
11917
|
}
|
|
11389
11918
|
]);
|
|
11390
|
-
stagedPath =
|
|
11919
|
+
stagedPath = path20.join(approval.approvalDir, "wiki", staged.page.path);
|
|
11391
11920
|
savedPageId = staged.page.id;
|
|
11392
11921
|
approvalId = approval.approvalId;
|
|
11393
11922
|
approvalDir = approval.approvalDir;
|
|
@@ -11643,9 +12172,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
11643
12172
|
approvalId = approval.approvalId;
|
|
11644
12173
|
approvalDir = approval.approvalDir;
|
|
11645
12174
|
stepResults.forEach((result, index) => {
|
|
11646
|
-
result.stagedPath =
|
|
12175
|
+
result.stagedPath = path20.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
11647
12176
|
});
|
|
11648
|
-
stagedHubPath =
|
|
12177
|
+
stagedHubPath = path20.join(approval.approvalDir, "wiki", hubPage.path);
|
|
11649
12178
|
} else {
|
|
11650
12179
|
await refreshVaultAfterOutputSave(rootDir);
|
|
11651
12180
|
}
|
|
@@ -11706,11 +12235,18 @@ async function ensureCompiledGraph(rootDir) {
|
|
|
11706
12235
|
}
|
|
11707
12236
|
return graph;
|
|
11708
12237
|
}
|
|
11709
|
-
async function
|
|
12238
|
+
async function runResolvedGraphQuery(rootDir, graph, question, options = {}) {
|
|
11710
12239
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11711
|
-
const graph = await ensureCompiledGraph(rootDir);
|
|
11712
12240
|
const searchResults = searchPages(paths.searchDbPath, question, { limit: Math.max(5, options.budget ?? 10) });
|
|
11713
|
-
|
|
12241
|
+
const semanticMatches = await semanticGraphMatches(rootDir, graph, question, Math.max(8, options.budget ?? 12)).catch(() => []);
|
|
12242
|
+
return queryGraph(graph, question, searchResults, {
|
|
12243
|
+
...options,
|
|
12244
|
+
semanticMatches
|
|
12245
|
+
});
|
|
12246
|
+
}
|
|
12247
|
+
async function queryGraphVault(rootDir, question, options = {}) {
|
|
12248
|
+
const graph = await ensureCompiledGraph(rootDir);
|
|
12249
|
+
return runResolvedGraphQuery(rootDir, graph, question, options);
|
|
11714
12250
|
}
|
|
11715
12251
|
async function benchmarkVault(rootDir, options = {}) {
|
|
11716
12252
|
const { config, paths } = await loadVaultConfig(rootDir);
|
|
@@ -11725,30 +12261,31 @@ async function benchmarkVault(rootDir, options = {}) {
|
|
|
11725
12261
|
}
|
|
11726
12262
|
}
|
|
11727
12263
|
for (const page of graph.pages) {
|
|
11728
|
-
const absolutePath =
|
|
12264
|
+
const absolutePath = path20.join(paths.wikiDir, page.path);
|
|
11729
12265
|
if (!await fileExists(absolutePath)) {
|
|
11730
12266
|
continue;
|
|
11731
12267
|
}
|
|
11732
|
-
const parsed = matter9(await
|
|
12268
|
+
const parsed = matter9(await fs16.readFile(absolutePath, "utf8"));
|
|
11733
12269
|
pageContentsById.set(page.id, parsed.content);
|
|
11734
12270
|
}
|
|
11735
12271
|
const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
11736
12272
|
const maxQuestions = Math.max(1, options.maxQuestions ?? config.benchmark?.maxQuestions ?? 3);
|
|
11737
12273
|
const questions = (options.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
11738
12274
|
const sampleQuestions = (questions.length ? questions : configuredQuestions.length ? configuredQuestions : defaultBenchmarkQuestionsForGraph(graph, maxQuestions)).slice(0, maxQuestions);
|
|
11739
|
-
const perQuestion =
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
12275
|
+
const perQuestion = await Promise.all(
|
|
12276
|
+
sampleQuestions.map(async (question) => {
|
|
12277
|
+
const result = await runResolvedGraphQuery(rootDir, graph, question, { budget: 12 });
|
|
12278
|
+
const metrics = benchmarkQueryTokens(graph, result, pageContentsById);
|
|
12279
|
+
return {
|
|
12280
|
+
question,
|
|
12281
|
+
queryTokens: metrics.queryTokens,
|
|
12282
|
+
reduction: metrics.reduction,
|
|
12283
|
+
visitedNodeIds: result.visitedNodeIds,
|
|
12284
|
+
visitedEdgeIds: result.visitedEdgeIds,
|
|
12285
|
+
pageIds: result.pageIds
|
|
12286
|
+
};
|
|
12287
|
+
})
|
|
12288
|
+
);
|
|
11752
12289
|
const artifact = buildBenchmarkArtifact({
|
|
11753
12290
|
graph,
|
|
11754
12291
|
corpusWords,
|
|
@@ -11773,7 +12310,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
|
|
|
11773
12310
|
}
|
|
11774
12311
|
async function readGraphReport(rootDir) {
|
|
11775
12312
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11776
|
-
return readJsonFile(
|
|
12313
|
+
return readJsonFile(path20.join(paths.wikiDir, "graph", "report.json"));
|
|
11777
12314
|
}
|
|
11778
12315
|
async function listGodNodes(rootDir, limit = 10) {
|
|
11779
12316
|
const graph = await ensureCompiledGraph(rootDir);
|
|
@@ -11786,15 +12323,15 @@ async function listPages(rootDir) {
|
|
|
11786
12323
|
}
|
|
11787
12324
|
async function readPage(rootDir, relativePath) {
|
|
11788
12325
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11789
|
-
const absolutePath =
|
|
12326
|
+
const absolutePath = path20.resolve(paths.wikiDir, relativePath);
|
|
11790
12327
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
11791
12328
|
return null;
|
|
11792
12329
|
}
|
|
11793
|
-
const raw = await
|
|
12330
|
+
const raw = await fs16.readFile(absolutePath, "utf8");
|
|
11794
12331
|
const parsed = matter9(raw);
|
|
11795
12332
|
return {
|
|
11796
12333
|
path: relativePath,
|
|
11797
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
12334
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(relativePath, path20.extname(relativePath)),
|
|
11798
12335
|
frontmatter: parsed.data,
|
|
11799
12336
|
content: parsed.content
|
|
11800
12337
|
};
|
|
@@ -11830,7 +12367,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11830
12367
|
severity: "warning",
|
|
11831
12368
|
code: "stale_page",
|
|
11832
12369
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
11833
|
-
pagePath:
|
|
12370
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11834
12371
|
relatedPageIds: [page.id]
|
|
11835
12372
|
});
|
|
11836
12373
|
}
|
|
@@ -11841,7 +12378,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11841
12378
|
severity: "warning",
|
|
11842
12379
|
code: "stale_page",
|
|
11843
12380
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
11844
|
-
pagePath:
|
|
12381
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11845
12382
|
relatedSourceIds: [sourceId],
|
|
11846
12383
|
relatedPageIds: [page.id]
|
|
11847
12384
|
});
|
|
@@ -11852,13 +12389,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11852
12389
|
severity: "info",
|
|
11853
12390
|
code: "orphan_page",
|
|
11854
12391
|
message: `Page ${page.title} has no backlinks.`,
|
|
11855
|
-
pagePath:
|
|
12392
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11856
12393
|
relatedPageIds: [page.id]
|
|
11857
12394
|
});
|
|
11858
12395
|
}
|
|
11859
|
-
const absolutePath =
|
|
12396
|
+
const absolutePath = path20.join(paths.wikiDir, page.path);
|
|
11860
12397
|
if (await fileExists(absolutePath)) {
|
|
11861
|
-
const content = await
|
|
12398
|
+
const content = await fs16.readFile(absolutePath, "utf8");
|
|
11862
12399
|
if (content.includes("## Claims")) {
|
|
11863
12400
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
11864
12401
|
if (uncited.length) {
|
|
@@ -11938,7 +12475,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
11938
12475
|
}
|
|
11939
12476
|
|
|
11940
12477
|
// src/mcp.ts
|
|
11941
|
-
var SERVER_VERSION = "0.1.
|
|
12478
|
+
var SERVER_VERSION = "0.1.26";
|
|
11942
12479
|
async function createMcpServer(rootDir) {
|
|
11943
12480
|
const server = new McpServer({
|
|
11944
12481
|
name: "swarmvault",
|
|
@@ -12209,7 +12746,7 @@ async function createMcpServer(rootDir) {
|
|
|
12209
12746
|
},
|
|
12210
12747
|
async () => {
|
|
12211
12748
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12212
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
12749
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path21.relative(paths.sessionsDir, filePath))).sort();
|
|
12213
12750
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
12214
12751
|
}
|
|
12215
12752
|
);
|
|
@@ -12242,8 +12779,8 @@ async function createMcpServer(rootDir) {
|
|
|
12242
12779
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
12243
12780
|
}
|
|
12244
12781
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12245
|
-
const absolutePath =
|
|
12246
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
12782
|
+
const absolutePath = path21.resolve(paths.wikiDir, relativePath);
|
|
12783
|
+
return asTextResource(`swarmvault://pages/${encodedPath}`, await fs17.readFile(absolutePath, "utf8"));
|
|
12247
12784
|
}
|
|
12248
12785
|
);
|
|
12249
12786
|
server.registerResource(
|
|
@@ -12251,11 +12788,11 @@ async function createMcpServer(rootDir) {
|
|
|
12251
12788
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
12252
12789
|
list: async () => {
|
|
12253
12790
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12254
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
12791
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path21.relative(paths.sessionsDir, filePath))).sort();
|
|
12255
12792
|
return {
|
|
12256
12793
|
resources: files.map((relativePath) => ({
|
|
12257
12794
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
12258
|
-
name:
|
|
12795
|
+
name: path21.basename(relativePath, ".md"),
|
|
12259
12796
|
title: relativePath,
|
|
12260
12797
|
description: "SwarmVault session artifact",
|
|
12261
12798
|
mimeType: "text/markdown"
|
|
@@ -12272,11 +12809,11 @@ async function createMcpServer(rootDir) {
|
|
|
12272
12809
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12273
12810
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
12274
12811
|
const relativePath = decodeURIComponent(encodedPath);
|
|
12275
|
-
const absolutePath =
|
|
12812
|
+
const absolutePath = path21.resolve(paths.sessionsDir, relativePath);
|
|
12276
12813
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
12277
12814
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
12278
12815
|
}
|
|
12279
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
12816
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs17.readFile(absolutePath, "utf8"));
|
|
12280
12817
|
}
|
|
12281
12818
|
);
|
|
12282
12819
|
return server;
|
|
@@ -12324,13 +12861,13 @@ function asTextResource(uri, text) {
|
|
|
12324
12861
|
}
|
|
12325
12862
|
|
|
12326
12863
|
// src/schedule.ts
|
|
12327
|
-
import
|
|
12328
|
-
import
|
|
12864
|
+
import fs18 from "fs/promises";
|
|
12865
|
+
import path22 from "path";
|
|
12329
12866
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
12330
|
-
return
|
|
12867
|
+
return path22.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
12331
12868
|
}
|
|
12332
12869
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
12333
|
-
return
|
|
12870
|
+
return path22.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
12334
12871
|
}
|
|
12335
12872
|
function parseEveryDuration(value) {
|
|
12336
12873
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -12433,13 +12970,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
12433
12970
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12434
12971
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
12435
12972
|
await ensureDir(paths.schedulesDir);
|
|
12436
|
-
const handle = await
|
|
12973
|
+
const handle = await fs18.open(leasePath, "wx");
|
|
12437
12974
|
await handle.writeFile(`${process.pid}
|
|
12438
12975
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
12439
12976
|
`);
|
|
12440
12977
|
await handle.close();
|
|
12441
12978
|
return async () => {
|
|
12442
|
-
await
|
|
12979
|
+
await fs18.rm(leasePath, { force: true });
|
|
12443
12980
|
};
|
|
12444
12981
|
}
|
|
12445
12982
|
async function listSchedules(rootDir) {
|
|
@@ -12587,31 +13124,31 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
12587
13124
|
|
|
12588
13125
|
// src/viewer.ts
|
|
12589
13126
|
import { execFile } from "child_process";
|
|
12590
|
-
import
|
|
13127
|
+
import fs19 from "fs/promises";
|
|
12591
13128
|
import http from "http";
|
|
12592
|
-
import
|
|
13129
|
+
import path24 from "path";
|
|
12593
13130
|
import { promisify } from "util";
|
|
12594
13131
|
import matter10 from "gray-matter";
|
|
12595
13132
|
import mime2 from "mime-types";
|
|
12596
13133
|
|
|
12597
13134
|
// src/watch.ts
|
|
12598
|
-
import
|
|
13135
|
+
import path23 from "path";
|
|
12599
13136
|
import process2 from "process";
|
|
12600
13137
|
import chokidar from "chokidar";
|
|
12601
13138
|
var MAX_BACKOFF_MS = 3e4;
|
|
12602
13139
|
var BACKOFF_THRESHOLD = 3;
|
|
12603
13140
|
var CRITICAL_THRESHOLD = 10;
|
|
12604
|
-
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", "
|
|
13141
|
+
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
12605
13142
|
function withinRoot2(rootPath, targetPath) {
|
|
12606
|
-
const relative =
|
|
12607
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
13143
|
+
const relative = path23.relative(rootPath, targetPath);
|
|
13144
|
+
return relative === "" || !relative.startsWith("..") && !path23.isAbsolute(relative);
|
|
12608
13145
|
}
|
|
12609
13146
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
12610
|
-
const relativePath =
|
|
13147
|
+
const relativePath = path23.relative(baseDir, targetPath);
|
|
12611
13148
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
12612
13149
|
return false;
|
|
12613
13150
|
}
|
|
12614
|
-
return relativePath.split(
|
|
13151
|
+
return relativePath.split(path23.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
|
|
12615
13152
|
}
|
|
12616
13153
|
function workspaceIgnoreRoots(rootDir, paths) {
|
|
12617
13154
|
return [
|
|
@@ -12620,16 +13157,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
|
|
|
12620
13157
|
paths.stateDir,
|
|
12621
13158
|
paths.agentDir,
|
|
12622
13159
|
paths.inboxDir,
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
].map((candidate) =>
|
|
13160
|
+
path23.join(rootDir, ".claude"),
|
|
13161
|
+
path23.join(rootDir, ".cursor"),
|
|
13162
|
+
path23.join(rootDir, ".obsidian")
|
|
13163
|
+
].map((candidate) => path23.resolve(candidate));
|
|
12627
13164
|
}
|
|
12628
13165
|
async function resolveWatchTargets(rootDir, paths, options) {
|
|
12629
|
-
const targets = /* @__PURE__ */ new Set([
|
|
13166
|
+
const targets = /* @__PURE__ */ new Set([path23.resolve(paths.inboxDir)]);
|
|
12630
13167
|
if (options.repo) {
|
|
12631
13168
|
for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
|
|
12632
|
-
targets.add(
|
|
13169
|
+
targets.add(path23.resolve(repoRoot));
|
|
12633
13170
|
}
|
|
12634
13171
|
}
|
|
12635
13172
|
return [...targets].sort((left, right) => left.localeCompare(right));
|
|
@@ -12759,7 +13296,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12759
13296
|
const { paths } = await initWorkspace(rootDir);
|
|
12760
13297
|
const baseDebounceMs = options.debounceMs ?? 900;
|
|
12761
13298
|
const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
|
|
12762
|
-
const inboxWatchRoot =
|
|
13299
|
+
const inboxWatchRoot = path23.resolve(paths.inboxDir);
|
|
12763
13300
|
let watchTargets = await resolveWatchTargets(rootDir, paths, options);
|
|
12764
13301
|
let timer;
|
|
12765
13302
|
let running = false;
|
|
@@ -12773,7 +13310,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12773
13310
|
usePolling: true,
|
|
12774
13311
|
interval: 100,
|
|
12775
13312
|
ignored: (targetPath) => {
|
|
12776
|
-
const absolutePath =
|
|
13313
|
+
const absolutePath = path23.resolve(targetPath);
|
|
12777
13314
|
const primaryTarget = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
12778
13315
|
if (!primaryTarget) {
|
|
12779
13316
|
return false;
|
|
@@ -12945,8 +13482,8 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12945
13482
|
}
|
|
12946
13483
|
};
|
|
12947
13484
|
const reasonForPath = (targetPath) => {
|
|
12948
|
-
const baseDir = watchTargets.filter((watchTarget) => withinRoot2(watchTarget,
|
|
12949
|
-
return
|
|
13485
|
+
const baseDir = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, path23.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
|
|
13486
|
+
return path23.relative(baseDir, targetPath) || ".";
|
|
12950
13487
|
};
|
|
12951
13488
|
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)}`));
|
|
12952
13489
|
await new Promise((resolve, reject) => {
|
|
@@ -12987,15 +13524,15 @@ async function getWatchStatus(rootDir) {
|
|
|
12987
13524
|
var execFileAsync = promisify(execFile);
|
|
12988
13525
|
async function readViewerPage(rootDir, relativePath) {
|
|
12989
13526
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12990
|
-
const absolutePath =
|
|
13527
|
+
const absolutePath = path24.resolve(paths.wikiDir, relativePath);
|
|
12991
13528
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
12992
13529
|
return null;
|
|
12993
13530
|
}
|
|
12994
|
-
const raw = await
|
|
13531
|
+
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
12995
13532
|
const parsed = matter10(raw);
|
|
12996
13533
|
return {
|
|
12997
13534
|
path: relativePath,
|
|
12998
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
13535
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path24.basename(relativePath, path24.extname(relativePath)),
|
|
12999
13536
|
frontmatter: parsed.data,
|
|
13000
13537
|
content: parsed.content,
|
|
13001
13538
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -13003,12 +13540,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
13003
13540
|
}
|
|
13004
13541
|
async function readViewerAsset(rootDir, relativePath) {
|
|
13005
13542
|
const { paths } = await loadVaultConfig(rootDir);
|
|
13006
|
-
const absolutePath =
|
|
13543
|
+
const absolutePath = path24.resolve(paths.wikiDir, relativePath);
|
|
13007
13544
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
13008
13545
|
return null;
|
|
13009
13546
|
}
|
|
13010
13547
|
return {
|
|
13011
|
-
buffer: await
|
|
13548
|
+
buffer: await fs19.readFile(absolutePath),
|
|
13012
13549
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
13013
13550
|
};
|
|
13014
13551
|
}
|
|
@@ -13031,12 +13568,12 @@ async function readJsonBody(request) {
|
|
|
13031
13568
|
return JSON.parse(raw);
|
|
13032
13569
|
}
|
|
13033
13570
|
async function ensureViewerDist(viewerDistDir) {
|
|
13034
|
-
const indexPath =
|
|
13571
|
+
const indexPath = path24.join(viewerDistDir, "index.html");
|
|
13035
13572
|
if (await fileExists(indexPath)) {
|
|
13036
13573
|
return;
|
|
13037
13574
|
}
|
|
13038
|
-
const viewerProjectDir =
|
|
13039
|
-
if (await fileExists(
|
|
13575
|
+
const viewerProjectDir = path24.dirname(viewerDistDir);
|
|
13576
|
+
if (await fileExists(path24.join(viewerProjectDir, "package.json"))) {
|
|
13040
13577
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
13041
13578
|
}
|
|
13042
13579
|
}
|
|
@@ -13053,7 +13590,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
13053
13590
|
return;
|
|
13054
13591
|
}
|
|
13055
13592
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13056
|
-
response.end(await
|
|
13593
|
+
response.end(await fs19.readFile(paths.graphPath, "utf8"));
|
|
13057
13594
|
return;
|
|
13058
13595
|
}
|
|
13059
13596
|
if (url.pathname === "/api/graph/query") {
|
|
@@ -13096,26 +13633,28 @@ async function startGraphServer(rootDir, port) {
|
|
|
13096
13633
|
const status = url.searchParams.get("status") ?? "all";
|
|
13097
13634
|
const project = url.searchParams.get("project") ?? "all";
|
|
13098
13635
|
const sourceType = url.searchParams.get("sourceType") ?? "all";
|
|
13636
|
+
const sourceClass = url.searchParams.get("sourceClass") ?? "all";
|
|
13099
13637
|
const results = searchPages(paths.searchDbPath, query, {
|
|
13100
13638
|
limit: Number.isFinite(limit) ? limit : 10,
|
|
13101
13639
|
kind,
|
|
13102
13640
|
status,
|
|
13103
13641
|
project,
|
|
13104
|
-
sourceType
|
|
13642
|
+
sourceType,
|
|
13643
|
+
sourceClass
|
|
13105
13644
|
});
|
|
13106
13645
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13107
13646
|
response.end(JSON.stringify(results));
|
|
13108
13647
|
return;
|
|
13109
13648
|
}
|
|
13110
13649
|
if (url.pathname === "/api/graph-report") {
|
|
13111
|
-
const reportPath =
|
|
13650
|
+
const reportPath = path24.join(paths.wikiDir, "graph", "report.json");
|
|
13112
13651
|
if (!await fileExists(reportPath)) {
|
|
13113
13652
|
response.writeHead(404, { "content-type": "application/json" });
|
|
13114
13653
|
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
13115
13654
|
return;
|
|
13116
13655
|
}
|
|
13117
13656
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13118
|
-
response.end(await
|
|
13657
|
+
response.end(await fs19.readFile(reportPath, "utf8"));
|
|
13119
13658
|
return;
|
|
13120
13659
|
}
|
|
13121
13660
|
if (url.pathname === "/api/watch-status") {
|
|
@@ -13198,8 +13737,8 @@ async function startGraphServer(rootDir, port) {
|
|
|
13198
13737
|
return;
|
|
13199
13738
|
}
|
|
13200
13739
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
13201
|
-
const target =
|
|
13202
|
-
const fallback =
|
|
13740
|
+
const target = path24.join(paths.viewerDistDir, relativePath);
|
|
13741
|
+
const fallback = path24.join(paths.viewerDistDir, "index.html");
|
|
13203
13742
|
const filePath = await fileExists(target) ? target : fallback;
|
|
13204
13743
|
if (!await fileExists(filePath)) {
|
|
13205
13744
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -13207,7 +13746,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
13207
13746
|
return;
|
|
13208
13747
|
}
|
|
13209
13748
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
13210
|
-
response.end(await
|
|
13749
|
+
response.end(await fs19.readFile(filePath));
|
|
13211
13750
|
});
|
|
13212
13751
|
await new Promise((resolve) => {
|
|
13213
13752
|
server.listen(effectivePort, resolve);
|
|
@@ -13234,7 +13773,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13234
13773
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
13235
13774
|
}
|
|
13236
13775
|
await ensureViewerDist(paths.viewerDistDir);
|
|
13237
|
-
const indexPath =
|
|
13776
|
+
const indexPath = path24.join(paths.viewerDistDir, "index.html");
|
|
13238
13777
|
if (!await fileExists(indexPath)) {
|
|
13239
13778
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
13240
13779
|
}
|
|
@@ -13248,6 +13787,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13248
13787
|
kind: page.kind,
|
|
13249
13788
|
status: page.status,
|
|
13250
13789
|
sourceType: page.sourceType,
|
|
13790
|
+
sourceClass: page.sourceClass,
|
|
13251
13791
|
projectIds: page.projectIds,
|
|
13252
13792
|
content: loaded.content,
|
|
13253
13793
|
assets: await Promise.all(
|
|
@@ -13259,17 +13799,17 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13259
13799
|
} : null;
|
|
13260
13800
|
})
|
|
13261
13801
|
);
|
|
13262
|
-
const rawHtml = await
|
|
13802
|
+
const rawHtml = await fs19.readFile(indexPath, "utf8");
|
|
13263
13803
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
13264
13804
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
13265
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
13266
|
-
const stylePath = styleMatch?.[1] ?
|
|
13805
|
+
const scriptPath = scriptMatch?.[1] ? path24.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
|
|
13806
|
+
const stylePath = styleMatch?.[1] ? path24.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
|
|
13267
13807
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
13268
13808
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
13269
13809
|
}
|
|
13270
|
-
const script = await
|
|
13271
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
13272
|
-
const report = await readJsonFile(
|
|
13810
|
+
const script = await fs19.readFile(scriptPath, "utf8");
|
|
13811
|
+
const style = stylePath && await fileExists(stylePath) ? await fs19.readFile(stylePath, "utf8") : "";
|
|
13812
|
+
const report = await readJsonFile(path24.join(paths.wikiDir, "graph", "report.json"));
|
|
13273
13813
|
const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean), report }, null, 2).replace(/</g, "\\u003c");
|
|
13274
13814
|
const html = [
|
|
13275
13815
|
"<!doctype html>",
|
|
@@ -13288,9 +13828,9 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13288
13828
|
"</html>",
|
|
13289
13829
|
""
|
|
13290
13830
|
].filter(Boolean).join("\n");
|
|
13291
|
-
await
|
|
13292
|
-
await
|
|
13293
|
-
return
|
|
13831
|
+
await fs19.mkdir(path24.dirname(outputPath), { recursive: true });
|
|
13832
|
+
await fs19.writeFile(outputPath, html, "utf8");
|
|
13833
|
+
return path24.resolve(outputPath);
|
|
13294
13834
|
}
|
|
13295
13835
|
export {
|
|
13296
13836
|
acceptApproval,
|