@swarmvaultai/engine 0.1.25 → 0.1.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/chunk-LEUV6TWJ.js +1131 -0
- package/dist/index.d.ts +41 -1
- package/dist/index.js +961 -414
- package/dist/registry-YGVTLIZH.js +12 -0
- package/dist/viewer/assets/index-C7PCTMog.js +330 -0
- package/dist/viewer/assets/index-DiMCbjBi.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/lib.d.ts +17 -0
- package/dist/viewer/lib.js +8 -4
- package/package.json +7 -7
- package/dist/viewer/assets/index-CmEm2Pd_.js +0 -330
- package/dist/viewer/assets/index-mRA-6D-Z.css +0 -1
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,8 @@ 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
|
+
import { pathToFileURL } from "url";
|
|
666
667
|
import { Readability } from "@mozilla/readability";
|
|
667
668
|
import matter3 from "gray-matter";
|
|
668
669
|
import ignore from "ignore";
|
|
@@ -3143,9 +3144,68 @@ async function appendWatchRun(rootDir, run) {
|
|
|
3143
3144
|
await appendJsonLine(paths.jobsLogPath, run);
|
|
3144
3145
|
}
|
|
3145
3146
|
|
|
3147
|
+
// src/source-classification.ts
|
|
3148
|
+
import path8 from "path";
|
|
3149
|
+
var ALL_SOURCE_CLASSES = ["first_party", "third_party", "resource", "generated"];
|
|
3150
|
+
var THIRD_PARTY_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "vendor", "Pods"]);
|
|
3151
|
+
var GENERATED_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", ".next", "coverage", "DerivedData", "target"]);
|
|
3152
|
+
function matchesAnyGlob(relativePath, patterns) {
|
|
3153
|
+
return patterns.some(
|
|
3154
|
+
(pattern) => path8.matchesGlob(relativePath, pattern) || path8.matchesGlob(path8.posix.basename(relativePath), pattern)
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3157
|
+
function classifyRepoPath(relativePath, repoAnalysis) {
|
|
3158
|
+
const normalized = relativePath.replace(/\\/g, "/");
|
|
3159
|
+
const custom = repoAnalysis?.classifyGlobs;
|
|
3160
|
+
if (custom?.first_party?.length && matchesAnyGlob(normalized, custom.first_party)) {
|
|
3161
|
+
return "first_party";
|
|
3162
|
+
}
|
|
3163
|
+
for (const sourceClass of ["third_party", "resource", "generated"]) {
|
|
3164
|
+
const patterns = custom?.[sourceClass];
|
|
3165
|
+
if (patterns?.length && matchesAnyGlob(normalized, patterns)) {
|
|
3166
|
+
return sourceClass;
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
3170
|
+
if (segments.some((segment) => THIRD_PARTY_SEGMENTS.has(segment))) {
|
|
3171
|
+
return "third_party";
|
|
3172
|
+
}
|
|
3173
|
+
if (segments.some((segment) => GENERATED_SEGMENTS.has(segment))) {
|
|
3174
|
+
return "generated";
|
|
3175
|
+
}
|
|
3176
|
+
if (segments.some((segment) => segment.endsWith(".xcassets") || segment.endsWith(".imageset"))) {
|
|
3177
|
+
return "resource";
|
|
3178
|
+
}
|
|
3179
|
+
return "first_party";
|
|
3180
|
+
}
|
|
3181
|
+
function normalizeExtractClasses(repoAnalysis, extra = []) {
|
|
3182
|
+
const configured = repoAnalysis?.extractClasses?.length ? repoAnalysis.extractClasses : ["first_party"];
|
|
3183
|
+
return ALL_SOURCE_CLASSES.filter((sourceClass) => (/* @__PURE__ */ new Set([...configured, ...extra])).has(sourceClass));
|
|
3184
|
+
}
|
|
3185
|
+
function aggregateSourceClass(values) {
|
|
3186
|
+
const available = ALL_SOURCE_CLASSES.filter((sourceClass) => values.includes(sourceClass));
|
|
3187
|
+
if (!available.length) {
|
|
3188
|
+
return void 0;
|
|
3189
|
+
}
|
|
3190
|
+
if (available.includes("first_party")) {
|
|
3191
|
+
return "first_party";
|
|
3192
|
+
}
|
|
3193
|
+
if (available.includes("resource")) {
|
|
3194
|
+
return "resource";
|
|
3195
|
+
}
|
|
3196
|
+
if (available.includes("third_party")) {
|
|
3197
|
+
return "third_party";
|
|
3198
|
+
}
|
|
3199
|
+
return "generated";
|
|
3200
|
+
}
|
|
3201
|
+
function aggregateManifestSourceClass(manifests, sourceIds) {
|
|
3202
|
+
const byId = new Map(manifests.map((manifest) => [manifest.sourceId, manifest.sourceClass]));
|
|
3203
|
+
return aggregateSourceClass(sourceIds.map((sourceId) => byId.get(sourceId)));
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3146
3206
|
// src/watch-state.ts
|
|
3147
3207
|
import fs8 from "fs/promises";
|
|
3148
|
-
import
|
|
3208
|
+
import path9 from "path";
|
|
3149
3209
|
import matter2 from "gray-matter";
|
|
3150
3210
|
function pendingEntryKey(entry) {
|
|
3151
3211
|
return entry.path;
|
|
@@ -3159,7 +3219,7 @@ function normalizeRelativePath(rootDir, filePath) {
|
|
|
3159
3219
|
if (!filePath) {
|
|
3160
3220
|
return void 0;
|
|
3161
3221
|
}
|
|
3162
|
-
return toPosix(
|
|
3222
|
+
return toPosix(path9.relative(rootDir, path9.resolve(filePath)));
|
|
3163
3223
|
}
|
|
3164
3224
|
async function readPendingSemanticRefresh(rootDir) {
|
|
3165
3225
|
const { paths } = await initWorkspace(rootDir);
|
|
@@ -3253,7 +3313,7 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
3253
3313
|
if (page.freshness !== "stale" || !page.sourceIds.some((sourceId) => affectedSourceIds.has(sourceId))) {
|
|
3254
3314
|
continue;
|
|
3255
3315
|
}
|
|
3256
|
-
const absolutePath =
|
|
3316
|
+
const absolutePath = path9.join(paths.wikiDir, page.path);
|
|
3257
3317
|
if (!await fileExists(absolutePath)) {
|
|
3258
3318
|
continue;
|
|
3259
3319
|
}
|
|
@@ -3272,7 +3332,7 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
3272
3332
|
// src/ingest.ts
|
|
3273
3333
|
var DEFAULT_MAX_ASSET_SIZE = 10 * 1024 * 1024;
|
|
3274
3334
|
var DEFAULT_MAX_DIRECTORY_FILES = 5e3;
|
|
3275
|
-
var
|
|
3335
|
+
var HARD_REPO_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
3276
3336
|
function uniqueStrings(values) {
|
|
3277
3337
|
return [...new Set(values.filter(Boolean))];
|
|
3278
3338
|
}
|
|
@@ -3283,6 +3343,9 @@ function inferKind(mimeType, filePath) {
|
|
|
3283
3343
|
if (mimeType.includes("markdown")) {
|
|
3284
3344
|
return "markdown";
|
|
3285
3345
|
}
|
|
3346
|
+
if (mimeType.includes("html")) {
|
|
3347
|
+
return "html";
|
|
3348
|
+
}
|
|
3286
3349
|
if (mimeType.startsWith("text/")) {
|
|
3287
3350
|
return "text";
|
|
3288
3351
|
}
|
|
@@ -3292,9 +3355,6 @@ function inferKind(mimeType, filePath) {
|
|
|
3292
3355
|
if (mimeType.startsWith("image/")) {
|
|
3293
3356
|
return "image";
|
|
3294
3357
|
}
|
|
3295
|
-
if (mimeType.includes("html")) {
|
|
3296
|
-
return "html";
|
|
3297
|
-
}
|
|
3298
3358
|
return "binary";
|
|
3299
3359
|
}
|
|
3300
3360
|
function titleFromText(fallback, content) {
|
|
@@ -3308,36 +3368,47 @@ function normalizeIngestOptions(options) {
|
|
|
3308
3368
|
return {
|
|
3309
3369
|
includeAssets: options?.includeAssets ?? true,
|
|
3310
3370
|
maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
|
|
3311
|
-
repoRoot: options?.repoRoot ?
|
|
3371
|
+
repoRoot: options?.repoRoot ? path10.resolve(options.repoRoot) : void 0,
|
|
3312
3372
|
include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
3313
3373
|
exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
3314
3374
|
maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
|
|
3315
|
-
gitignore: options?.gitignore ?? true
|
|
3375
|
+
gitignore: options?.gitignore ?? true,
|
|
3376
|
+
extractClasses: options?.extractClasses ?? ["first_party"]
|
|
3316
3377
|
};
|
|
3317
3378
|
}
|
|
3318
|
-
function
|
|
3379
|
+
async function resolveRepoIngestOptions(rootDir, options) {
|
|
3380
|
+
const normalized = normalizeIngestOptions(options);
|
|
3381
|
+
const { config } = await loadVaultConfig(rootDir);
|
|
3382
|
+
const repoAnalysis = config.repoAnalysis;
|
|
3383
|
+
return {
|
|
3384
|
+
...normalized,
|
|
3385
|
+
extractClasses: normalizeExtractClasses(repoAnalysis, normalized.extractClasses),
|
|
3386
|
+
repoAnalysis
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
function matchesAnyGlob2(relativePath, patterns) {
|
|
3319
3390
|
return patterns.some(
|
|
3320
|
-
(pattern) =>
|
|
3391
|
+
(pattern) => path10.matchesGlob(relativePath, pattern) || path10.matchesGlob(path10.posix.basename(relativePath), pattern)
|
|
3321
3392
|
);
|
|
3322
3393
|
}
|
|
3323
3394
|
function supportedDirectoryKind(sourceKind) {
|
|
3324
3395
|
return sourceKind !== "binary";
|
|
3325
3396
|
}
|
|
3326
3397
|
async function findNearestGitRoot2(startPath) {
|
|
3327
|
-
let current =
|
|
3398
|
+
let current = path10.resolve(startPath);
|
|
3328
3399
|
try {
|
|
3329
3400
|
const stat = await fs9.stat(current);
|
|
3330
3401
|
if (!stat.isDirectory()) {
|
|
3331
|
-
current =
|
|
3402
|
+
current = path10.dirname(current);
|
|
3332
3403
|
}
|
|
3333
3404
|
} catch {
|
|
3334
|
-
current =
|
|
3405
|
+
current = path10.dirname(current);
|
|
3335
3406
|
}
|
|
3336
3407
|
while (true) {
|
|
3337
|
-
if (await fileExists(
|
|
3408
|
+
if (await fileExists(path10.join(current, ".git"))) {
|
|
3338
3409
|
return current;
|
|
3339
3410
|
}
|
|
3340
|
-
const parent =
|
|
3411
|
+
const parent = path10.dirname(current);
|
|
3341
3412
|
if (parent === current) {
|
|
3342
3413
|
return null;
|
|
3343
3414
|
}
|
|
@@ -3345,26 +3416,26 @@ async function findNearestGitRoot2(startPath) {
|
|
|
3345
3416
|
}
|
|
3346
3417
|
}
|
|
3347
3418
|
function withinRoot(rootPath, targetPath) {
|
|
3348
|
-
const relative =
|
|
3349
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3419
|
+
const relative = path10.relative(rootPath, targetPath);
|
|
3420
|
+
return relative === "" || !relative.startsWith("..") && !path10.isAbsolute(relative);
|
|
3350
3421
|
}
|
|
3351
3422
|
function repoRootFromManifest(manifest) {
|
|
3352
3423
|
if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
|
|
3353
3424
|
return null;
|
|
3354
3425
|
}
|
|
3355
|
-
const repoDir =
|
|
3356
|
-
const fileDir =
|
|
3426
|
+
const repoDir = path10.posix.dirname(manifest.repoRelativePath);
|
|
3427
|
+
const fileDir = path10.dirname(path10.resolve(manifest.originalPath));
|
|
3357
3428
|
if (repoDir === "." || !repoDir) {
|
|
3358
3429
|
return fileDir;
|
|
3359
3430
|
}
|
|
3360
3431
|
const segments = repoDir.split("/").filter(Boolean);
|
|
3361
|
-
return
|
|
3432
|
+
return path10.resolve(fileDir, ...segments.map(() => ".."));
|
|
3362
3433
|
}
|
|
3363
3434
|
function repoRelativePathFor(absolutePath, repoRoot) {
|
|
3364
3435
|
if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
|
|
3365
3436
|
return void 0;
|
|
3366
3437
|
}
|
|
3367
|
-
const relative = toPosix(
|
|
3438
|
+
const relative = toPosix(path10.relative(repoRoot, absolutePath));
|
|
3368
3439
|
return relative && !relative.startsWith("..") ? relative : void 0;
|
|
3369
3440
|
}
|
|
3370
3441
|
function normalizeOriginUrl(input) {
|
|
@@ -3661,7 +3732,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
|
|
|
3661
3732
|
return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
|
|
3662
3733
|
}
|
|
3663
3734
|
function sanitizeAssetRelativePath(value) {
|
|
3664
|
-
const normalized =
|
|
3735
|
+
const normalized = path10.posix.normalize(value.replace(/\\/g, "/"));
|
|
3665
3736
|
const segments = normalized.split("/").filter(Boolean).map((segment) => {
|
|
3666
3737
|
if (segment === ".") {
|
|
3667
3738
|
return "";
|
|
@@ -3681,7 +3752,7 @@ function normalizeLocalReference(value) {
|
|
|
3681
3752
|
return null;
|
|
3682
3753
|
}
|
|
3683
3754
|
const lowered = candidate.toLowerCase();
|
|
3684
|
-
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") ||
|
|
3755
|
+
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path10.isAbsolute(candidate)) {
|
|
3685
3756
|
return null;
|
|
3686
3757
|
}
|
|
3687
3758
|
return candidate.replace(/\\/g, "/");
|
|
@@ -3748,7 +3819,7 @@ async function readManifestByHash(manifestsDir, contentHash) {
|
|
|
3748
3819
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
3749
3820
|
continue;
|
|
3750
3821
|
}
|
|
3751
|
-
const manifest = await readJsonFile(
|
|
3822
|
+
const manifest = await readJsonFile(path10.join(manifestsDir, entry.name));
|
|
3752
3823
|
if (manifest?.contentHash === contentHash) {
|
|
3753
3824
|
return manifest;
|
|
3754
3825
|
}
|
|
@@ -3761,7 +3832,7 @@ async function readManifestByOrigin(manifestsDir, prepared) {
|
|
|
3761
3832
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
3762
3833
|
continue;
|
|
3763
3834
|
}
|
|
3764
|
-
const manifest = await readJsonFile(
|
|
3835
|
+
const manifest = await readJsonFile(path10.join(manifestsDir, entry.name));
|
|
3765
3836
|
if (manifest && manifestMatchesOrigin(manifest, prepared)) {
|
|
3766
3837
|
return manifest;
|
|
3767
3838
|
}
|
|
@@ -3772,7 +3843,7 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
|
3772
3843
|
if (!enabled) {
|
|
3773
3844
|
return null;
|
|
3774
3845
|
}
|
|
3775
|
-
const gitignorePath =
|
|
3846
|
+
const gitignorePath = path10.join(repoRoot, ".gitignore");
|
|
3776
3847
|
if (!await fileExists(gitignorePath)) {
|
|
3777
3848
|
return null;
|
|
3778
3849
|
}
|
|
@@ -3782,12 +3853,15 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
|
3782
3853
|
}
|
|
3783
3854
|
function builtInIgnoreReason(relativePath) {
|
|
3784
3855
|
for (const segment of relativePath.split("/")) {
|
|
3785
|
-
if (
|
|
3856
|
+
if (HARD_REPO_IGNORES.has(segment)) {
|
|
3786
3857
|
return `built_in_ignore:${segment}`;
|
|
3787
3858
|
}
|
|
3788
3859
|
}
|
|
3789
3860
|
return null;
|
|
3790
3861
|
}
|
|
3862
|
+
function sourceClassForRelativePath(relativePath, options) {
|
|
3863
|
+
return classifyRepoPath(relativePath, options.repoAnalysis);
|
|
3864
|
+
}
|
|
3791
3865
|
async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
3792
3866
|
const matcher = await loadGitignoreMatcher(repoRoot, options.gitignore);
|
|
3793
3867
|
const skipped = [];
|
|
@@ -3801,20 +3875,20 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
3801
3875
|
const entries = await fs9.readdir(currentDir, { withFileTypes: true });
|
|
3802
3876
|
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
3803
3877
|
for (const entry of entries) {
|
|
3804
|
-
const absolutePath =
|
|
3805
|
-
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
3878
|
+
const absolutePath = path10.join(currentDir, entry.name);
|
|
3879
|
+
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(inputDir, absolutePath));
|
|
3806
3880
|
const relativePath = relativeToRepo || entry.name;
|
|
3807
3881
|
const builtInReason = builtInIgnoreReason(relativePath);
|
|
3808
3882
|
if (builtInReason) {
|
|
3809
|
-
skipped.push({ path: toPosix(
|
|
3883
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: builtInReason });
|
|
3810
3884
|
continue;
|
|
3811
3885
|
}
|
|
3812
3886
|
if (matcher?.ignores(relativePath)) {
|
|
3813
|
-
skipped.push({ path: toPosix(
|
|
3887
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "gitignore" });
|
|
3814
3888
|
continue;
|
|
3815
3889
|
}
|
|
3816
|
-
if (
|
|
3817
|
-
skipped.push({ path: toPosix(
|
|
3890
|
+
if (matchesAnyGlob2(relativePath, options.exclude)) {
|
|
3891
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "exclude_glob" });
|
|
3818
3892
|
continue;
|
|
3819
3893
|
}
|
|
3820
3894
|
if (entry.isDirectory()) {
|
|
@@ -3822,21 +3896,26 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
3822
3896
|
continue;
|
|
3823
3897
|
}
|
|
3824
3898
|
if (!entry.isFile()) {
|
|
3825
|
-
skipped.push({ path: toPosix(
|
|
3899
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
|
|
3826
3900
|
continue;
|
|
3827
3901
|
}
|
|
3828
|
-
if (options.include.length > 0 && !
|
|
3829
|
-
skipped.push({ path: toPosix(
|
|
3902
|
+
if (options.include.length > 0 && !matchesAnyGlob2(relativePath, options.include)) {
|
|
3903
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "include_glob" });
|
|
3830
3904
|
continue;
|
|
3831
3905
|
}
|
|
3832
3906
|
const mimeType = guessMimeType(absolutePath);
|
|
3833
3907
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
3908
|
+
const sourceClass = sourceClassForRelativePath(relativePath, options);
|
|
3834
3909
|
if (!supportedDirectoryKind(sourceKind)) {
|
|
3835
|
-
skipped.push({ path: toPosix(
|
|
3910
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
3911
|
+
continue;
|
|
3912
|
+
}
|
|
3913
|
+
if (!options.extractClasses.includes(sourceClass)) {
|
|
3914
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
|
|
3836
3915
|
continue;
|
|
3837
3916
|
}
|
|
3838
3917
|
if (files.length >= options.maxFiles) {
|
|
3839
|
-
skipped.push({ path: toPosix(
|
|
3918
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "max_files" });
|
|
3840
3919
|
continue;
|
|
3841
3920
|
}
|
|
3842
3921
|
files.push(absolutePath);
|
|
@@ -3858,12 +3937,12 @@ function resolveUrlMimeType(input, response) {
|
|
|
3858
3937
|
function buildRemoteAssetRelativePath(assetUrl, mimeType) {
|
|
3859
3938
|
const url = new URL(assetUrl);
|
|
3860
3939
|
const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
|
|
3861
|
-
const extension =
|
|
3862
|
-
const directory =
|
|
3863
|
-
const basename = extension ?
|
|
3940
|
+
const extension = path10.posix.extname(normalized);
|
|
3941
|
+
const directory = path10.posix.dirname(normalized);
|
|
3942
|
+
const basename = extension ? path10.posix.basename(normalized, extension) : path10.posix.basename(normalized);
|
|
3864
3943
|
const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
3865
3944
|
const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
|
|
3866
|
-
return directory === "." ? hashedName :
|
|
3945
|
+
return directory === "." ? hashedName : path10.posix.join(directory, hashedName);
|
|
3867
3946
|
}
|
|
3868
3947
|
async function readResponseBytesWithinLimit(response, maxBytes) {
|
|
3869
3948
|
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
@@ -3990,7 +4069,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
3990
4069
|
const extractionHash = prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact);
|
|
3991
4070
|
const existingByOrigin = await readManifestByOrigin(paths.manifestsDir, prepared);
|
|
3992
4071
|
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) {
|
|
4072
|
+
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
4073
|
return { manifest: existingByOrigin, isNew: false, wasUpdated: false };
|
|
3995
4074
|
}
|
|
3996
4075
|
if (existingByHash) {
|
|
@@ -3999,18 +4078,18 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
3999
4078
|
const previous = existingByOrigin ?? void 0;
|
|
4000
4079
|
const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
|
|
4001
4080
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4002
|
-
const storedPath =
|
|
4003
|
-
const extractedTextPath = prepared.extractedText ?
|
|
4004
|
-
const extractedMetadataPath = prepared.extractionArtifact ?
|
|
4005
|
-
const attachmentsDir =
|
|
4081
|
+
const storedPath = path10.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
|
|
4082
|
+
const extractedTextPath = prepared.extractedText ? path10.join(paths.extractsDir, `${sourceId}.md`) : void 0;
|
|
4083
|
+
const extractedMetadataPath = prepared.extractionArtifact ? path10.join(paths.extractsDir, `${sourceId}.json`) : void 0;
|
|
4084
|
+
const attachmentsDir = path10.join(paths.rawAssetsDir, sourceId);
|
|
4006
4085
|
if (previous?.storedPath) {
|
|
4007
|
-
await fs9.rm(
|
|
4086
|
+
await fs9.rm(path10.resolve(rootDir, previous.storedPath), { force: true });
|
|
4008
4087
|
}
|
|
4009
4088
|
if (previous?.extractedTextPath) {
|
|
4010
|
-
await fs9.rm(
|
|
4089
|
+
await fs9.rm(path10.resolve(rootDir, previous.extractedTextPath), { force: true });
|
|
4011
4090
|
}
|
|
4012
4091
|
if (previous?.extractedMetadataPath) {
|
|
4013
|
-
await fs9.rm(
|
|
4092
|
+
await fs9.rm(path10.resolve(rootDir, previous.extractedMetadataPath), { force: true });
|
|
4014
4093
|
}
|
|
4015
4094
|
await fs9.rm(attachmentsDir, { recursive: true, force: true });
|
|
4016
4095
|
await fs9.writeFile(storedPath, prepared.payloadBytes);
|
|
@@ -4022,11 +4101,11 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4022
4101
|
}
|
|
4023
4102
|
const manifestAttachments = [];
|
|
4024
4103
|
for (const attachment of attachments) {
|
|
4025
|
-
const absoluteAttachmentPath =
|
|
4026
|
-
await ensureDir(
|
|
4104
|
+
const absoluteAttachmentPath = path10.join(attachmentsDir, attachment.relativePath);
|
|
4105
|
+
await ensureDir(path10.dirname(absoluteAttachmentPath));
|
|
4027
4106
|
await fs9.writeFile(absoluteAttachmentPath, attachment.bytes);
|
|
4028
4107
|
manifestAttachments.push({
|
|
4029
|
-
path: toPosix(
|
|
4108
|
+
path: toPosix(path10.relative(rootDir, absoluteAttachmentPath)),
|
|
4030
4109
|
mimeType: attachment.mimeType,
|
|
4031
4110
|
originalPath: attachment.originalPath
|
|
4032
4111
|
});
|
|
@@ -4037,13 +4116,14 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4037
4116
|
originType: prepared.originType,
|
|
4038
4117
|
sourceKind: prepared.sourceKind,
|
|
4039
4118
|
sourceType: prepared.sourceType,
|
|
4119
|
+
sourceClass: prepared.sourceClass,
|
|
4040
4120
|
language: prepared.language,
|
|
4041
4121
|
originalPath: prepared.originalPath,
|
|
4042
4122
|
repoRelativePath: prepared.repoRelativePath,
|
|
4043
4123
|
url: prepared.url,
|
|
4044
|
-
storedPath: toPosix(
|
|
4045
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
4046
|
-
extractedMetadataPath: extractedMetadataPath ? toPosix(
|
|
4124
|
+
storedPath: toPosix(path10.relative(rootDir, storedPath)),
|
|
4125
|
+
extractedTextPath: extractedTextPath ? toPosix(path10.relative(rootDir, extractedTextPath)) : void 0,
|
|
4126
|
+
extractedMetadataPath: extractedMetadataPath ? toPosix(path10.relative(rootDir, extractedMetadataPath)) : void 0,
|
|
4047
4127
|
extractionHash,
|
|
4048
4128
|
mimeType: prepared.mimeType,
|
|
4049
4129
|
contentHash,
|
|
@@ -4051,7 +4131,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4051
4131
|
updatedAt: now,
|
|
4052
4132
|
attachments: manifestAttachments.length ? manifestAttachments : void 0
|
|
4053
4133
|
};
|
|
4054
|
-
await writeJsonFile(
|
|
4134
|
+
await writeJsonFile(path10.join(paths.manifestsDir, `${sourceId}.json`), manifest);
|
|
4055
4135
|
await appendLogEntry(rootDir, "ingest", prepared.title, [
|
|
4056
4136
|
`source_id=${sourceId}`,
|
|
4057
4137
|
`kind=${prepared.sourceKind}`,
|
|
@@ -4069,16 +4149,16 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
4069
4149
|
return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
|
|
4070
4150
|
}
|
|
4071
4151
|
async function removeManifestArtifacts(rootDir, manifest, paths) {
|
|
4072
|
-
await fs9.rm(
|
|
4073
|
-
await fs9.rm(
|
|
4152
|
+
await fs9.rm(path10.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
|
|
4153
|
+
await fs9.rm(path10.resolve(rootDir, manifest.storedPath), { force: true });
|
|
4074
4154
|
if (manifest.extractedTextPath) {
|
|
4075
|
-
await fs9.rm(
|
|
4155
|
+
await fs9.rm(path10.resolve(rootDir, manifest.extractedTextPath), { force: true });
|
|
4076
4156
|
}
|
|
4077
4157
|
if (manifest.extractedMetadataPath) {
|
|
4078
|
-
await fs9.rm(
|
|
4158
|
+
await fs9.rm(path10.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
|
|
4079
4159
|
}
|
|
4080
|
-
await fs9.rm(
|
|
4081
|
-
await fs9.rm(
|
|
4160
|
+
await fs9.rm(path10.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
|
|
4161
|
+
await fs9.rm(path10.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
|
|
4082
4162
|
}
|
|
4083
4163
|
function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
4084
4164
|
const candidates = [
|
|
@@ -4087,14 +4167,14 @@ function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
|
|
|
4087
4167
|
paths.stateDir,
|
|
4088
4168
|
paths.agentDir,
|
|
4089
4169
|
paths.inboxDir,
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4170
|
+
path10.join(rootDir, ".claude"),
|
|
4171
|
+
path10.join(rootDir, ".cursor"),
|
|
4172
|
+
path10.join(rootDir, ".obsidian")
|
|
4093
4173
|
];
|
|
4094
|
-
return candidates.map((candidate) =>
|
|
4174
|
+
return candidates.map((candidate) => path10.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
|
|
4095
4175
|
}
|
|
4096
4176
|
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;
|
|
4177
|
+
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
4178
|
}
|
|
4099
4179
|
function shouldDeferWatchSemanticRefresh(sourceKind) {
|
|
4100
4180
|
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "image";
|
|
@@ -4110,19 +4190,19 @@ async function listTrackedRepoRoots(rootDir) {
|
|
|
4110
4190
|
}
|
|
4111
4191
|
async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
4112
4192
|
const { paths } = await initWorkspace(rootDir);
|
|
4113
|
-
const normalizedOptions =
|
|
4193
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4114
4194
|
const manifests = await listManifests(rootDir);
|
|
4115
4195
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
4116
|
-
(item) =>
|
|
4196
|
+
(item) => path10.resolve(item)
|
|
4117
4197
|
);
|
|
4118
4198
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
4119
4199
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
4120
4200
|
for (const manifest of manifests) {
|
|
4121
4201
|
const repoRoot = repoRootFromManifest(manifest);
|
|
4122
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
4202
|
+
if (!repoRoot || !uniqueRoots.includes(path10.resolve(repoRoot))) {
|
|
4123
4203
|
continue;
|
|
4124
4204
|
}
|
|
4125
|
-
const key =
|
|
4205
|
+
const key = path10.resolve(repoRoot);
|
|
4126
4206
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
4127
4207
|
bucket.push(manifest);
|
|
4128
4208
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -4147,14 +4227,15 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4147
4227
|
skipped.push(
|
|
4148
4228
|
...collected.skipped,
|
|
4149
4229
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
4150
|
-
path: toPosix(
|
|
4230
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4151
4231
|
reason: "workspace_generated"
|
|
4152
4232
|
}))
|
|
4153
4233
|
);
|
|
4154
4234
|
scannedCount += files.length;
|
|
4155
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
4235
|
+
const currentPaths = new Set(files.map((absolutePath) => path10.resolve(absolutePath)));
|
|
4156
4236
|
for (const absolutePath of files) {
|
|
4157
|
-
const
|
|
4237
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4238
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4158
4239
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4159
4240
|
if (result.isNew) {
|
|
4160
4241
|
imported.push(result.manifest);
|
|
@@ -4163,7 +4244,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4163
4244
|
}
|
|
4164
4245
|
}
|
|
4165
4246
|
for (const manifest of repoManifests) {
|
|
4166
|
-
const originalPath = manifest.originalPath ?
|
|
4247
|
+
const originalPath = manifest.originalPath ? path10.resolve(manifest.originalPath) : null;
|
|
4167
4248
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
4168
4249
|
await removeManifestArtifacts(rootDir, manifest, paths);
|
|
4169
4250
|
removed.push(manifest);
|
|
@@ -4171,7 +4252,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4171
4252
|
}
|
|
4172
4253
|
}
|
|
4173
4254
|
if (uniqueRoots.length > 0) {
|
|
4174
|
-
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(
|
|
4255
|
+
await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path10.relative(rootDir, repoRoot)) || ".").join(","), [
|
|
4175
4256
|
`repo_roots=${uniqueRoots.length}`,
|
|
4176
4257
|
`scanned=${scannedCount}`,
|
|
4177
4258
|
`imported=${imported.length}`,
|
|
@@ -4191,19 +4272,19 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
4191
4272
|
}
|
|
4192
4273
|
async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
4193
4274
|
const { paths } = await initWorkspace(rootDir);
|
|
4194
|
-
const normalizedOptions =
|
|
4275
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4195
4276
|
const manifests = await listManifests(rootDir);
|
|
4196
4277
|
const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
|
|
4197
|
-
(item) =>
|
|
4278
|
+
(item) => path10.resolve(item)
|
|
4198
4279
|
);
|
|
4199
4280
|
const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
|
|
4200
4281
|
const manifestsByRepoRoot = /* @__PURE__ */ new Map();
|
|
4201
4282
|
for (const manifest of manifests) {
|
|
4202
4283
|
const repoRoot = repoRootFromManifest(manifest);
|
|
4203
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
4284
|
+
if (!repoRoot || !uniqueRoots.includes(path10.resolve(repoRoot))) {
|
|
4204
4285
|
continue;
|
|
4205
4286
|
}
|
|
4206
|
-
const key =
|
|
4287
|
+
const key = path10.resolve(repoRoot);
|
|
4207
4288
|
const bucket = manifestsByRepoRoot.get(key) ?? [];
|
|
4208
4289
|
bucket.push(manifest);
|
|
4209
4290
|
manifestsByRepoRoot.set(key, bucket);
|
|
@@ -4218,7 +4299,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4218
4299
|
for (const repoRoot of uniqueRoots) {
|
|
4219
4300
|
const repoManifests = manifestsByRepoRoot.get(repoRoot) ?? [];
|
|
4220
4301
|
const manifestsByOriginalPath = new Map(
|
|
4221
|
-
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [
|
|
4302
|
+
repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path10.resolve(manifest.originalPath), manifest])
|
|
4222
4303
|
);
|
|
4223
4304
|
if (!await fileExists(repoRoot)) {
|
|
4224
4305
|
for (const manifest of repoManifests) {
|
|
@@ -4226,7 +4307,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4226
4307
|
pendingSemanticRefresh.push({
|
|
4227
4308
|
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? manifest.storedPath),
|
|
4228
4309
|
repoRoot,
|
|
4229
|
-
path: toPosix(
|
|
4310
|
+
path: toPosix(path10.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
|
|
4230
4311
|
changeType: "removed",
|
|
4231
4312
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4232
4313
|
sourceId: manifest.sourceId,
|
|
@@ -4246,16 +4327,17 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4246
4327
|
skipped.push(
|
|
4247
4328
|
...collected.skipped,
|
|
4248
4329
|
...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
|
|
4249
|
-
path: toPosix(
|
|
4330
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4250
4331
|
reason: "workspace_generated"
|
|
4251
4332
|
}))
|
|
4252
4333
|
);
|
|
4253
4334
|
scannedCount += files.length;
|
|
4254
|
-
const currentPaths = new Set(files.map((absolutePath) =>
|
|
4335
|
+
const currentPaths = new Set(files.map((absolutePath) => path10.resolve(absolutePath)));
|
|
4255
4336
|
for (const absolutePath of files) {
|
|
4256
|
-
const
|
|
4337
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4338
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4257
4339
|
if (shouldDeferWatchSemanticRefresh(prepared.sourceKind)) {
|
|
4258
|
-
const existing = manifestsByOriginalPath.get(
|
|
4340
|
+
const existing = manifestsByOriginalPath.get(path10.resolve(absolutePath));
|
|
4259
4341
|
const contentHash = buildCompositeHash(prepared.payloadBytes, prepared.attachments);
|
|
4260
4342
|
const changed = !existing || !preparedMatchesManifest(existing, prepared, contentHash);
|
|
4261
4343
|
if (changed) {
|
|
@@ -4263,10 +4345,10 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4263
4345
|
id: pendingSemanticRefreshId(
|
|
4264
4346
|
existing ? "modified" : "added",
|
|
4265
4347
|
repoRoot,
|
|
4266
|
-
prepared.repoRelativePath ?? toPosix(
|
|
4348
|
+
prepared.repoRelativePath ?? toPosix(path10.relative(repoRoot, absolutePath))
|
|
4267
4349
|
),
|
|
4268
4350
|
repoRoot,
|
|
4269
|
-
path: toPosix(
|
|
4351
|
+
path: toPosix(path10.relative(rootDir, absolutePath)),
|
|
4270
4352
|
changeType: existing ? "modified" : "added",
|
|
4271
4353
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4272
4354
|
sourceId: existing?.sourceId,
|
|
@@ -4286,13 +4368,13 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4286
4368
|
}
|
|
4287
4369
|
}
|
|
4288
4370
|
for (const manifest of repoManifests) {
|
|
4289
|
-
const originalPath = manifest.originalPath ?
|
|
4371
|
+
const originalPath = manifest.originalPath ? path10.resolve(manifest.originalPath) : null;
|
|
4290
4372
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
4291
4373
|
if (shouldDeferWatchSemanticRefresh(manifest.sourceKind)) {
|
|
4292
4374
|
pendingSemanticRefresh.push({
|
|
4293
|
-
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(
|
|
4375
|
+
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path10.relative(repoRoot, originalPath))),
|
|
4294
4376
|
repoRoot,
|
|
4295
|
-
path: toPosix(
|
|
4377
|
+
path: toPosix(path10.relative(rootDir, originalPath)),
|
|
4296
4378
|
changeType: "removed",
|
|
4297
4379
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4298
4380
|
sourceId: manifest.sourceId,
|
|
@@ -4310,7 +4392,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4310
4392
|
await appendLogEntry(
|
|
4311
4393
|
rootDir,
|
|
4312
4394
|
"sync_repo_watch",
|
|
4313
|
-
uniqueRoots.map((repoRoot) => toPosix(
|
|
4395
|
+
uniqueRoots.map((repoRoot) => toPosix(path10.relative(rootDir, repoRoot)) || ".").join(","),
|
|
4314
4396
|
[
|
|
4315
4397
|
`repo_roots=${uniqueRoots.length}`,
|
|
4316
4398
|
`scanned=${scannedCount}`,
|
|
@@ -4335,26 +4417,32 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
4335
4417
|
staleSourceIds: [...staleSourceIds]
|
|
4336
4418
|
};
|
|
4337
4419
|
}
|
|
4338
|
-
async function prepareFileInput(rootDir, absoluteInput, repoRoot) {
|
|
4420
|
+
async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
4339
4421
|
const payloadBytes = await fs9.readFile(absoluteInput);
|
|
4340
4422
|
const mimeType = guessMimeType(absoluteInput);
|
|
4341
4423
|
const sourceKind = inferKind(mimeType, absoluteInput);
|
|
4342
4424
|
const language = inferCodeLanguage(absoluteInput, mimeType);
|
|
4343
|
-
const storedExtension =
|
|
4425
|
+
const storedExtension = path10.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
4344
4426
|
let title;
|
|
4345
4427
|
let extractedText;
|
|
4346
4428
|
let extractionArtifact;
|
|
4347
4429
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
4348
4430
|
extractedText = payloadBytes.toString("utf8");
|
|
4349
|
-
title = titleFromText(
|
|
4431
|
+
title = titleFromText(path10.basename(absoluteInput, path10.extname(absoluteInput)), extractedText);
|
|
4350
4432
|
extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
|
|
4433
|
+
} else if (sourceKind === "html") {
|
|
4434
|
+
const html = payloadBytes.toString("utf8");
|
|
4435
|
+
const converted = await convertHtmlToMarkdown(html, pathToFileURL(absoluteInput).toString());
|
|
4436
|
+
title = converted.title;
|
|
4437
|
+
extractedText = converted.markdown;
|
|
4438
|
+
extractionArtifact = createHtmlReadabilityExtractionArtifact(sourceKind, mimeType);
|
|
4351
4439
|
} else if (sourceKind === "pdf") {
|
|
4352
|
-
title =
|
|
4440
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4353
4441
|
const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
|
|
4354
4442
|
extractedText = extracted.extractedText;
|
|
4355
4443
|
extractionArtifact = extracted.artifact;
|
|
4356
4444
|
} else if (sourceKind === "image") {
|
|
4357
|
-
title =
|
|
4445
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4358
4446
|
const extracted = await extractImageWithVision(rootDir, {
|
|
4359
4447
|
title,
|
|
4360
4448
|
mimeType,
|
|
@@ -4364,12 +4452,13 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot) {
|
|
|
4364
4452
|
extractedText = extracted.extractedText;
|
|
4365
4453
|
extractionArtifact = extracted.artifact;
|
|
4366
4454
|
} else {
|
|
4367
|
-
title =
|
|
4455
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4368
4456
|
}
|
|
4369
4457
|
return {
|
|
4370
4458
|
title,
|
|
4371
4459
|
originType: "file",
|
|
4372
4460
|
sourceKind,
|
|
4461
|
+
sourceClass,
|
|
4373
4462
|
language,
|
|
4374
4463
|
originalPath: toPosix(absoluteInput),
|
|
4375
4464
|
repoRelativePath: repoRelativePathFor(absoluteInput, repoRoot),
|
|
@@ -4439,7 +4528,7 @@ async function prepareUrlInput(rootDir, input, options) {
|
|
|
4439
4528
|
sourceKind = "markdown";
|
|
4440
4529
|
storedExtension = ".md";
|
|
4441
4530
|
} else {
|
|
4442
|
-
const extension =
|
|
4531
|
+
const extension = path10.extname(inputUrl.pathname);
|
|
4443
4532
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
4444
4533
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
4445
4534
|
extractedText = payloadBytes.toString("utf8");
|
|
@@ -4512,7 +4601,7 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
4512
4601
|
}
|
|
4513
4602
|
const sourceRefs = [];
|
|
4514
4603
|
for (const ref of refs) {
|
|
4515
|
-
const resolved =
|
|
4604
|
+
const resolved = path10.resolve(path10.dirname(absolutePath), ref);
|
|
4516
4605
|
if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
|
|
4517
4606
|
continue;
|
|
4518
4607
|
}
|
|
@@ -4548,7 +4637,7 @@ function rewriteMarkdownReferences(content, replacements) {
|
|
|
4548
4637
|
async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
4549
4638
|
const originalBytes = await fs9.readFile(absolutePath);
|
|
4550
4639
|
const originalText = originalBytes.toString("utf8");
|
|
4551
|
-
const title = titleFromText(
|
|
4640
|
+
const title = titleFromText(path10.basename(absolutePath, path10.extname(absolutePath)), originalText);
|
|
4552
4641
|
const attachments = [];
|
|
4553
4642
|
for (const attachmentRef of attachmentRefs) {
|
|
4554
4643
|
const bytes = await fs9.readFile(attachmentRef.absolutePath);
|
|
@@ -4575,7 +4664,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
4575
4664
|
sourceKind: "markdown",
|
|
4576
4665
|
originalPath: toPosix(absolutePath),
|
|
4577
4666
|
mimeType: "text/markdown",
|
|
4578
|
-
storedExtension:
|
|
4667
|
+
storedExtension: path10.extname(absolutePath) || ".md",
|
|
4579
4668
|
payloadBytes: Buffer.from(rewrittenText, "utf8"),
|
|
4580
4669
|
extractedText: rewrittenText,
|
|
4581
4670
|
extractionArtifact,
|
|
@@ -4590,8 +4679,8 @@ function isSupportedInboxKind(sourceKind) {
|
|
|
4590
4679
|
async function ingestInput(rootDir, input, options) {
|
|
4591
4680
|
const { paths } = await initWorkspace(rootDir);
|
|
4592
4681
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
4593
|
-
const absoluteInput =
|
|
4594
|
-
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ??
|
|
4682
|
+
const absoluteInput = path10.resolve(rootDir, input);
|
|
4683
|
+
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path10.dirname(absoluteInput));
|
|
4595
4684
|
const prepared = isHttpUrl(input) ? await prepareUrlInput(rootDir, input, normalizedOptions) : await prepareFileInput(rootDir, absoluteInput, repoRoot);
|
|
4596
4685
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4597
4686
|
return result.manifest;
|
|
@@ -4680,8 +4769,8 @@ async function addInput(rootDir, input, options = {}) {
|
|
|
4680
4769
|
}
|
|
4681
4770
|
async function ingestDirectory(rootDir, inputDir, options) {
|
|
4682
4771
|
const { paths } = await initWorkspace(rootDir);
|
|
4683
|
-
const normalizedOptions =
|
|
4684
|
-
const absoluteInputDir =
|
|
4772
|
+
const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
|
|
4773
|
+
const absoluteInputDir = path10.resolve(rootDir, inputDir);
|
|
4685
4774
|
const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot2(absoluteInputDir) ?? absoluteInputDir;
|
|
4686
4775
|
if (!await fileExists(absoluteInputDir)) {
|
|
4687
4776
|
throw new Error(`Directory not found: ${absoluteInputDir}`);
|
|
@@ -4690,18 +4779,19 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
4690
4779
|
const imported = [];
|
|
4691
4780
|
const updated = [];
|
|
4692
4781
|
for (const absolutePath of files) {
|
|
4693
|
-
const
|
|
4782
|
+
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path10.relative(repoRoot, absolutePath));
|
|
4783
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
4694
4784
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4695
4785
|
if (result.isNew) {
|
|
4696
4786
|
imported.push(result.manifest);
|
|
4697
4787
|
} else if (result.wasUpdated) {
|
|
4698
4788
|
updated.push(result.manifest);
|
|
4699
4789
|
} else {
|
|
4700
|
-
skipped.push({ path: toPosix(
|
|
4790
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
4701
4791
|
}
|
|
4702
4792
|
}
|
|
4703
|
-
await appendLogEntry(rootDir, "ingest_directory", toPosix(
|
|
4704
|
-
`repo_root=${toPosix(
|
|
4793
|
+
await appendLogEntry(rootDir, "ingest_directory", toPosix(path10.relative(rootDir, absoluteInputDir)) || ".", [
|
|
4794
|
+
`repo_root=${toPosix(path10.relative(rootDir, repoRoot)) || "."}`,
|
|
4705
4795
|
`scanned=${files.length}`,
|
|
4706
4796
|
`imported=${imported.length}`,
|
|
4707
4797
|
`updated=${updated.length}`,
|
|
@@ -4718,7 +4808,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
4718
4808
|
}
|
|
4719
4809
|
async function importInbox(rootDir, inputDir) {
|
|
4720
4810
|
const { paths } = await initWorkspace(rootDir);
|
|
4721
|
-
const effectiveInputDir =
|
|
4811
|
+
const effectiveInputDir = path10.resolve(rootDir, inputDir ?? paths.inboxDir);
|
|
4722
4812
|
if (!await fileExists(effectiveInputDir)) {
|
|
4723
4813
|
throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
|
|
4724
4814
|
}
|
|
@@ -4729,31 +4819,31 @@ async function importInbox(rootDir, inputDir) {
|
|
|
4729
4819
|
const skipped = [];
|
|
4730
4820
|
let attachmentCount = 0;
|
|
4731
4821
|
for (const absolutePath of files) {
|
|
4732
|
-
const basename =
|
|
4822
|
+
const basename = path10.basename(absolutePath);
|
|
4733
4823
|
if (basename.startsWith(".")) {
|
|
4734
|
-
skipped.push({ path: toPosix(
|
|
4824
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "hidden_file" });
|
|
4735
4825
|
continue;
|
|
4736
4826
|
}
|
|
4737
4827
|
if (claimedAttachments.has(absolutePath)) {
|
|
4738
|
-
skipped.push({ path: toPosix(
|
|
4828
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
|
|
4739
4829
|
continue;
|
|
4740
4830
|
}
|
|
4741
4831
|
const mimeType = guessMimeType(absolutePath);
|
|
4742
4832
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
4743
4833
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
4744
|
-
skipped.push({ path: toPosix(
|
|
4834
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
4745
4835
|
continue;
|
|
4746
4836
|
}
|
|
4747
4837
|
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
4748
4838
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
4749
4839
|
if (!result.isNew) {
|
|
4750
|
-
skipped.push({ path: toPosix(
|
|
4840
|
+
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
4751
4841
|
continue;
|
|
4752
4842
|
}
|
|
4753
4843
|
attachmentCount += result.manifest.attachments?.length ?? 0;
|
|
4754
4844
|
imported.push(result.manifest);
|
|
4755
4845
|
}
|
|
4756
|
-
await appendLogEntry(rootDir, "inbox_import", toPosix(
|
|
4846
|
+
await appendLogEntry(rootDir, "inbox_import", toPosix(path10.relative(rootDir, effectiveInputDir)) || ".", [
|
|
4757
4847
|
`scanned=${files.length}`,
|
|
4758
4848
|
`imported=${imported.length}`,
|
|
4759
4849
|
`attachments=${attachmentCount}`,
|
|
@@ -4774,7 +4864,7 @@ async function listManifests(rootDir) {
|
|
|
4774
4864
|
}
|
|
4775
4865
|
const entries = await fs9.readdir(paths.manifestsDir);
|
|
4776
4866
|
const manifests = await Promise.all(
|
|
4777
|
-
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(
|
|
4867
|
+
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path10.join(paths.manifestsDir, entry)))
|
|
4778
4868
|
);
|
|
4779
4869
|
return manifests.filter((manifest) => Boolean(manifest));
|
|
4780
4870
|
}
|
|
@@ -4782,7 +4872,7 @@ async function readExtractedText(rootDir, manifest) {
|
|
|
4782
4872
|
if (!manifest.extractedTextPath) {
|
|
4783
4873
|
return void 0;
|
|
4784
4874
|
}
|
|
4785
|
-
const absolutePath =
|
|
4875
|
+
const absolutePath = path10.resolve(rootDir, manifest.extractedTextPath);
|
|
4786
4876
|
if (!await fileExists(absolutePath)) {
|
|
4787
4877
|
return void 0;
|
|
4788
4878
|
}
|
|
@@ -4792,7 +4882,7 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
4792
4882
|
if (!manifest.extractedMetadataPath) {
|
|
4793
4883
|
return void 0;
|
|
4794
4884
|
}
|
|
4795
|
-
const absolutePath =
|
|
4885
|
+
const absolutePath = path10.resolve(rootDir, manifest.extractedMetadataPath);
|
|
4796
4886
|
if (!await fileExists(absolutePath)) {
|
|
4797
4887
|
return void 0;
|
|
4798
4888
|
}
|
|
@@ -4800,15 +4890,15 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
4800
4890
|
}
|
|
4801
4891
|
|
|
4802
4892
|
// src/mcp.ts
|
|
4803
|
-
import
|
|
4804
|
-
import
|
|
4893
|
+
import fs17 from "fs/promises";
|
|
4894
|
+
import path21 from "path";
|
|
4805
4895
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4806
4896
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4807
4897
|
import { z as z8 } from "zod";
|
|
4808
4898
|
|
|
4809
4899
|
// src/schema.ts
|
|
4810
4900
|
import fs10 from "fs/promises";
|
|
4811
|
-
import
|
|
4901
|
+
import path11 from "path";
|
|
4812
4902
|
function normalizeSchemaContent(content) {
|
|
4813
4903
|
return content.trim() ? content.trim() : defaultVaultSchema().trim();
|
|
4814
4904
|
}
|
|
@@ -4822,7 +4912,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
|
4822
4912
|
};
|
|
4823
4913
|
}
|
|
4824
4914
|
function resolveProjectSchemaPath(rootDir, schemaPath) {
|
|
4825
|
-
return
|
|
4915
|
+
return path11.resolve(rootDir, schemaPath);
|
|
4826
4916
|
}
|
|
4827
4917
|
function composeVaultSchema(root, projectSchemas = []) {
|
|
4828
4918
|
if (!projectSchemas.length) {
|
|
@@ -4838,7 +4928,7 @@ function composeVaultSchema(root, projectSchemas = []) {
|
|
|
4838
4928
|
(schema) => [
|
|
4839
4929
|
`## Project Schema`,
|
|
4840
4930
|
"",
|
|
4841
|
-
`Path: ${toPosix(
|
|
4931
|
+
`Path: ${toPosix(path11.relative(path11.dirname(root.path), schema.path) || schema.path)}`,
|
|
4842
4932
|
"",
|
|
4843
4933
|
schema.content
|
|
4844
4934
|
].join("\n")
|
|
@@ -4914,13 +5004,13 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
4914
5004
|
}
|
|
4915
5005
|
|
|
4916
5006
|
// src/vault.ts
|
|
4917
|
-
import
|
|
4918
|
-
import
|
|
5007
|
+
import fs16 from "fs/promises";
|
|
5008
|
+
import path20 from "path";
|
|
4919
5009
|
import matter9 from "gray-matter";
|
|
4920
5010
|
import { z as z7 } from "zod";
|
|
4921
5011
|
|
|
4922
5012
|
// src/analysis.ts
|
|
4923
|
-
import
|
|
5013
|
+
import path12 from "path";
|
|
4924
5014
|
import { z as z2 } from "zod";
|
|
4925
5015
|
var ANALYSIS_FORMAT_VERSION = 5;
|
|
4926
5016
|
var sourceAnalysisSchema = z2.object({
|
|
@@ -5143,7 +5233,7 @@ function extractionWarningSummary(manifest, extraction) {
|
|
|
5143
5233
|
return `Imported ${manifest.sourceKind} source. Text extraction is not yet available for this source.`;
|
|
5144
5234
|
}
|
|
5145
5235
|
async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
5146
|
-
const cachePath =
|
|
5236
|
+
const cachePath = path12.join(paths.analysesDir, `${manifest.sourceId}.json`);
|
|
5147
5237
|
const cached = await readJsonFile(cachePath);
|
|
5148
5238
|
if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
|
|
5149
5239
|
return cached;
|
|
@@ -5286,6 +5376,7 @@ function graphHash(graph) {
|
|
|
5286
5376
|
type: node.type,
|
|
5287
5377
|
label: node.label,
|
|
5288
5378
|
pageId: node.pageId ?? null,
|
|
5379
|
+
sourceClass: node.sourceClass ?? null,
|
|
5289
5380
|
communityId: node.communityId ?? null,
|
|
5290
5381
|
degree: node.degree ?? null,
|
|
5291
5382
|
bridgeScore: node.bridgeScore ?? null,
|
|
@@ -5300,6 +5391,7 @@ function graphHash(graph) {
|
|
|
5300
5391
|
relation: edge.relation,
|
|
5301
5392
|
status: edge.status,
|
|
5302
5393
|
evidenceClass: edge.evidenceClass,
|
|
5394
|
+
similarityBasis: edge.similarityBasis ?? null,
|
|
5303
5395
|
confidence: edge.confidence,
|
|
5304
5396
|
provenance: [...edge.provenance].sort()
|
|
5305
5397
|
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
@@ -5309,6 +5401,7 @@ function graphHash(graph) {
|
|
|
5309
5401
|
kind: page.kind,
|
|
5310
5402
|
status: page.status,
|
|
5311
5403
|
sourceType: page.sourceType ?? null,
|
|
5404
|
+
sourceClass: page.sourceClass ?? null,
|
|
5312
5405
|
sourceIds: [...page.sourceIds].sort(),
|
|
5313
5406
|
projectIds: [...page.projectIds].sort(),
|
|
5314
5407
|
nodeIds: [...page.nodeIds].sort()
|
|
@@ -5386,7 +5479,7 @@ function conflictConfidence(claimA, claimB) {
|
|
|
5386
5479
|
|
|
5387
5480
|
// src/deep-lint.ts
|
|
5388
5481
|
import fs11 from "fs/promises";
|
|
5389
|
-
import
|
|
5482
|
+
import path15 from "path";
|
|
5390
5483
|
import matter4 from "gray-matter";
|
|
5391
5484
|
import { z as z5 } from "zod";
|
|
5392
5485
|
|
|
@@ -5407,7 +5500,7 @@ function normalizeFindingSeverity(value) {
|
|
|
5407
5500
|
|
|
5408
5501
|
// src/orchestration.ts
|
|
5409
5502
|
import { spawn } from "child_process";
|
|
5410
|
-
import
|
|
5503
|
+
import path13 from "path";
|
|
5411
5504
|
import { z as z3 } from "zod";
|
|
5412
5505
|
var orchestrationRoleResultSchema = z3.object({
|
|
5413
5506
|
summary: z3.string().optional(),
|
|
@@ -5500,7 +5593,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
|
|
|
5500
5593
|
}
|
|
5501
5594
|
async function runCommandRole(rootDir, role, executor, input) {
|
|
5502
5595
|
const [command, ...args] = executor.command;
|
|
5503
|
-
const cwd = executor.cwd ?
|
|
5596
|
+
const cwd = executor.cwd ? path13.resolve(rootDir, executor.cwd) : rootDir;
|
|
5504
5597
|
const child = spawn(command, args, {
|
|
5505
5598
|
cwd,
|
|
5506
5599
|
env: {
|
|
@@ -5594,8 +5687,8 @@ function summarizeRoleQuestions(results) {
|
|
|
5594
5687
|
}
|
|
5595
5688
|
|
|
5596
5689
|
// src/web-search/registry.ts
|
|
5597
|
-
import
|
|
5598
|
-
import { pathToFileURL } from "url";
|
|
5690
|
+
import path14 from "path";
|
|
5691
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5599
5692
|
import { z as z4 } from "zod";
|
|
5600
5693
|
|
|
5601
5694
|
// src/web-search/http-json.ts
|
|
@@ -5692,8 +5785,8 @@ async function createWebSearchAdapter(id, config, rootDir) {
|
|
|
5692
5785
|
if (!config.module) {
|
|
5693
5786
|
throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
|
|
5694
5787
|
}
|
|
5695
|
-
const resolvedModule =
|
|
5696
|
-
const loaded = await import(
|
|
5788
|
+
const resolvedModule = path14.isAbsolute(config.module) ? config.module : path14.resolve(rootDir, config.module);
|
|
5789
|
+
const loaded = await import(pathToFileURL2(resolvedModule).href);
|
|
5697
5790
|
const parsed = customWebSearchModuleSchema.parse(loaded);
|
|
5698
5791
|
return parsed.createAdapter(id, config, rootDir);
|
|
5699
5792
|
}
|
|
@@ -5752,7 +5845,7 @@ async function loadContextPages(rootDir, graph) {
|
|
|
5752
5845
|
);
|
|
5753
5846
|
return Promise.all(
|
|
5754
5847
|
contextPages.slice(0, 18).map(async (page) => {
|
|
5755
|
-
const absolutePath =
|
|
5848
|
+
const absolutePath = path15.join(paths.wikiDir, page.path);
|
|
5756
5849
|
const raw = await fs11.readFile(absolutePath, "utf8").catch(() => "");
|
|
5757
5850
|
const parsed = matter4(raw);
|
|
5758
5851
|
return {
|
|
@@ -5801,7 +5894,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
|
|
|
5801
5894
|
code: "missing_citation",
|
|
5802
5895
|
message: finding.message,
|
|
5803
5896
|
pagePath: finding.pagePath,
|
|
5804
|
-
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${
|
|
5897
|
+
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path15.basename(finding.pagePath, ".md")}?` : void 0
|
|
5805
5898
|
});
|
|
5806
5899
|
}
|
|
5807
5900
|
for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
|
|
@@ -5978,6 +6071,353 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
|
|
|
5978
6071
|
);
|
|
5979
6072
|
}
|
|
5980
6073
|
|
|
6074
|
+
// src/embeddings.ts
|
|
6075
|
+
import fs12 from "fs/promises";
|
|
6076
|
+
import path16 from "path";
|
|
6077
|
+
var MAX_EMBEDDING_BATCH = 32;
|
|
6078
|
+
var MAX_SIMILARITY_NODES = 240;
|
|
6079
|
+
function cosineSimilarity(left, right) {
|
|
6080
|
+
if (!left.length || left.length !== right.length) {
|
|
6081
|
+
return 0;
|
|
6082
|
+
}
|
|
6083
|
+
let dot = 0;
|
|
6084
|
+
let leftNorm = 0;
|
|
6085
|
+
let rightNorm = 0;
|
|
6086
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
6087
|
+
dot += left[index] * right[index];
|
|
6088
|
+
leftNorm += left[index] * left[index];
|
|
6089
|
+
rightNorm += right[index] * right[index];
|
|
6090
|
+
}
|
|
6091
|
+
if (leftNorm <= 0 || rightNorm <= 0) {
|
|
6092
|
+
return 0;
|
|
6093
|
+
}
|
|
6094
|
+
return dot / Math.sqrt(leftNorm * rightNorm);
|
|
6095
|
+
}
|
|
6096
|
+
function appendIfMissing(parts, value) {
|
|
6097
|
+
const normalized = value?.trim();
|
|
6098
|
+
if (!normalized) {
|
|
6099
|
+
return;
|
|
6100
|
+
}
|
|
6101
|
+
if (!parts.includes(normalized)) {
|
|
6102
|
+
parts.push(normalized);
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
async function loadPageContents(rootDir, graph) {
|
|
6106
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6107
|
+
const contents = /* @__PURE__ */ new Map();
|
|
6108
|
+
await Promise.all(
|
|
6109
|
+
graph.pages.map(async (page) => {
|
|
6110
|
+
const absolutePath = path16.join(paths.wikiDir, page.path);
|
|
6111
|
+
const content = await fs12.readFile(absolutePath, "utf8").catch(() => "");
|
|
6112
|
+
contents.set(page.id, content);
|
|
6113
|
+
})
|
|
6114
|
+
);
|
|
6115
|
+
return contents;
|
|
6116
|
+
}
|
|
6117
|
+
function itemTextForNode(node, graph, pageContents) {
|
|
6118
|
+
const page = graph.pages.find((candidate) => candidate.id === node.pageId);
|
|
6119
|
+
const parts = [`node ${node.type}`, node.label];
|
|
6120
|
+
appendIfMissing(parts, node.sourceClass);
|
|
6121
|
+
appendIfMissing(parts, node.language);
|
|
6122
|
+
appendIfMissing(parts, page?.title);
|
|
6123
|
+
appendIfMissing(parts, page?.sourceType);
|
|
6124
|
+
appendIfMissing(parts, page?.sourceClass);
|
|
6125
|
+
if (page) {
|
|
6126
|
+
appendIfMissing(parts, pageContents.get(page.id)?.slice(0, 800));
|
|
6127
|
+
}
|
|
6128
|
+
return parts.join("\n");
|
|
6129
|
+
}
|
|
6130
|
+
function itemTextForPage(page, pageContents) {
|
|
6131
|
+
const parts = [`page ${page.kind}`, page.title, page.path];
|
|
6132
|
+
appendIfMissing(parts, page.sourceType);
|
|
6133
|
+
appendIfMissing(parts, page.sourceClass);
|
|
6134
|
+
appendIfMissing(parts, pageContents.get(page.id)?.slice(0, 1200));
|
|
6135
|
+
return parts.join("\n");
|
|
6136
|
+
}
|
|
6137
|
+
function itemTextForHyperedge(graph, hyperedgeId) {
|
|
6138
|
+
const hyperedge = graph.hyperedges.find((candidate) => candidate.id === hyperedgeId);
|
|
6139
|
+
if (!hyperedge) {
|
|
6140
|
+
return "";
|
|
6141
|
+
}
|
|
6142
|
+
const nodeLabels = hyperedge.nodeIds.map((nodeId) => graph.nodes.find((node) => node.id === nodeId)?.label).filter((value) => Boolean(value));
|
|
6143
|
+
return [hyperedge.label, hyperedge.relation, hyperedge.why, ...nodeLabels].join("\n");
|
|
6144
|
+
}
|
|
6145
|
+
async function buildEmbeddableItems(rootDir, graph) {
|
|
6146
|
+
const pageContents = await loadPageContents(rootDir, graph);
|
|
6147
|
+
return uniqueBy(
|
|
6148
|
+
[
|
|
6149
|
+
...graph.nodes.map(
|
|
6150
|
+
(node) => ({
|
|
6151
|
+
id: node.id,
|
|
6152
|
+
kind: "node",
|
|
6153
|
+
label: node.label,
|
|
6154
|
+
text: itemTextForNode(node, graph, pageContents),
|
|
6155
|
+
match: {
|
|
6156
|
+
type: "node",
|
|
6157
|
+
id: node.id,
|
|
6158
|
+
label: node.label,
|
|
6159
|
+
score: 0
|
|
6160
|
+
}
|
|
6161
|
+
})
|
|
6162
|
+
),
|
|
6163
|
+
...graph.pages.map(
|
|
6164
|
+
(page) => ({
|
|
6165
|
+
id: page.id,
|
|
6166
|
+
kind: "page",
|
|
6167
|
+
label: page.title,
|
|
6168
|
+
text: itemTextForPage(page, pageContents),
|
|
6169
|
+
match: {
|
|
6170
|
+
type: "page",
|
|
6171
|
+
id: page.id,
|
|
6172
|
+
label: page.title,
|
|
6173
|
+
score: 0
|
|
6174
|
+
}
|
|
6175
|
+
})
|
|
6176
|
+
),
|
|
6177
|
+
...(graph.hyperedges ?? []).map(
|
|
6178
|
+
(hyperedge) => ({
|
|
6179
|
+
id: hyperedge.id,
|
|
6180
|
+
kind: "hyperedge",
|
|
6181
|
+
label: hyperedge.label,
|
|
6182
|
+
text: itemTextForHyperedge(graph, hyperedge.id),
|
|
6183
|
+
match: {
|
|
6184
|
+
type: "hyperedge",
|
|
6185
|
+
id: hyperedge.id,
|
|
6186
|
+
label: hyperedge.label,
|
|
6187
|
+
score: 0
|
|
6188
|
+
}
|
|
6189
|
+
})
|
|
6190
|
+
)
|
|
6191
|
+
],
|
|
6192
|
+
(item) => `${item.kind}:${item.id}`
|
|
6193
|
+
).filter((item) => item.text.trim().length > 0);
|
|
6194
|
+
}
|
|
6195
|
+
async function resolveEmbeddingProvider(rootDir) {
|
|
6196
|
+
const { config } = await loadVaultConfig(rootDir);
|
|
6197
|
+
const explicitProviderId = config.tasks.embeddingProvider;
|
|
6198
|
+
if (explicitProviderId) {
|
|
6199
|
+
const providerConfig = config.providers[explicitProviderId];
|
|
6200
|
+
if (!providerConfig) {
|
|
6201
|
+
throw new Error(`No provider configured with id "${explicitProviderId}" for task "embeddingProvider".`);
|
|
6202
|
+
}
|
|
6203
|
+
const provider2 = await createProvider(explicitProviderId, providerConfig, rootDir);
|
|
6204
|
+
if (!provider2.capabilities.has("embeddings") || typeof provider2.embedTexts !== "function") {
|
|
6205
|
+
throw new Error(`Provider ${provider2.id} does not support required capability "embeddings".`);
|
|
6206
|
+
}
|
|
6207
|
+
return provider2;
|
|
6208
|
+
}
|
|
6209
|
+
const queryProviderId = config.tasks.queryProvider;
|
|
6210
|
+
const queryProviderConfig = config.providers[queryProviderId];
|
|
6211
|
+
if (!queryProviderConfig) {
|
|
6212
|
+
return null;
|
|
6213
|
+
}
|
|
6214
|
+
const provider = await createProvider(queryProviderId, queryProviderConfig, rootDir);
|
|
6215
|
+
return provider.capabilities.has("embeddings") && typeof provider.embedTexts === "function" ? provider : null;
|
|
6216
|
+
}
|
|
6217
|
+
async function readEmbeddingCache(rootDir) {
|
|
6218
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6219
|
+
const provider = await resolveEmbeddingProvider(rootDir);
|
|
6220
|
+
if (!provider) {
|
|
6221
|
+
return { artifact: null, provider: null };
|
|
6222
|
+
}
|
|
6223
|
+
const cache = await readJsonFile(paths.embeddingsPath);
|
|
6224
|
+
if (!cache || cache.providerId !== provider.id || cache.providerModel !== provider.model) {
|
|
6225
|
+
return { artifact: null, provider };
|
|
6226
|
+
}
|
|
6227
|
+
return { artifact: cache, provider };
|
|
6228
|
+
}
|
|
6229
|
+
async function writeEmbeddingCache(rootDir, provider, graphHash2, entries) {
|
|
6230
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
6231
|
+
await writeJsonFile(paths.embeddingsPath, {
|
|
6232
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6233
|
+
providerId: provider.id,
|
|
6234
|
+
providerModel: provider.model,
|
|
6235
|
+
graphHash: graphHash2,
|
|
6236
|
+
entries: entries.sort((left, right) => `${left.kind}:${left.itemId}`.localeCompare(`${right.kind}:${right.itemId}`))
|
|
6237
|
+
});
|
|
6238
|
+
}
|
|
6239
|
+
async function embedTexts(provider, texts) {
|
|
6240
|
+
const vectors = [];
|
|
6241
|
+
for (let index = 0; index < texts.length; index += MAX_EMBEDDING_BATCH) {
|
|
6242
|
+
const batch = texts.slice(index, index + MAX_EMBEDDING_BATCH);
|
|
6243
|
+
const nextVectors = await provider.embedTexts(batch);
|
|
6244
|
+
vectors.push(...nextVectors);
|
|
6245
|
+
}
|
|
6246
|
+
return vectors;
|
|
6247
|
+
}
|
|
6248
|
+
async function resolveVectorsForItems(rootDir, graphHash2, items) {
|
|
6249
|
+
const { artifact, provider } = await readEmbeddingCache(rootDir);
|
|
6250
|
+
if (!provider) {
|
|
6251
|
+
return { provider: null, vectors: /* @__PURE__ */ new Map() };
|
|
6252
|
+
}
|
|
6253
|
+
const cachedByKey = new Map(
|
|
6254
|
+
(artifact?.entries ?? []).map((entry) => [`${entry.kind}:${entry.itemId}:${entry.contentHash}`, entry.values])
|
|
6255
|
+
);
|
|
6256
|
+
const vectors = /* @__PURE__ */ new Map();
|
|
6257
|
+
const missing = [];
|
|
6258
|
+
for (const item of items) {
|
|
6259
|
+
const contentHash = sha256(item.text);
|
|
6260
|
+
const cached = cachedByKey.get(`${item.kind}:${item.id}:${contentHash}`);
|
|
6261
|
+
if (cached?.length) {
|
|
6262
|
+
vectors.set(`${item.kind}:${item.id}`, cached);
|
|
6263
|
+
} else {
|
|
6264
|
+
missing.push(item);
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6267
|
+
if (missing.length) {
|
|
6268
|
+
const nextVectors = await embedTexts(
|
|
6269
|
+
provider,
|
|
6270
|
+
missing.map((item) => item.text)
|
|
6271
|
+
);
|
|
6272
|
+
for (let index = 0; index < missing.length; index += 1) {
|
|
6273
|
+
vectors.set(`${missing[index].kind}:${missing[index].id}`, nextVectors[index] ?? []);
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
await writeEmbeddingCache(
|
|
6277
|
+
rootDir,
|
|
6278
|
+
provider,
|
|
6279
|
+
graphHash2,
|
|
6280
|
+
items.map((item) => ({
|
|
6281
|
+
itemId: item.id,
|
|
6282
|
+
kind: item.kind,
|
|
6283
|
+
label: item.label,
|
|
6284
|
+
contentHash: sha256(item.text),
|
|
6285
|
+
values: vectors.get(`${item.kind}:${item.id}`) ?? []
|
|
6286
|
+
}))
|
|
6287
|
+
);
|
|
6288
|
+
return { provider, vectors };
|
|
6289
|
+
}
|
|
6290
|
+
async function semanticGraphMatches(rootDir, graph, question, limit = 12) {
|
|
6291
|
+
const items = await buildEmbeddableItems(rootDir, graph);
|
|
6292
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
6293
|
+
if (!provider) {
|
|
6294
|
+
return [];
|
|
6295
|
+
}
|
|
6296
|
+
const [queryVector] = await provider.embedTexts([question]);
|
|
6297
|
+
if (!Array.isArray(queryVector) || queryVector.length === 0) {
|
|
6298
|
+
return [];
|
|
6299
|
+
}
|
|
6300
|
+
return items.map((item) => ({
|
|
6301
|
+
...item.match,
|
|
6302
|
+
score: Math.max(0, Number((cosineSimilarity(queryVector, vectors.get(`${item.kind}:${item.id}`) ?? []) * 100).toFixed(2)))
|
|
6303
|
+
})).filter((match) => match.score >= 18).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, limit);
|
|
6304
|
+
}
|
|
6305
|
+
function distinctScope(left, right) {
|
|
6306
|
+
const leftSources = new Set(left.sourceIds);
|
|
6307
|
+
const rightSources = new Set(right.sourceIds);
|
|
6308
|
+
return [...leftSources].some((sourceId) => !rightSources.has(sourceId)) || [...rightSources].some((sourceId) => !leftSources.has(sourceId));
|
|
6309
|
+
}
|
|
6310
|
+
function nodePairKey(left, right) {
|
|
6311
|
+
return [left, right].sort((a, b) => a.localeCompare(b)).join("|");
|
|
6312
|
+
}
|
|
6313
|
+
function similarityReasonsForNodes(left, right) {
|
|
6314
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
6315
|
+
if (left.sourceClass && right.sourceClass && left.sourceClass === right.sourceClass) {
|
|
6316
|
+
reasons.add("shared_tag");
|
|
6317
|
+
}
|
|
6318
|
+
if (left.language && right.language && left.language === right.language) {
|
|
6319
|
+
reasons.add("shared_symbol");
|
|
6320
|
+
}
|
|
6321
|
+
return [...reasons].sort((a, b) => a.localeCompare(b));
|
|
6322
|
+
}
|
|
6323
|
+
async function embeddingSimilarityEdges(rootDir, graph) {
|
|
6324
|
+
const candidateNodes = graph.nodes.filter(
|
|
6325
|
+
(node) => (node.type === "source" || node.type === "module" || node.type === "rationale") && node.sourceClass !== "generated"
|
|
6326
|
+
);
|
|
6327
|
+
if (candidateNodes.length < 2 || candidateNodes.length > MAX_SIMILARITY_NODES) {
|
|
6328
|
+
return [];
|
|
6329
|
+
}
|
|
6330
|
+
const items = candidateNodes.map(
|
|
6331
|
+
(node) => ({
|
|
6332
|
+
id: node.id,
|
|
6333
|
+
kind: "node",
|
|
6334
|
+
label: node.label,
|
|
6335
|
+
text: [
|
|
6336
|
+
node.label,
|
|
6337
|
+
node.type,
|
|
6338
|
+
node.sourceClass ?? "",
|
|
6339
|
+
node.language ?? "",
|
|
6340
|
+
graph.pages.find((page) => page.id === node.pageId)?.title ?? ""
|
|
6341
|
+
].filter(Boolean).join("\n"),
|
|
6342
|
+
match: { type: "node", id: node.id, label: node.label, score: 0 }
|
|
6343
|
+
})
|
|
6344
|
+
);
|
|
6345
|
+
const { provider, vectors } = await resolveVectorsForItems(rootDir, graph.generatedAt, items);
|
|
6346
|
+
if (!provider) {
|
|
6347
|
+
return [];
|
|
6348
|
+
}
|
|
6349
|
+
const directPairs = new Set(graph.edges.map((edge) => nodePairKey(edge.source, edge.target)));
|
|
6350
|
+
const edges = [];
|
|
6351
|
+
for (let leftIndex = 0; leftIndex < candidateNodes.length; leftIndex += 1) {
|
|
6352
|
+
const left = candidateNodes[leftIndex];
|
|
6353
|
+
const leftVector = vectors.get(`node:${left.id}`) ?? [];
|
|
6354
|
+
const candidates = candidateNodes.slice(leftIndex + 1).filter((right) => distinctScope(left, right) && !directPairs.has(nodePairKey(left.id, right.id))).map((right) => ({
|
|
6355
|
+
right,
|
|
6356
|
+
score: cosineSimilarity(leftVector, vectors.get(`node:${right.id}`) ?? [])
|
|
6357
|
+
})).filter((candidate) => candidate.score >= 0.82).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
6358
|
+
for (const candidate of candidates) {
|
|
6359
|
+
const right = candidate.right;
|
|
6360
|
+
const reasons = similarityReasonsForNodes(left, right) ?? [];
|
|
6361
|
+
edges.push({
|
|
6362
|
+
id: `similar-embed:${sha256(`${left.id}|${right.id}|${provider.id}`).slice(0, 16)}`,
|
|
6363
|
+
source: left.id,
|
|
6364
|
+
target: right.id,
|
|
6365
|
+
relation: "semantically_similar_to",
|
|
6366
|
+
status: "inferred",
|
|
6367
|
+
evidenceClass: "inferred",
|
|
6368
|
+
confidence: Number(candidate.score.toFixed(3)),
|
|
6369
|
+
provenance: uniqueBy(
|
|
6370
|
+
[...left.sourceIds, ...right.sourceIds].sort((a, b) => a.localeCompare(b)),
|
|
6371
|
+
(value) => value
|
|
6372
|
+
),
|
|
6373
|
+
similarityReasons: reasons.length ? reasons : ["shared_tag"],
|
|
6374
|
+
similarityBasis: "embeddings"
|
|
6375
|
+
});
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
return uniqueBy(edges, (edge) => edge.id).sort((left, right) => right.confidence - left.confidence || left.id.localeCompare(right.id));
|
|
6379
|
+
}
|
|
6380
|
+
function sourceClassBreakdown(graph) {
|
|
6381
|
+
return {
|
|
6382
|
+
first_party: {
|
|
6383
|
+
sources: graph.sources.filter((source) => source.sourceClass === "first_party").length,
|
|
6384
|
+
pages: graph.pages.filter((page) => page.sourceClass === "first_party").length,
|
|
6385
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "first_party").length
|
|
6386
|
+
},
|
|
6387
|
+
third_party: {
|
|
6388
|
+
sources: graph.sources.filter((source) => source.sourceClass === "third_party").length,
|
|
6389
|
+
pages: graph.pages.filter((page) => page.sourceClass === "third_party").length,
|
|
6390
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "third_party").length
|
|
6391
|
+
},
|
|
6392
|
+
resource: {
|
|
6393
|
+
sources: graph.sources.filter((source) => source.sourceClass === "resource").length,
|
|
6394
|
+
pages: graph.pages.filter((page) => page.sourceClass === "resource").length,
|
|
6395
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "resource").length
|
|
6396
|
+
},
|
|
6397
|
+
generated: {
|
|
6398
|
+
sources: graph.sources.filter((source) => source.sourceClass === "generated").length,
|
|
6399
|
+
pages: graph.pages.filter((page) => page.sourceClass === "generated").length,
|
|
6400
|
+
nodes: graph.nodes.filter((node) => node.sourceClass === "generated").length
|
|
6401
|
+
}
|
|
6402
|
+
};
|
|
6403
|
+
}
|
|
6404
|
+
function filterGraphBySourceClass(graph, sourceClass) {
|
|
6405
|
+
const nodeIds = new Set(graph.nodes.filter((node) => node.sourceClass === sourceClass).map((node) => node.id));
|
|
6406
|
+
const pageIds = new Set(graph.pages.filter((page) => page.sourceClass === sourceClass).map((page) => page.id));
|
|
6407
|
+
return {
|
|
6408
|
+
...graph,
|
|
6409
|
+
nodes: graph.nodes.filter((node) => nodeIds.has(node.id)),
|
|
6410
|
+
edges: graph.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)),
|
|
6411
|
+
hyperedges: graph.hyperedges.filter((hyperedge) => hyperedge.nodeIds.every((nodeId) => nodeIds.has(nodeId))),
|
|
6412
|
+
communities: (graph.communities ?? []).map((community) => ({
|
|
6413
|
+
...community,
|
|
6414
|
+
nodeIds: community.nodeIds.filter((nodeId) => nodeIds.has(nodeId))
|
|
6415
|
+
})).filter((community) => community.nodeIds.length > 0),
|
|
6416
|
+
sources: graph.sources.filter((source) => source.sourceClass === sourceClass),
|
|
6417
|
+
pages: graph.pages.filter((page) => pageIds.has(page.id))
|
|
6418
|
+
};
|
|
6419
|
+
}
|
|
6420
|
+
|
|
5981
6421
|
// src/graph-enrichment.ts
|
|
5982
6422
|
var STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
5983
6423
|
"about",
|
|
@@ -6204,7 +6644,8 @@ function buildSemanticSimilarityEdges(nodes, edges, manifests, analyses) {
|
|
|
6204
6644
|
[...left.sourceIds, ...right.sourceIds].sort((a, b) => a.localeCompare(b)),
|
|
6205
6645
|
(value) => value
|
|
6206
6646
|
),
|
|
6207
|
-
similarityReasons: [...reasons.keys()].sort((a, b) => a.localeCompare(b))
|
|
6647
|
+
similarityReasons: [...reasons.keys()].sort((a, b) => a.localeCompare(b)),
|
|
6648
|
+
similarityBasis: "feature_overlap"
|
|
6208
6649
|
}
|
|
6209
6650
|
];
|
|
6210
6651
|
}).sort((left, right) => right.confidence - left.confidence || left.id.localeCompare(right.id));
|
|
@@ -6287,9 +6728,11 @@ function buildModuleFormHyperedges(graph) {
|
|
|
6287
6728
|
];
|
|
6288
6729
|
});
|
|
6289
6730
|
}
|
|
6290
|
-
function enrichGraph(graph, manifests, analyses) {
|
|
6731
|
+
function enrichGraph(graph, manifests, analyses, extraSimilarityEdges = []) {
|
|
6291
6732
|
const similarityEdges = buildSemanticSimilarityEdges(graph.nodes, graph.edges, manifests, analyses);
|
|
6292
|
-
const enrichedEdges = [...graph.edges, ...similarityEdges]
|
|
6733
|
+
const enrichedEdges = uniqueBy([...graph.edges, ...similarityEdges, ...extraSimilarityEdges], (edge) => edge.id).sort(
|
|
6734
|
+
(left, right) => left.id.localeCompare(right.id)
|
|
6735
|
+
);
|
|
6293
6736
|
const hyperedges = uniqueBy(
|
|
6294
6737
|
[
|
|
6295
6738
|
...buildTopicHyperedges({ ...graph, edges: enrichedEdges, hyperedges: [] }),
|
|
@@ -6420,13 +6863,19 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6420
6863
|
const traversal = options?.traversal ?? "bfs";
|
|
6421
6864
|
const budget = Math.max(3, Math.min(options?.budget ?? 12, 50));
|
|
6422
6865
|
const matches = uniqueBy(
|
|
6423
|
-
[
|
|
6866
|
+
[
|
|
6867
|
+
...options?.semanticMatches ?? [],
|
|
6868
|
+
...pageSearchMatches(graph, question, searchResults),
|
|
6869
|
+
...nodeMatches(graph, question),
|
|
6870
|
+
...hyperedgeMatches(graph, question)
|
|
6871
|
+
],
|
|
6424
6872
|
(match) => `${match.type}:${match.id}`
|
|
6425
6873
|
).sort((left, right) => right.score - left.score || left.label.localeCompare(right.label)).slice(0, 12);
|
|
6426
6874
|
const pages = pageById(graph);
|
|
6427
6875
|
const seeds = uniqueBy(
|
|
6428
6876
|
[
|
|
6429
6877
|
...searchResults.flatMap((result) => pages.get(result.pageId)?.nodeIds ?? []),
|
|
6878
|
+
...matches.filter((match) => match.type === "page").flatMap((match) => pages.get(match.id)?.nodeIds ?? []),
|
|
6430
6879
|
...matches.filter((match) => match.type === "node").map((match) => match.id),
|
|
6431
6880
|
...matches.filter((match) => match.type === "hyperedge").flatMap((match) => graph.hyperedges.find((hyperedge) => hyperedge.id === match.id)?.nodeIds ?? [])
|
|
6432
6881
|
],
|
|
@@ -6458,6 +6907,7 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6458
6907
|
const pageIds = uniqueBy(
|
|
6459
6908
|
[
|
|
6460
6909
|
...searchResults.map((result) => result.pageId),
|
|
6910
|
+
...matches.filter((match) => match.type === "page").map((match) => match.id),
|
|
6461
6911
|
...visitedNodeIds.flatMap((nodeId) => {
|
|
6462
6912
|
const node = nodes.get(nodeId);
|
|
6463
6913
|
return node?.pageId ? [node.pageId] : [];
|
|
@@ -6478,7 +6928,7 @@ function queryGraph(graph, question, searchResults, options) {
|
|
|
6478
6928
|
traversal,
|
|
6479
6929
|
seedNodeIds: seeds,
|
|
6480
6930
|
seedPageIds: uniqueBy(
|
|
6481
|
-
searchResults.map((result) => result.pageId),
|
|
6931
|
+
[...searchResults.map((result) => result.pageId), ...matches.filter((match) => match.type === "page").map((match) => match.id)],
|
|
6482
6932
|
(item) => item
|
|
6483
6933
|
),
|
|
6484
6934
|
visitedNodeIds,
|
|
@@ -6729,6 +7179,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6729
7179
|
kind: "source",
|
|
6730
7180
|
title: analysis.title,
|
|
6731
7181
|
...manifest.sourceType ? { source_type: manifest.sourceType } : {},
|
|
7182
|
+
...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
|
|
6732
7183
|
tags: decoratedTags(analysis.code ? ["source", "code"] : ["source"], decorations),
|
|
6733
7184
|
source_ids: [manifest.sourceId],
|
|
6734
7185
|
project_ids: decorations?.projectIds ?? [],
|
|
@@ -6752,6 +7203,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6752
7203
|
`Source ID: \`${manifest.sourceId}\``,
|
|
6753
7204
|
manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
6754
7205
|
...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
|
|
7206
|
+
...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
|
|
6755
7207
|
"",
|
|
6756
7208
|
"## Summary",
|
|
6757
7209
|
"",
|
|
@@ -6797,6 +7249,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
|
|
|
6797
7249
|
title: analysis.title,
|
|
6798
7250
|
kind: "source",
|
|
6799
7251
|
sourceType: manifest.sourceType,
|
|
7252
|
+
sourceClass: manifest.sourceClass,
|
|
6800
7253
|
sourceIds: [manifest.sourceId],
|
|
6801
7254
|
projectIds: decorations?.projectIds ?? [],
|
|
6802
7255
|
nodeIds,
|
|
@@ -6859,6 +7312,7 @@ function buildModulePage(input) {
|
|
|
6859
7312
|
page_id: pageId,
|
|
6860
7313
|
kind: "module",
|
|
6861
7314
|
title,
|
|
7315
|
+
...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
|
|
6862
7316
|
tags: decoratedTags(["module", "code", code.language], { projectIds: input.projectIds, extraTags: input.extraTags }),
|
|
6863
7317
|
source_ids: [manifest.sourceId],
|
|
6864
7318
|
project_ids: input.projectIds ?? [],
|
|
@@ -6890,6 +7344,7 @@ function buildModulePage(input) {
|
|
|
6890
7344
|
`Source ID: \`${manifest.sourceId}\``,
|
|
6891
7345
|
`Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
6892
7346
|
...manifest.repoRelativePath ? [`Repo Path: \`${manifest.repoRelativePath}\``] : [],
|
|
7347
|
+
...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``] : [],
|
|
6893
7348
|
`Language: \`${code.language}\``,
|
|
6894
7349
|
...code.moduleName ? [`Module Name: \`${code.moduleName}\``] : [],
|
|
6895
7350
|
...code.namespace ? [`Namespace/Package: \`${code.namespace}\``] : [],
|
|
@@ -6940,6 +7395,7 @@ function buildModulePage(input) {
|
|
|
6940
7395
|
path: relativePath,
|
|
6941
7396
|
title,
|
|
6942
7397
|
kind: "module",
|
|
7398
|
+
sourceClass: manifest.sourceClass,
|
|
6943
7399
|
sourceIds: [manifest.sourceId],
|
|
6944
7400
|
projectIds: input.projectIds ?? [],
|
|
6945
7401
|
nodeIds,
|
|
@@ -6974,6 +7430,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
|
|
|
6974
7430
|
page_id: pageId,
|
|
6975
7431
|
kind,
|
|
6976
7432
|
title: name,
|
|
7433
|
+
...decorations?.sourceClass ? { source_class: decorations.sourceClass } : {},
|
|
6977
7434
|
tags: decoratedTags(metadata.status === "candidate" ? [kind, "candidate"] : [kind], decorations),
|
|
6978
7435
|
source_ids: sourceIds,
|
|
6979
7436
|
project_ids: decorations?.projectIds ?? [],
|
|
@@ -7015,6 +7472,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
|
|
|
7015
7472
|
path: relativePath,
|
|
7016
7473
|
title: name,
|
|
7017
7474
|
kind,
|
|
7475
|
+
sourceClass: decorations?.sourceClass,
|
|
7018
7476
|
sourceIds,
|
|
7019
7477
|
projectIds: decorations?.projectIds ?? [],
|
|
7020
7478
|
nodeIds: [pageId],
|
|
@@ -7146,15 +7604,15 @@ function sourceTypeForNode(node, pagesById) {
|
|
|
7146
7604
|
return pagesById.get(node.pageId)?.sourceType;
|
|
7147
7605
|
}
|
|
7148
7606
|
function supportingPathDetails(graph, edge) {
|
|
7149
|
-
const
|
|
7607
|
+
const path25 = shortestGraphPath(graph, edge.source, edge.target);
|
|
7150
7608
|
const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
|
|
7151
|
-
const pathEdges =
|
|
7609
|
+
const pathEdges = path25.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
|
|
7152
7610
|
return {
|
|
7153
|
-
pathNodeIds:
|
|
7154
|
-
pathEdgeIds:
|
|
7611
|
+
pathNodeIds: path25.nodeIds,
|
|
7612
|
+
pathEdgeIds: path25.edgeIds,
|
|
7155
7613
|
pathRelations: pathEdges.map((item) => item.relation),
|
|
7156
7614
|
pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
|
|
7157
|
-
pathSummary:
|
|
7615
|
+
pathSummary: path25.summary
|
|
7158
7616
|
};
|
|
7159
7617
|
}
|
|
7160
7618
|
function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
|
|
@@ -7223,7 +7681,7 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
7223
7681
|
}).map((edge) => {
|
|
7224
7682
|
const source = nodesById.get(edge.source);
|
|
7225
7683
|
const target = nodesById.get(edge.target);
|
|
7226
|
-
const
|
|
7684
|
+
const path25 = supportingPathDetails(graph, edge);
|
|
7227
7685
|
const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
|
|
7228
7686
|
return {
|
|
7229
7687
|
id: edge.id,
|
|
@@ -7234,11 +7692,11 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
7234
7692
|
relation: edge.relation,
|
|
7235
7693
|
evidenceClass: edge.evidenceClass,
|
|
7236
7694
|
confidence: edge.confidence,
|
|
7237
|
-
pathNodeIds:
|
|
7238
|
-
pathEdgeIds:
|
|
7239
|
-
pathRelations:
|
|
7240
|
-
pathEvidenceClasses:
|
|
7241
|
-
pathSummary:
|
|
7695
|
+
pathNodeIds: path25.pathNodeIds,
|
|
7696
|
+
pathEdgeIds: path25.pathEdgeIds,
|
|
7697
|
+
pathRelations: path25.pathRelations,
|
|
7698
|
+
pathEvidenceClasses: path25.pathEvidenceClasses,
|
|
7699
|
+
pathSummary: path25.pathSummary,
|
|
7242
7700
|
why: scored.why,
|
|
7243
7701
|
explanation: scored.explanation,
|
|
7244
7702
|
surpriseScore: scored.score
|
|
@@ -7263,10 +7721,12 @@ function suggestedGraphQuestions(graph) {
|
|
|
7263
7721
|
]).slice(0, 6);
|
|
7264
7722
|
}
|
|
7265
7723
|
function buildGraphReportArtifact(input) {
|
|
7266
|
-
const
|
|
7267
|
-
const
|
|
7268
|
-
const
|
|
7269
|
-
const
|
|
7724
|
+
const firstPartyGraph = filterGraphBySourceClass(input.graph, "first_party");
|
|
7725
|
+
const reportGraph = firstPartyGraph.nodes.length ? firstPartyGraph : input.graph;
|
|
7726
|
+
const pagesById = new Map(reportGraph.pages.map((page) => [page.id, page]));
|
|
7727
|
+
const godNodes = reportGraph.nodes.filter((node) => node.isGodNode).sort((left, right) => (right.degree ?? 0) - (left.degree ?? 0)).slice(0, 8);
|
|
7728
|
+
const bridgeNodes = reportGraph.nodes.filter((node) => (node.bridgeScore ?? 0) > 0).sort((left, right) => (right.bridgeScore ?? 0) - (left.bridgeScore ?? 0)).slice(0, 8);
|
|
7729
|
+
const thinCommunities = (reportGraph.communities ?? []).filter((community) => community.nodeIds.length <= 2).map((community) => {
|
|
7270
7730
|
const page = input.communityPages.find((candidate) => candidate.id === `graph:${community.id}`);
|
|
7271
7731
|
return {
|
|
7272
7732
|
id: community.id,
|
|
@@ -7277,8 +7737,19 @@ function buildGraphReportArtifact(input) {
|
|
|
7277
7737
|
title: page?.title
|
|
7278
7738
|
};
|
|
7279
7739
|
});
|
|
7280
|
-
const surprisingConnections = topSurprisingConnections(
|
|
7281
|
-
const groupPatterns = topGroupPatterns(
|
|
7740
|
+
const surprisingConnections = topSurprisingConnections(reportGraph, pagesById);
|
|
7741
|
+
const groupPatterns = topGroupPatterns(reportGraph);
|
|
7742
|
+
const breakdown = sourceClassBreakdown(input.graph);
|
|
7743
|
+
const warnings = [];
|
|
7744
|
+
const nonFirstPartyNodes = input.graph.nodes.length - breakdown.first_party.nodes;
|
|
7745
|
+
if (input.graph.nodes.length >= 1200) {
|
|
7746
|
+
warnings.push(`Large graph detected (${input.graph.nodes.length} nodes). First-party defaults are applied to report highlights.`);
|
|
7747
|
+
}
|
|
7748
|
+
if (nonFirstPartyNodes > 0 && nonFirstPartyNodes / Math.max(1, input.graph.nodes.length) >= 0.25) {
|
|
7749
|
+
warnings.push(
|
|
7750
|
+
`Non-first-party material accounts for ${(nonFirstPartyNodes / Math.max(1, input.graph.nodes.length) * 100).toFixed(1)}% of graph nodes.`
|
|
7751
|
+
);
|
|
7752
|
+
}
|
|
7282
7753
|
return {
|
|
7283
7754
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7284
7755
|
graphHash: input.graphHash,
|
|
@@ -7288,6 +7759,14 @@ function buildGraphReportArtifact(input) {
|
|
|
7288
7759
|
pages: input.graph.pages.length,
|
|
7289
7760
|
communities: input.graph.communities?.length ?? 0
|
|
7290
7761
|
},
|
|
7762
|
+
firstPartyOverview: {
|
|
7763
|
+
nodes: reportGraph.nodes.length,
|
|
7764
|
+
edges: reportGraph.edges.length,
|
|
7765
|
+
pages: reportGraph.pages.length,
|
|
7766
|
+
communities: reportGraph.communities?.length ?? 0
|
|
7767
|
+
},
|
|
7768
|
+
sourceClassBreakdown: breakdown,
|
|
7769
|
+
warnings,
|
|
7291
7770
|
benchmark: input.benchmark ? {
|
|
7292
7771
|
generatedAt: input.benchmark.generatedAt,
|
|
7293
7772
|
stale: input.benchmarkStale ?? false,
|
|
@@ -7383,6 +7862,18 @@ function buildGraphReportPage(input) {
|
|
|
7383
7862
|
`- Edges: ${input.report.overview.edges}`,
|
|
7384
7863
|
`- Pages: ${input.report.overview.pages}`,
|
|
7385
7864
|
`- Communities: ${input.report.overview.communities}`,
|
|
7865
|
+
`- Default Focus: First-party nodes/pages (${input.report.firstPartyOverview.nodes} nodes, ${input.report.firstPartyOverview.edges} edges, ${input.report.firstPartyOverview.pages} pages).`,
|
|
7866
|
+
"",
|
|
7867
|
+
"## Repo Quality Warnings",
|
|
7868
|
+
"",
|
|
7869
|
+
...input.report.warnings.length ? input.report.warnings.map((warning) => `- ${warning}`) : ["- No large-repo warnings."],
|
|
7870
|
+
"",
|
|
7871
|
+
"## Source Class Breakdown",
|
|
7872
|
+
"",
|
|
7873
|
+
`- First-party: ${input.report.sourceClassBreakdown.first_party.sources} sources, ${input.report.sourceClassBreakdown.first_party.pages} pages, ${input.report.sourceClassBreakdown.first_party.nodes} nodes`,
|
|
7874
|
+
`- Third-party: ${input.report.sourceClassBreakdown.third_party.sources} sources, ${input.report.sourceClassBreakdown.third_party.pages} pages, ${input.report.sourceClassBreakdown.third_party.nodes} nodes`,
|
|
7875
|
+
`- Resources: ${input.report.sourceClassBreakdown.resource.sources} sources, ${input.report.sourceClassBreakdown.resource.pages} pages, ${input.report.sourceClassBreakdown.resource.nodes} nodes`,
|
|
7876
|
+
`- Generated: ${input.report.sourceClassBreakdown.generated.sources} sources, ${input.report.sourceClassBreakdown.generated.pages} pages, ${input.report.sourceClassBreakdown.generated.nodes} nodes`,
|
|
7386
7877
|
"",
|
|
7387
7878
|
"## Benchmark Summary",
|
|
7388
7879
|
"",
|
|
@@ -8090,13 +8581,13 @@ function buildOutputAssetManifest(input) {
|
|
|
8090
8581
|
}
|
|
8091
8582
|
|
|
8092
8583
|
// src/outputs.ts
|
|
8093
|
-
import
|
|
8094
|
-
import
|
|
8584
|
+
import fs14 from "fs/promises";
|
|
8585
|
+
import path18 from "path";
|
|
8095
8586
|
import matter7 from "gray-matter";
|
|
8096
8587
|
|
|
8097
8588
|
// src/pages.ts
|
|
8098
|
-
import
|
|
8099
|
-
import
|
|
8589
|
+
import fs13 from "fs/promises";
|
|
8590
|
+
import path17 from "path";
|
|
8100
8591
|
import matter6 from "gray-matter";
|
|
8101
8592
|
function normalizeStringArray(value) {
|
|
8102
8593
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -8121,6 +8612,9 @@ function normalizePageManager(value, fallback = "system") {
|
|
|
8121
8612
|
function normalizeSourceType(value) {
|
|
8122
8613
|
return value === "arxiv" || value === "doi" || value === "tweet" || value === "article" || value === "url" ? value : void 0;
|
|
8123
8614
|
}
|
|
8615
|
+
function normalizeSourceClass(value) {
|
|
8616
|
+
return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
|
|
8617
|
+
}
|
|
8124
8618
|
function normalizeOutputFormat(value, fallback = "markdown") {
|
|
8125
8619
|
return value === "report" || value === "slides" || value === "chart" || value === "image" ? value : fallback;
|
|
8126
8620
|
}
|
|
@@ -8171,7 +8665,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
8171
8665
|
updatedAt: updatedFallback
|
|
8172
8666
|
};
|
|
8173
8667
|
}
|
|
8174
|
-
const content = await
|
|
8668
|
+
const content = await fs13.readFile(absolutePath, "utf8");
|
|
8175
8669
|
const parsed = matter6(content);
|
|
8176
8670
|
return {
|
|
8177
8671
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
@@ -8210,7 +8704,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8210
8704
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8211
8705
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
8212
8706
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
8213
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8707
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path17.basename(relativePath, ".md");
|
|
8214
8708
|
const kind = inferPageKind(relativePath, parsed.data.kind);
|
|
8215
8709
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
8216
8710
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
@@ -8227,6 +8721,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8227
8721
|
title,
|
|
8228
8722
|
kind,
|
|
8229
8723
|
sourceType: normalizeSourceType(parsed.data.source_type),
|
|
8724
|
+
sourceClass: normalizeSourceClass(parsed.data.source_class),
|
|
8230
8725
|
sourceIds,
|
|
8231
8726
|
projectIds,
|
|
8232
8727
|
nodeIds,
|
|
@@ -8250,18 +8745,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
8250
8745
|
};
|
|
8251
8746
|
}
|
|
8252
8747
|
async function loadInsightPages(wikiDir) {
|
|
8253
|
-
const insightsDir =
|
|
8748
|
+
const insightsDir = path17.join(wikiDir, "insights");
|
|
8254
8749
|
if (!await fileExists(insightsDir)) {
|
|
8255
8750
|
return [];
|
|
8256
8751
|
}
|
|
8257
|
-
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) =>
|
|
8752
|
+
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path17.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
|
|
8258
8753
|
const insights = [];
|
|
8259
8754
|
for (const absolutePath of files) {
|
|
8260
|
-
const relativePath = toPosix(
|
|
8261
|
-
const content = await
|
|
8755
|
+
const relativePath = toPosix(path17.relative(wikiDir, absolutePath));
|
|
8756
|
+
const content = await fs13.readFile(absolutePath, "utf8");
|
|
8262
8757
|
const parsed = matter6(content);
|
|
8263
|
-
const stats = await
|
|
8264
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8758
|
+
const stats = await fs13.stat(absolutePath);
|
|
8759
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path17.basename(absolutePath, ".md");
|
|
8265
8760
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
8266
8761
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
8267
8762
|
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
@@ -8279,6 +8774,7 @@ async function loadInsightPages(wikiDir) {
|
|
|
8279
8774
|
path: relativePath,
|
|
8280
8775
|
title,
|
|
8281
8776
|
kind: "insight",
|
|
8777
|
+
sourceClass: normalizeSourceClass(parsed.data.source_class),
|
|
8282
8778
|
sourceIds,
|
|
8283
8779
|
projectIds,
|
|
8284
8780
|
nodeIds,
|
|
@@ -8323,27 +8819,27 @@ function relatedOutputsForPage(targetPage, outputPages) {
|
|
|
8323
8819
|
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
8820
|
}
|
|
8325
8821
|
async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
|
|
8326
|
-
const outputsDir =
|
|
8822
|
+
const outputsDir = path18.join(wikiDir, "outputs");
|
|
8327
8823
|
const root = baseSlug || "output";
|
|
8328
8824
|
let candidate = root;
|
|
8329
8825
|
let counter = 2;
|
|
8330
|
-
while (await fileExists(
|
|
8826
|
+
while (await fileExists(path18.join(outputsDir, `${candidate}.md`))) {
|
|
8331
8827
|
candidate = `${root}-${counter}`;
|
|
8332
8828
|
counter++;
|
|
8333
8829
|
}
|
|
8334
8830
|
return candidate;
|
|
8335
8831
|
}
|
|
8336
8832
|
async function loadSavedOutputPages(wikiDir) {
|
|
8337
|
-
const outputsDir =
|
|
8338
|
-
const entries = await
|
|
8833
|
+
const outputsDir = path18.join(wikiDir, "outputs");
|
|
8834
|
+
const entries = await fs14.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
8339
8835
|
const outputs = [];
|
|
8340
8836
|
for (const entry of entries) {
|
|
8341
8837
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
|
|
8342
8838
|
continue;
|
|
8343
8839
|
}
|
|
8344
|
-
const relativePath =
|
|
8345
|
-
const absolutePath =
|
|
8346
|
-
const content = await
|
|
8840
|
+
const relativePath = path18.posix.join("outputs", entry.name);
|
|
8841
|
+
const absolutePath = path18.join(outputsDir, entry.name);
|
|
8842
|
+
const content = await fs14.readFile(absolutePath, "utf8");
|
|
8347
8843
|
const parsed = matter7(content);
|
|
8348
8844
|
const slug = entry.name.replace(/\.md$/, "");
|
|
8349
8845
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : slug;
|
|
@@ -8356,7 +8852,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
8356
8852
|
const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
|
|
8357
8853
|
const backlinks = normalizeStringArray(parsed.data.backlinks);
|
|
8358
8854
|
const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
|
|
8359
|
-
const stats = await
|
|
8855
|
+
const stats = await fs14.stat(absolutePath);
|
|
8360
8856
|
const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
|
|
8361
8857
|
const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
|
|
8362
8858
|
outputs.push({
|
|
@@ -8394,8 +8890,8 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
8394
8890
|
}
|
|
8395
8891
|
|
|
8396
8892
|
// src/search.ts
|
|
8397
|
-
import
|
|
8398
|
-
import
|
|
8893
|
+
import fs15 from "fs/promises";
|
|
8894
|
+
import path19 from "path";
|
|
8399
8895
|
import matter8 from "gray-matter";
|
|
8400
8896
|
function getDatabaseSync() {
|
|
8401
8897
|
const builtin = process.getBuiltinModule?.("node:sqlite");
|
|
@@ -8417,8 +8913,11 @@ function normalizeStatus(value) {
|
|
|
8417
8913
|
function normalizeSourceType2(value) {
|
|
8418
8914
|
return value === "arxiv" || value === "doi" || value === "tweet" || value === "article" || value === "url" ? value : void 0;
|
|
8419
8915
|
}
|
|
8916
|
+
function normalizeSourceClass2(value) {
|
|
8917
|
+
return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
|
|
8918
|
+
}
|
|
8420
8919
|
async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
8421
|
-
await ensureDir(
|
|
8920
|
+
await ensureDir(path19.dirname(dbPath));
|
|
8422
8921
|
const DatabaseSync = getDatabaseSync();
|
|
8423
8922
|
const db = new DatabaseSync(dbPath);
|
|
8424
8923
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -8433,6 +8932,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8433
8932
|
kind TEXT NOT NULL,
|
|
8434
8933
|
status TEXT NOT NULL,
|
|
8435
8934
|
source_type TEXT NOT NULL,
|
|
8935
|
+
source_class TEXT NOT NULL,
|
|
8436
8936
|
project_ids TEXT NOT NULL,
|
|
8437
8937
|
project_key TEXT NOT NULL
|
|
8438
8938
|
);
|
|
@@ -8446,11 +8946,11 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8446
8946
|
DELETE FROM pages;
|
|
8447
8947
|
`);
|
|
8448
8948
|
const insertPage = db.prepare(
|
|
8449
|
-
"INSERT INTO pages (id, path, title, body, kind, status, source_type, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8949
|
+
"INSERT INTO pages (id, path, title, body, kind, status, source_type, source_class, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
8450
8950
|
);
|
|
8451
8951
|
for (const page of pages) {
|
|
8452
|
-
const absolutePath =
|
|
8453
|
-
const content = await
|
|
8952
|
+
const absolutePath = path19.join(wikiDir, page.path);
|
|
8953
|
+
const content = await fs15.readFile(absolutePath, "utf8");
|
|
8454
8954
|
const parsed = matter8(content);
|
|
8455
8955
|
insertPage.run(
|
|
8456
8956
|
page.id,
|
|
@@ -8460,6 +8960,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
8460
8960
|
page.kind,
|
|
8461
8961
|
page.status,
|
|
8462
8962
|
typeof parsed.data.source_type === "string" ? parsed.data.source_type : "",
|
|
8963
|
+
typeof parsed.data.source_class === "string" ? parsed.data.source_class : "",
|
|
8463
8964
|
JSON.stringify(page.projectIds),
|
|
8464
8965
|
page.projectIds.map((projectId) => `|${projectId}|`).join("")
|
|
8465
8966
|
);
|
|
@@ -8497,6 +8998,10 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8497
8998
|
clauses.push("pages.source_type = ?");
|
|
8498
8999
|
params.push(options.sourceType);
|
|
8499
9000
|
}
|
|
9001
|
+
if (options.sourceClass && options.sourceClass !== "all") {
|
|
9002
|
+
clauses.push("pages.source_class = ?");
|
|
9003
|
+
params.push(options.sourceClass);
|
|
9004
|
+
}
|
|
8500
9005
|
const statement = db.prepare(`
|
|
8501
9006
|
SELECT
|
|
8502
9007
|
pages.id AS pageId,
|
|
@@ -8505,6 +9010,7 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8505
9010
|
pages.kind AS kind,
|
|
8506
9011
|
pages.status AS status,
|
|
8507
9012
|
pages.source_type AS sourceType,
|
|
9013
|
+
pages.source_class AS sourceClass,
|
|
8508
9014
|
pages.project_ids AS projectIds,
|
|
8509
9015
|
snippet(page_search, 1, '[', ']', '...', 16) AS snippet,
|
|
8510
9016
|
bm25(page_search) AS rank
|
|
@@ -8533,6 +9039,7 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
8533
9039
|
kind: normalizeKind(row.kind),
|
|
8534
9040
|
status: normalizeStatus(row.status),
|
|
8535
9041
|
sourceType: normalizeSourceType2(row.sourceType),
|
|
9042
|
+
sourceClass: normalizeSourceClass2(row.sourceClass),
|
|
8536
9043
|
snippet: String(row.snippet ?? ""),
|
|
8537
9044
|
rank: Number(row.rank ?? 0)
|
|
8538
9045
|
}));
|
|
@@ -8560,7 +9067,7 @@ function outputFormatInstruction(format) {
|
|
|
8560
9067
|
}
|
|
8561
9068
|
}
|
|
8562
9069
|
function outputAssetPath(slug, fileName) {
|
|
8563
|
-
return toPosix(
|
|
9070
|
+
return toPosix(path20.join("outputs", "assets", slug, fileName));
|
|
8564
9071
|
}
|
|
8565
9072
|
function outputAssetId(slug, role) {
|
|
8566
9073
|
return `output:${slug}:asset:${role}`;
|
|
@@ -8700,7 +9207,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
8700
9207
|
if (!providerConfig) {
|
|
8701
9208
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
8702
9209
|
}
|
|
8703
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
9210
|
+
const { createProvider: createProvider2 } = await import("./registry-YGVTLIZH.js");
|
|
8704
9211
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
8705
9212
|
}
|
|
8706
9213
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -8898,7 +9405,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
8898
9405
|
};
|
|
8899
9406
|
}
|
|
8900
9407
|
function normalizeProjectRoot(root) {
|
|
8901
|
-
const normalized = toPosix(
|
|
9408
|
+
const normalized = toPosix(path20.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
8902
9409
|
return normalized;
|
|
8903
9410
|
}
|
|
8904
9411
|
function projectEntries(config) {
|
|
@@ -8924,10 +9431,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
8924
9431
|
if (!rawPath) {
|
|
8925
9432
|
return toPosix(manifest.storedPath);
|
|
8926
9433
|
}
|
|
8927
|
-
if (!
|
|
9434
|
+
if (!path20.isAbsolute(rawPath)) {
|
|
8928
9435
|
return normalizeProjectRoot(rawPath);
|
|
8929
9436
|
}
|
|
8930
|
-
const relative = toPosix(
|
|
9437
|
+
const relative = toPosix(path20.relative(rootDir, rawPath));
|
|
8931
9438
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
8932
9439
|
}
|
|
8933
9440
|
function prefixMatches(value, prefix) {
|
|
@@ -9101,7 +9608,7 @@ function pageHashes(pages) {
|
|
|
9101
9608
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
9102
9609
|
}
|
|
9103
9610
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
9104
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
9611
|
+
const existingContent = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
9105
9612
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
9106
9613
|
status: defaults.status ?? "active",
|
|
9107
9614
|
managedBy: defaults.managedBy
|
|
@@ -9139,7 +9646,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
9139
9646
|
return built;
|
|
9140
9647
|
}
|
|
9141
9648
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
9142
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
9649
|
+
const existingContent = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
9143
9650
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
9144
9651
|
status: defaults.status ?? "active",
|
|
9145
9652
|
managedBy: defaults.managedBy
|
|
@@ -9258,7 +9765,11 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
9258
9765
|
communities
|
|
9259
9766
|
};
|
|
9260
9767
|
}
|
|
9768
|
+
function resetGraphNodeMetrics(nodes) {
|
|
9769
|
+
return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
|
|
9770
|
+
}
|
|
9261
9771
|
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
9772
|
+
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
9262
9773
|
const sourceNodes = manifests.map((manifest) => ({
|
|
9263
9774
|
id: `source:${manifest.sourceId}`,
|
|
9264
9775
|
type: "source",
|
|
@@ -9268,6 +9779,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9268
9779
|
confidence: 1,
|
|
9269
9780
|
sourceIds: [manifest.sourceId],
|
|
9270
9781
|
projectIds: scopedProjectIdsFromSources([manifest.sourceId], sourceProjects),
|
|
9782
|
+
sourceClass: manifest.sourceClass,
|
|
9271
9783
|
language: manifest.language
|
|
9272
9784
|
}));
|
|
9273
9785
|
const conceptMap = /* @__PURE__ */ new Map();
|
|
@@ -9284,7 +9796,6 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9284
9796
|
edgesById.add(edge.id);
|
|
9285
9797
|
edges.push(edge);
|
|
9286
9798
|
};
|
|
9287
|
-
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
9288
9799
|
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
9289
9800
|
for (const analysis of analyses) {
|
|
9290
9801
|
for (const concept of analysis.concepts) {
|
|
@@ -9298,7 +9809,8 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9298
9809
|
freshness: "fresh",
|
|
9299
9810
|
confidence: nodeConfidence(sourceIds.length),
|
|
9300
9811
|
sourceIds,
|
|
9301
|
-
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects)
|
|
9812
|
+
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects),
|
|
9813
|
+
sourceClass: aggregateManifestSourceClass(manifests, sourceIds)
|
|
9302
9814
|
});
|
|
9303
9815
|
pushEdge({
|
|
9304
9816
|
id: `${analysis.sourceId}->${concept.id}`,
|
|
@@ -9322,7 +9834,8 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9322
9834
|
freshness: "fresh",
|
|
9323
9835
|
confidence: nodeConfidence(sourceIds.length),
|
|
9324
9836
|
sourceIds,
|
|
9325
|
-
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects)
|
|
9837
|
+
projectIds: scopedProjectIdsFromSources(sourceIds, sourceProjects),
|
|
9838
|
+
sourceClass: aggregateManifestSourceClass(manifests, sourceIds)
|
|
9326
9839
|
});
|
|
9327
9840
|
pushEdge({
|
|
9328
9841
|
id: `${analysis.sourceId}->${entity.id}`,
|
|
@@ -9350,6 +9863,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9350
9863
|
confidence: 1,
|
|
9351
9864
|
sourceIds: [analysis.sourceId],
|
|
9352
9865
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9866
|
+
sourceClass: manifest.sourceClass,
|
|
9353
9867
|
language: analysis.code.language,
|
|
9354
9868
|
moduleId
|
|
9355
9869
|
});
|
|
@@ -9373,6 +9887,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9373
9887
|
confidence: symbol.exported ? 0.88 : 0.74,
|
|
9374
9888
|
sourceIds: [analysis.sourceId],
|
|
9375
9889
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9890
|
+
sourceClass: manifest.sourceClass,
|
|
9376
9891
|
language: analysis.code.language,
|
|
9377
9892
|
moduleId,
|
|
9378
9893
|
symbolKind: symbol.kind
|
|
@@ -9413,6 +9928,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
9413
9928
|
confidence: 1,
|
|
9414
9929
|
sourceIds: [analysis.sourceId],
|
|
9415
9930
|
projectIds: scopedProjectIdsFromSources([analysis.sourceId], sourceProjects),
|
|
9931
|
+
sourceClass: manifest.sourceClass,
|
|
9416
9932
|
language: analysis.code.language,
|
|
9417
9933
|
moduleId
|
|
9418
9934
|
});
|
|
@@ -9608,7 +10124,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9608
10124
|
const benchmark = await readJsonFile(paths.benchmarkPath);
|
|
9609
10125
|
const communityRecords = [];
|
|
9610
10126
|
for (const community of graph.communities ?? []) {
|
|
9611
|
-
const absolutePath =
|
|
10127
|
+
const absolutePath = path20.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
|
|
9612
10128
|
communityRecords.push(
|
|
9613
10129
|
await buildManagedGraphPage(
|
|
9614
10130
|
absolutePath,
|
|
@@ -9636,7 +10152,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9636
10152
|
recentResearchSources: recentResearchSourcePages(graph, previousCompiledAt),
|
|
9637
10153
|
graphHash: graphHash(graph)
|
|
9638
10154
|
});
|
|
9639
|
-
const reportAbsolutePath =
|
|
10155
|
+
const reportAbsolutePath = path20.join(paths.wikiDir, "graph", "report.md");
|
|
9640
10156
|
const reportRecord = await buildManagedGraphPage(
|
|
9641
10157
|
reportAbsolutePath,
|
|
9642
10158
|
{
|
|
@@ -9657,7 +10173,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
9657
10173
|
};
|
|
9658
10174
|
}
|
|
9659
10175
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
9660
|
-
const absolutePath =
|
|
10176
|
+
const absolutePath = path20.resolve(wikiDir, relativePath);
|
|
9661
10177
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
9662
10178
|
if (changed) {
|
|
9663
10179
|
changedPages.push(relativePath);
|
|
@@ -9688,6 +10204,7 @@ function emptyGraphPage(input) {
|
|
|
9688
10204
|
path: input.path,
|
|
9689
10205
|
title: input.title,
|
|
9690
10206
|
kind: input.kind,
|
|
10207
|
+
sourceClass: input.sourceClass,
|
|
9691
10208
|
sourceIds: input.sourceIds,
|
|
9692
10209
|
projectIds: input.projectIds ?? [],
|
|
9693
10210
|
nodeIds: input.nodeIds,
|
|
@@ -9719,29 +10236,29 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
9719
10236
|
paths.graphPath,
|
|
9720
10237
|
paths.codeIndexPath,
|
|
9721
10238
|
paths.searchDbPath,
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
10239
|
+
path20.join(paths.wikiDir, "index.md"),
|
|
10240
|
+
path20.join(paths.wikiDir, "sources", "index.md"),
|
|
10241
|
+
path20.join(paths.wikiDir, "code", "index.md"),
|
|
10242
|
+
path20.join(paths.wikiDir, "concepts", "index.md"),
|
|
10243
|
+
path20.join(paths.wikiDir, "entities", "index.md"),
|
|
10244
|
+
path20.join(paths.wikiDir, "outputs", "index.md"),
|
|
10245
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
10246
|
+
path20.join(paths.wikiDir, "candidates", "index.md")
|
|
9730
10247
|
];
|
|
9731
10248
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
9732
10249
|
return checks.every(Boolean);
|
|
9733
10250
|
}
|
|
9734
10251
|
async function loadAvailableCachedAnalyses(paths, manifests) {
|
|
9735
10252
|
const analyses = await Promise.all(
|
|
9736
|
-
manifests.map(async (manifest) => readJsonFile(
|
|
10253
|
+
manifests.map(async (manifest) => readJsonFile(path20.join(paths.analysesDir, `${manifest.sourceId}.json`)))
|
|
9737
10254
|
);
|
|
9738
10255
|
return analyses.filter((analysis) => Boolean(analysis));
|
|
9739
10256
|
}
|
|
9740
10257
|
function approvalManifestPath(paths, approvalId) {
|
|
9741
|
-
return
|
|
10258
|
+
return path20.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
9742
10259
|
}
|
|
9743
10260
|
function approvalGraphPath(paths, approvalId) {
|
|
9744
|
-
return
|
|
10261
|
+
return path20.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
9745
10262
|
}
|
|
9746
10263
|
async function readApprovalManifest(paths, approvalId) {
|
|
9747
10264
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -9751,7 +10268,7 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
9751
10268
|
return manifest;
|
|
9752
10269
|
}
|
|
9753
10270
|
async function writeApprovalManifest(paths, manifest) {
|
|
9754
|
-
await
|
|
10271
|
+
await fs16.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
9755
10272
|
`, "utf8");
|
|
9756
10273
|
}
|
|
9757
10274
|
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
@@ -9766,7 +10283,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9766
10283
|
continue;
|
|
9767
10284
|
}
|
|
9768
10285
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
9769
|
-
const currentExists = await fileExists(
|
|
10286
|
+
const currentExists = await fileExists(path20.join(paths.wikiDir, file.relativePath));
|
|
9770
10287
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
9771
10288
|
entries.push({
|
|
9772
10289
|
pageId: nextPage.id,
|
|
@@ -9799,7 +10316,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9799
10316
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
9800
10317
|
entries.push({
|
|
9801
10318
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
9802
|
-
title: previousPage?.title ??
|
|
10319
|
+
title: previousPage?.title ?? path20.basename(deletedPath, ".md"),
|
|
9803
10320
|
kind: previousPage?.kind ?? "index",
|
|
9804
10321
|
changeType: "delete",
|
|
9805
10322
|
status: "pending",
|
|
@@ -9811,16 +10328,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
9811
10328
|
}
|
|
9812
10329
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
9813
10330
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
9814
|
-
const approvalDir =
|
|
10331
|
+
const approvalDir = path20.join(paths.approvalsDir, approvalId);
|
|
9815
10332
|
await ensureDir(approvalDir);
|
|
9816
|
-
await ensureDir(
|
|
9817
|
-
await ensureDir(
|
|
10333
|
+
await ensureDir(path20.join(approvalDir, "wiki"));
|
|
10334
|
+
await ensureDir(path20.join(approvalDir, "state"));
|
|
9818
10335
|
for (const file of changedFiles) {
|
|
9819
|
-
const targetPath =
|
|
9820
|
-
await ensureDir(
|
|
9821
|
-
await
|
|
10336
|
+
const targetPath = path20.join(approvalDir, "wiki", file.relativePath);
|
|
10337
|
+
await ensureDir(path20.dirname(targetPath));
|
|
10338
|
+
await fs16.writeFile(targetPath, file.content, "utf8");
|
|
9822
10339
|
}
|
|
9823
|
-
await
|
|
10340
|
+
await fs16.writeFile(path20.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
9824
10341
|
await writeApprovalManifest(paths, {
|
|
9825
10342
|
approvalId,
|
|
9826
10343
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9856,6 +10373,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9856
10373
|
title: modulePageTitle(manifest),
|
|
9857
10374
|
kind: "module",
|
|
9858
10375
|
sourceIds: [manifest.sourceId],
|
|
10376
|
+
sourceClass: manifest.sourceClass,
|
|
9859
10377
|
projectIds: sourceProjectIds,
|
|
9860
10378
|
nodeIds: [analysis.code.moduleId, ...analysis.code.symbols.map((symbol) => symbol.id)],
|
|
9861
10379
|
schemaHash: sourceSchemaHash,
|
|
@@ -9868,6 +10386,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9868
10386
|
title: analysis.title,
|
|
9869
10387
|
kind: "source",
|
|
9870
10388
|
sourceIds: [manifest.sourceId],
|
|
10389
|
+
sourceClass: manifest.sourceClass,
|
|
9871
10390
|
projectIds: sourceProjectIds,
|
|
9872
10391
|
nodeIds: [
|
|
9873
10392
|
`source:${manifest.sourceId}`,
|
|
@@ -9880,7 +10399,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9880
10399
|
confidence: 1
|
|
9881
10400
|
});
|
|
9882
10401
|
const sourceRecord = await buildManagedGraphPage(
|
|
9883
|
-
|
|
10402
|
+
path20.join(paths.wikiDir, preview.path),
|
|
9884
10403
|
{
|
|
9885
10404
|
managedBy: "system",
|
|
9886
10405
|
confidence: 1,
|
|
@@ -9895,7 +10414,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9895
10414
|
modulePreview ?? void 0,
|
|
9896
10415
|
{
|
|
9897
10416
|
projectIds: sourceProjectIds,
|
|
9898
|
-
extraTags: sourceCategoryTags
|
|
10417
|
+
extraTags: sourceCategoryTags,
|
|
10418
|
+
sourceClass: manifest.sourceClass
|
|
9899
10419
|
}
|
|
9900
10420
|
)
|
|
9901
10421
|
);
|
|
@@ -9925,7 +10445,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9925
10445
|
);
|
|
9926
10446
|
records.push(
|
|
9927
10447
|
await buildManagedGraphPage(
|
|
9928
|
-
|
|
10448
|
+
path20.join(paths.wikiDir, modulePreview.path),
|
|
9929
10449
|
{
|
|
9930
10450
|
managedBy: "system",
|
|
9931
10451
|
confidence: 1,
|
|
@@ -9957,9 +10477,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9957
10477
|
const previousEntry = input.previousState?.candidateHistory?.[pageId];
|
|
9958
10478
|
const promoted = previousEntry?.status === "active" || promoteCandidates && shouldPromoteCandidate(previousEntry, sourceIds);
|
|
9959
10479
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
10480
|
+
const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
|
|
9960
10481
|
const fallbackPaths = [
|
|
9961
|
-
|
|
9962
|
-
|
|
10482
|
+
path20.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
10483
|
+
path20.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
9963
10484
|
];
|
|
9964
10485
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
9965
10486
|
const preview = emptyGraphPage({
|
|
@@ -9968,6 +10489,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9968
10489
|
title: aggregate.name,
|
|
9969
10490
|
kind: itemKind,
|
|
9970
10491
|
sourceIds,
|
|
10492
|
+
sourceClass: aggregateSourceClass2,
|
|
9971
10493
|
projectIds,
|
|
9972
10494
|
nodeIds: [pageId],
|
|
9973
10495
|
schemaHash,
|
|
@@ -9976,7 +10498,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
9976
10498
|
status: promoted ? "active" : "candidate"
|
|
9977
10499
|
});
|
|
9978
10500
|
const pageRecord = await buildManagedGraphPage(
|
|
9979
|
-
|
|
10501
|
+
path20.join(paths.wikiDir, relativePath),
|
|
9980
10502
|
{
|
|
9981
10503
|
status: promoted ? "active" : "candidate",
|
|
9982
10504
|
managedBy: "system",
|
|
@@ -10000,7 +10522,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10000
10522
|
aggregate.name,
|
|
10001
10523
|
...aggregate.descriptions,
|
|
10002
10524
|
...aggregate.sourceAnalyses.map((item) => item.summary)
|
|
10003
|
-
])
|
|
10525
|
+
]),
|
|
10526
|
+
sourceClass: aggregateSourceClass2
|
|
10004
10527
|
}
|
|
10005
10528
|
)
|
|
10006
10529
|
);
|
|
@@ -10016,7 +10539,20 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10016
10539
|
}
|
|
10017
10540
|
const compiledPages = records.map((record) => record.page);
|
|
10018
10541
|
const basePages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
10019
|
-
const
|
|
10542
|
+
const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex);
|
|
10543
|
+
const embeddingEdges = await embeddingSimilarityEdges(rootDir, structuralGraph).catch(() => []);
|
|
10544
|
+
const baseGraph = embeddingEdges.length > 0 ? (() => {
|
|
10545
|
+
const edges = uniqueBy([...structuralGraph.edges, ...embeddingEdges], (edge) => edge.id).sort(
|
|
10546
|
+
(left, right) => left.id.localeCompare(right.id)
|
|
10547
|
+
);
|
|
10548
|
+
const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges);
|
|
10549
|
+
return {
|
|
10550
|
+
...structuralGraph,
|
|
10551
|
+
nodes: metrics.nodes,
|
|
10552
|
+
edges,
|
|
10553
|
+
communities: metrics.communities
|
|
10554
|
+
};
|
|
10555
|
+
})() : structuralGraph;
|
|
10020
10556
|
const graphOrientation = await buildGraphOrientationPages(baseGraph, paths, globalSchemaHash, input.previousState?.generatedAt);
|
|
10021
10557
|
records.push(...graphOrientation.records);
|
|
10022
10558
|
const allPages = [...basePages, ...graphOrientation.records.map((record) => record.page)];
|
|
@@ -10057,7 +10593,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10057
10593
|
confidence: 1
|
|
10058
10594
|
}),
|
|
10059
10595
|
content: await buildManagedContent(
|
|
10060
|
-
|
|
10596
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
10061
10597
|
{
|
|
10062
10598
|
managedBy: "system",
|
|
10063
10599
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -10081,7 +10617,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10081
10617
|
records.push({
|
|
10082
10618
|
page: projectIndexRef,
|
|
10083
10619
|
content: await buildManagedContent(
|
|
10084
|
-
|
|
10620
|
+
path20.join(paths.wikiDir, projectIndexRef.path),
|
|
10085
10621
|
{
|
|
10086
10622
|
managedBy: "system",
|
|
10087
10623
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -10109,7 +10645,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10109
10645
|
confidence: 1
|
|
10110
10646
|
}),
|
|
10111
10647
|
content: await buildManagedContent(
|
|
10112
|
-
|
|
10648
|
+
path20.join(paths.wikiDir, "index.md"),
|
|
10113
10649
|
{
|
|
10114
10650
|
managedBy: "system",
|
|
10115
10651
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -10140,7 +10676,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10140
10676
|
confidence: 1
|
|
10141
10677
|
}),
|
|
10142
10678
|
content: await buildManagedContent(
|
|
10143
|
-
|
|
10679
|
+
path20.join(paths.wikiDir, relativePath),
|
|
10144
10680
|
{
|
|
10145
10681
|
managedBy: "system",
|
|
10146
10682
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -10151,12 +10687,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10151
10687
|
}
|
|
10152
10688
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
10153
10689
|
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(
|
|
10690
|
+
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
10691
|
const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
10156
10692
|
const changedFiles = [];
|
|
10157
10693
|
for (const record of records) {
|
|
10158
|
-
const absolutePath =
|
|
10159
|
-
const current = await fileExists(absolutePath) ? await
|
|
10694
|
+
const absolutePath = path20.join(paths.wikiDir, record.page.path);
|
|
10695
|
+
const current = await fileExists(absolutePath) ? await fs16.readFile(absolutePath, "utf8") : null;
|
|
10160
10696
|
if (current !== record.content) {
|
|
10161
10697
|
changedPages.push(record.page.path);
|
|
10162
10698
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -10181,10 +10717,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
10181
10717
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
10182
10718
|
}
|
|
10183
10719
|
for (const relativePath of obsoletePaths) {
|
|
10184
|
-
await
|
|
10720
|
+
await fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true });
|
|
10185
10721
|
}
|
|
10186
10722
|
await writeJsonFile(paths.graphPath, graph);
|
|
10187
|
-
await writeJsonFile(
|
|
10723
|
+
await writeJsonFile(path20.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
10188
10724
|
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
10189
10725
|
await writeJsonFile(paths.compileStatePath, {
|
|
10190
10726
|
generatedAt: graph.generatedAt,
|
|
@@ -10255,17 +10791,17 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10255
10791
|
})
|
|
10256
10792
|
);
|
|
10257
10793
|
await Promise.all([
|
|
10258
|
-
ensureDir(
|
|
10259
|
-
ensureDir(
|
|
10260
|
-
ensureDir(
|
|
10261
|
-
ensureDir(
|
|
10262
|
-
ensureDir(
|
|
10263
|
-
ensureDir(
|
|
10264
|
-
ensureDir(
|
|
10265
|
-
ensureDir(
|
|
10266
|
-
ensureDir(
|
|
10794
|
+
ensureDir(path20.join(paths.wikiDir, "sources")),
|
|
10795
|
+
ensureDir(path20.join(paths.wikiDir, "code")),
|
|
10796
|
+
ensureDir(path20.join(paths.wikiDir, "concepts")),
|
|
10797
|
+
ensureDir(path20.join(paths.wikiDir, "entities")),
|
|
10798
|
+
ensureDir(path20.join(paths.wikiDir, "outputs")),
|
|
10799
|
+
ensureDir(path20.join(paths.wikiDir, "graph")),
|
|
10800
|
+
ensureDir(path20.join(paths.wikiDir, "graph", "communities")),
|
|
10801
|
+
ensureDir(path20.join(paths.wikiDir, "projects")),
|
|
10802
|
+
ensureDir(path20.join(paths.wikiDir, "candidates"))
|
|
10267
10803
|
]);
|
|
10268
|
-
const projectsIndexPath =
|
|
10804
|
+
const projectsIndexPath = path20.join(paths.wikiDir, "projects", "index.md");
|
|
10269
10805
|
await writeFileIfChanged(
|
|
10270
10806
|
projectsIndexPath,
|
|
10271
10807
|
await buildManagedContent(
|
|
@@ -10286,7 +10822,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10286
10822
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
10287
10823
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
10288
10824
|
};
|
|
10289
|
-
const absolutePath =
|
|
10825
|
+
const absolutePath = path20.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
10290
10826
|
await writeFileIfChanged(
|
|
10291
10827
|
absolutePath,
|
|
10292
10828
|
await buildManagedContent(
|
|
@@ -10304,7 +10840,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10304
10840
|
)
|
|
10305
10841
|
);
|
|
10306
10842
|
}
|
|
10307
|
-
const rootIndexPath =
|
|
10843
|
+
const rootIndexPath = path20.join(paths.wikiDir, "index.md");
|
|
10308
10844
|
await writeFileIfChanged(
|
|
10309
10845
|
rootIndexPath,
|
|
10310
10846
|
await buildManagedContent(
|
|
@@ -10325,7 +10861,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10325
10861
|
["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
|
|
10326
10862
|
["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
|
|
10327
10863
|
]) {
|
|
10328
|
-
const absolutePath =
|
|
10864
|
+
const absolutePath = path20.join(paths.wikiDir, relativePath);
|
|
10329
10865
|
await writeFileIfChanged(
|
|
10330
10866
|
absolutePath,
|
|
10331
10867
|
await buildManagedContent(
|
|
@@ -10339,23 +10875,23 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
10339
10875
|
);
|
|
10340
10876
|
}
|
|
10341
10877
|
for (const record of graphOrientation.records) {
|
|
10342
|
-
await writeFileIfChanged(
|
|
10878
|
+
await writeFileIfChanged(path20.join(paths.wikiDir, record.page.path), record.content);
|
|
10343
10879
|
}
|
|
10344
10880
|
if (graphOrientation.report) {
|
|
10345
|
-
await writeJsonFile(
|
|
10881
|
+
await writeJsonFile(path20.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
10346
10882
|
}
|
|
10347
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
10883
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path20.relative(paths.wikiDir, absolutePath)));
|
|
10348
10884
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
10349
10885
|
"projects/index.md",
|
|
10350
10886
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
10351
10887
|
]);
|
|
10352
10888
|
await Promise.all(
|
|
10353
|
-
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) =>
|
|
10889
|
+
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true }))
|
|
10354
10890
|
);
|
|
10355
|
-
const existingGraphPages = (await listFilesRecursive(
|
|
10891
|
+
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
10892
|
const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
|
|
10357
10893
|
await Promise.all(
|
|
10358
|
-
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) =>
|
|
10894
|
+
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs16.rm(path20.join(paths.wikiDir, relativePath), { force: true }))
|
|
10359
10895
|
);
|
|
10360
10896
|
await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
|
|
10361
10897
|
}
|
|
@@ -10375,7 +10911,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
10375
10911
|
confidence: 0.74
|
|
10376
10912
|
}
|
|
10377
10913
|
});
|
|
10378
|
-
const absolutePath =
|
|
10914
|
+
const absolutePath = path20.join(paths.wikiDir, output.page.path);
|
|
10379
10915
|
return {
|
|
10380
10916
|
page: output.page,
|
|
10381
10917
|
savedPath: absolutePath,
|
|
@@ -10387,15 +10923,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
10387
10923
|
async function persistOutputPage(rootDir, input) {
|
|
10388
10924
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10389
10925
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
10390
|
-
await ensureDir(
|
|
10391
|
-
await
|
|
10926
|
+
await ensureDir(path20.dirname(prepared.savedPath));
|
|
10927
|
+
await fs16.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
10392
10928
|
for (const assetFile of prepared.assetFiles) {
|
|
10393
|
-
const assetPath =
|
|
10394
|
-
await ensureDir(
|
|
10929
|
+
const assetPath = path20.join(paths.wikiDir, assetFile.relativePath);
|
|
10930
|
+
await ensureDir(path20.dirname(assetPath));
|
|
10395
10931
|
if (typeof assetFile.content === "string") {
|
|
10396
|
-
await
|
|
10932
|
+
await fs16.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
10397
10933
|
} else {
|
|
10398
|
-
await
|
|
10934
|
+
await fs16.writeFile(assetPath, assetFile.content);
|
|
10399
10935
|
}
|
|
10400
10936
|
}
|
|
10401
10937
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -10416,7 +10952,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
10416
10952
|
confidence: 0.76
|
|
10417
10953
|
}
|
|
10418
10954
|
});
|
|
10419
|
-
const absolutePath =
|
|
10955
|
+
const absolutePath = path20.join(paths.wikiDir, hub.page.path);
|
|
10420
10956
|
return {
|
|
10421
10957
|
page: hub.page,
|
|
10422
10958
|
savedPath: absolutePath,
|
|
@@ -10428,15 +10964,15 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
10428
10964
|
async function persistExploreHub(rootDir, input) {
|
|
10429
10965
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10430
10966
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
10431
|
-
await ensureDir(
|
|
10432
|
-
await
|
|
10967
|
+
await ensureDir(path20.dirname(prepared.savedPath));
|
|
10968
|
+
await fs16.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
10433
10969
|
for (const assetFile of prepared.assetFiles) {
|
|
10434
|
-
const assetPath =
|
|
10435
|
-
await ensureDir(
|
|
10970
|
+
const assetPath = path20.join(paths.wikiDir, assetFile.relativePath);
|
|
10971
|
+
await ensureDir(path20.dirname(assetPath));
|
|
10436
10972
|
if (typeof assetFile.content === "string") {
|
|
10437
|
-
await
|
|
10973
|
+
await fs16.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
10438
10974
|
} else {
|
|
10439
|
-
await
|
|
10975
|
+
await fs16.writeFile(assetPath, assetFile.content);
|
|
10440
10976
|
}
|
|
10441
10977
|
}
|
|
10442
10978
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -10453,17 +10989,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
10453
10989
|
}))
|
|
10454
10990
|
]);
|
|
10455
10991
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
10456
|
-
const approvalDir =
|
|
10992
|
+
const approvalDir = path20.join(paths.approvalsDir, approvalId);
|
|
10457
10993
|
await ensureDir(approvalDir);
|
|
10458
|
-
await ensureDir(
|
|
10459
|
-
await ensureDir(
|
|
10994
|
+
await ensureDir(path20.join(approvalDir, "wiki"));
|
|
10995
|
+
await ensureDir(path20.join(approvalDir, "state"));
|
|
10460
10996
|
for (const file of changedFiles) {
|
|
10461
|
-
const targetPath =
|
|
10462
|
-
await ensureDir(
|
|
10997
|
+
const targetPath = path20.join(approvalDir, "wiki", file.relativePath);
|
|
10998
|
+
await ensureDir(path20.dirname(targetPath));
|
|
10463
10999
|
if ("binary" in file && file.binary) {
|
|
10464
|
-
await
|
|
11000
|
+
await fs16.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
10465
11001
|
} else {
|
|
10466
|
-
await
|
|
11002
|
+
await fs16.writeFile(targetPath, file.content, "utf8");
|
|
10467
11003
|
}
|
|
10468
11004
|
}
|
|
10469
11005
|
const nextPages = sortGraphPages([
|
|
@@ -10478,7 +11014,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
10478
11014
|
sources: previousGraph?.sources ?? [],
|
|
10479
11015
|
pages: nextPages
|
|
10480
11016
|
};
|
|
10481
|
-
await
|
|
11017
|
+
await fs16.writeFile(path20.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
10482
11018
|
await writeApprovalManifest(paths, {
|
|
10483
11019
|
approvalId,
|
|
10484
11020
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10507,9 +11043,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
10507
11043
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
10508
11044
|
const excerpts = await Promise.all(
|
|
10509
11045
|
searchResults.map(async (result) => {
|
|
10510
|
-
const absolutePath =
|
|
11046
|
+
const absolutePath = path20.join(paths.wikiDir, result.path);
|
|
10511
11047
|
try {
|
|
10512
|
-
const content = await
|
|
11048
|
+
const content = await fs16.readFile(absolutePath, "utf8");
|
|
10513
11049
|
const parsed = matter9(content);
|
|
10514
11050
|
return `# ${result.title}
|
|
10515
11051
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -10691,7 +11227,7 @@ function sortGraphPages(pages) {
|
|
|
10691
11227
|
async function listApprovals(rootDir) {
|
|
10692
11228
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10693
11229
|
const manifests = await Promise.all(
|
|
10694
|
-
(await
|
|
11230
|
+
(await fs16.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
10695
11231
|
try {
|
|
10696
11232
|
return await readApprovalManifest(paths, entry.name);
|
|
10697
11233
|
} catch {
|
|
@@ -10707,8 +11243,8 @@ async function readApproval(rootDir, approvalId) {
|
|
|
10707
11243
|
const details = await Promise.all(
|
|
10708
11244
|
manifest.entries.map(async (entry) => {
|
|
10709
11245
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
10710
|
-
const currentContent = currentPath ? await
|
|
10711
|
-
const stagedContent = entry.nextPath ? await
|
|
11246
|
+
const currentContent = currentPath ? await fs16.readFile(path20.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
11247
|
+
const stagedContent = entry.nextPath ? await fs16.readFile(path20.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
10712
11248
|
return {
|
|
10713
11249
|
...entry,
|
|
10714
11250
|
currentContent,
|
|
@@ -10736,26 +11272,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
10736
11272
|
if (!entry.nextPath) {
|
|
10737
11273
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
10738
11274
|
}
|
|
10739
|
-
const stagedAbsolutePath =
|
|
10740
|
-
const stagedContent = await
|
|
10741
|
-
const targetAbsolutePath =
|
|
10742
|
-
await ensureDir(
|
|
10743
|
-
await
|
|
11275
|
+
const stagedAbsolutePath = path20.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
|
|
11276
|
+
const stagedContent = await fs16.readFile(stagedAbsolutePath, "utf8");
|
|
11277
|
+
const targetAbsolutePath = path20.join(paths.wikiDir, entry.nextPath);
|
|
11278
|
+
await ensureDir(path20.dirname(targetAbsolutePath));
|
|
11279
|
+
await fs16.writeFile(targetAbsolutePath, stagedContent, "utf8");
|
|
10744
11280
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
10745
|
-
await
|
|
11281
|
+
await fs16.rm(path20.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
10746
11282
|
}
|
|
10747
11283
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
10748
11284
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
10749
|
-
const outputAssetDir =
|
|
10750
|
-
await
|
|
11285
|
+
const outputAssetDir = path20.join(paths.wikiDir, "outputs", "assets", path20.basename(nextPage.path, ".md"));
|
|
11286
|
+
await fs16.rm(outputAssetDir, { recursive: true, force: true });
|
|
10751
11287
|
for (const asset of nextPage.outputAssets) {
|
|
10752
|
-
const stagedAssetPath =
|
|
11288
|
+
const stagedAssetPath = path20.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
10753
11289
|
if (!await fileExists(stagedAssetPath)) {
|
|
10754
11290
|
continue;
|
|
10755
11291
|
}
|
|
10756
|
-
const targetAssetPath =
|
|
10757
|
-
await ensureDir(
|
|
10758
|
-
await
|
|
11292
|
+
const targetAssetPath = path20.join(paths.wikiDir, asset.path);
|
|
11293
|
+
await ensureDir(path20.dirname(targetAssetPath));
|
|
11294
|
+
await fs16.copyFile(stagedAssetPath, targetAssetPath);
|
|
10759
11295
|
}
|
|
10760
11296
|
}
|
|
10761
11297
|
nextPages = nextPages.filter(
|
|
@@ -10766,10 +11302,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
10766
11302
|
} else {
|
|
10767
11303
|
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
11304
|
if (entry.previousPath) {
|
|
10769
|
-
await
|
|
11305
|
+
await fs16.rm(path20.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
10770
11306
|
}
|
|
10771
11307
|
if (deletedPage?.kind === "output") {
|
|
10772
|
-
await
|
|
11308
|
+
await fs16.rm(path20.join(paths.wikiDir, "outputs", "assets", path20.basename(deletedPage.path, ".md")), {
|
|
10773
11309
|
recursive: true,
|
|
10774
11310
|
force: true
|
|
10775
11311
|
});
|
|
@@ -10860,7 +11396,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
10860
11396
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10861
11397
|
const graph = await readJsonFile(paths.graphPath);
|
|
10862
11398
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
10863
|
-
const raw = await
|
|
11399
|
+
const raw = await fs16.readFile(path20.join(paths.wikiDir, candidate.path), "utf8");
|
|
10864
11400
|
const parsed = matter9(raw);
|
|
10865
11401
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10866
11402
|
const nextContent = matter9.stringify(parsed.content, {
|
|
@@ -10872,10 +11408,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
10872
11408
|
)
|
|
10873
11409
|
});
|
|
10874
11410
|
const nextPath = candidateActivePath(candidate);
|
|
10875
|
-
const nextAbsolutePath =
|
|
10876
|
-
await ensureDir(
|
|
10877
|
-
await
|
|
10878
|
-
await
|
|
11411
|
+
const nextAbsolutePath = path20.join(paths.wikiDir, nextPath);
|
|
11412
|
+
await ensureDir(path20.dirname(nextAbsolutePath));
|
|
11413
|
+
await fs16.writeFile(nextAbsolutePath, nextContent, "utf8");
|
|
11414
|
+
await fs16.rm(path20.join(paths.wikiDir, candidate.path), { force: true });
|
|
10879
11415
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
10880
11416
|
const nextPages = sortGraphPages(
|
|
10881
11417
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -10920,7 +11456,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
10920
11456
|
const { paths } = await loadVaultConfig(rootDir);
|
|
10921
11457
|
const graph = await readJsonFile(paths.graphPath);
|
|
10922
11458
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
10923
|
-
await
|
|
11459
|
+
await fs16.rm(path20.join(paths.wikiDir, candidate.path), { force: true });
|
|
10924
11460
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
10925
11461
|
const nextGraph = {
|
|
10926
11462
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -10959,18 +11495,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
10959
11495
|
}
|
|
10960
11496
|
async function ensureObsidianWorkspace(rootDir) {
|
|
10961
11497
|
const { config } = await loadVaultConfig(rootDir);
|
|
10962
|
-
const obsidianDir =
|
|
11498
|
+
const obsidianDir = path20.join(rootDir, ".obsidian");
|
|
10963
11499
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
10964
11500
|
await ensureDir(obsidianDir);
|
|
10965
11501
|
await Promise.all([
|
|
10966
|
-
writeJsonFile(
|
|
11502
|
+
writeJsonFile(path20.join(obsidianDir, "app.json"), {
|
|
10967
11503
|
alwaysUpdateLinks: true,
|
|
10968
11504
|
newFileLocation: "folder",
|
|
10969
11505
|
newFileFolderPath: "wiki/insights",
|
|
10970
11506
|
useMarkdownLinks: false,
|
|
10971
11507
|
attachmentFolderPath: "raw/assets"
|
|
10972
11508
|
}),
|
|
10973
|
-
writeJsonFile(
|
|
11509
|
+
writeJsonFile(path20.join(obsidianDir, "core-plugins.json"), [
|
|
10974
11510
|
"file-explorer",
|
|
10975
11511
|
"global-search",
|
|
10976
11512
|
"switcher",
|
|
@@ -10980,7 +11516,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
10980
11516
|
"tag-pane",
|
|
10981
11517
|
"page-preview"
|
|
10982
11518
|
]),
|
|
10983
|
-
writeJsonFile(
|
|
11519
|
+
writeJsonFile(path20.join(obsidianDir, "graph.json"), {
|
|
10984
11520
|
"collapse-filter": false,
|
|
10985
11521
|
search: "",
|
|
10986
11522
|
showTags: true,
|
|
@@ -10992,7 +11528,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
10992
11528
|
})),
|
|
10993
11529
|
localJumps: false
|
|
10994
11530
|
}),
|
|
10995
|
-
writeJsonFile(
|
|
11531
|
+
writeJsonFile(path20.join(obsidianDir, "workspace.json"), {
|
|
10996
11532
|
active: "root",
|
|
10997
11533
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
10998
11534
|
left: {
|
|
@@ -11007,7 +11543,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
11007
11543
|
async function initVault(rootDir, options = {}) {
|
|
11008
11544
|
const { paths } = await initWorkspace(rootDir);
|
|
11009
11545
|
await installConfiguredAgents(rootDir);
|
|
11010
|
-
const insightsIndexPath =
|
|
11546
|
+
const insightsIndexPath = path20.join(paths.wikiDir, "insights", "index.md");
|
|
11011
11547
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11012
11548
|
await writeFileIfChanged(
|
|
11013
11549
|
insightsIndexPath,
|
|
@@ -11043,7 +11579,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
11043
11579
|
)
|
|
11044
11580
|
);
|
|
11045
11581
|
await writeFileIfChanged(
|
|
11046
|
-
|
|
11582
|
+
path20.join(paths.wikiDir, "projects", "index.md"),
|
|
11047
11583
|
matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
11048
11584
|
page_id: "projects:index",
|
|
11049
11585
|
kind: "index",
|
|
@@ -11065,7 +11601,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
11065
11601
|
})
|
|
11066
11602
|
);
|
|
11067
11603
|
await writeFileIfChanged(
|
|
11068
|
-
|
|
11604
|
+
path20.join(paths.wikiDir, "candidates", "index.md"),
|
|
11069
11605
|
matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
11070
11606
|
page_id: "candidates:index",
|
|
11071
11607
|
kind: "index",
|
|
@@ -11201,7 +11737,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
11201
11737
|
),
|
|
11202
11738
|
Promise.all(
|
|
11203
11739
|
clean.map(async (manifest) => {
|
|
11204
|
-
const cached = await readJsonFile(
|
|
11740
|
+
const cached = await readJsonFile(path20.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
11205
11741
|
if (cached) {
|
|
11206
11742
|
return cached;
|
|
11207
11743
|
}
|
|
@@ -11225,22 +11761,22 @@ async function compileVault(rootDir, options = {}) {
|
|
|
11225
11761
|
}
|
|
11226
11762
|
const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
|
|
11227
11763
|
if (analysisSignature(enriched) !== analysisSignature(analysis)) {
|
|
11228
|
-
await writeJsonFile(
|
|
11764
|
+
await writeJsonFile(path20.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
|
|
11229
11765
|
}
|
|
11230
11766
|
return enriched;
|
|
11231
11767
|
})
|
|
11232
11768
|
);
|
|
11233
11769
|
await Promise.all([
|
|
11234
|
-
ensureDir(
|
|
11235
|
-
ensureDir(
|
|
11236
|
-
ensureDir(
|
|
11237
|
-
ensureDir(
|
|
11238
|
-
ensureDir(
|
|
11239
|
-
ensureDir(
|
|
11240
|
-
ensureDir(
|
|
11241
|
-
ensureDir(
|
|
11242
|
-
ensureDir(
|
|
11243
|
-
ensureDir(
|
|
11770
|
+
ensureDir(path20.join(paths.wikiDir, "sources")),
|
|
11771
|
+
ensureDir(path20.join(paths.wikiDir, "code")),
|
|
11772
|
+
ensureDir(path20.join(paths.wikiDir, "concepts")),
|
|
11773
|
+
ensureDir(path20.join(paths.wikiDir, "entities")),
|
|
11774
|
+
ensureDir(path20.join(paths.wikiDir, "outputs")),
|
|
11775
|
+
ensureDir(path20.join(paths.wikiDir, "projects")),
|
|
11776
|
+
ensureDir(path20.join(paths.wikiDir, "insights")),
|
|
11777
|
+
ensureDir(path20.join(paths.wikiDir, "candidates")),
|
|
11778
|
+
ensureDir(path20.join(paths.wikiDir, "candidates", "concepts")),
|
|
11779
|
+
ensureDir(path20.join(paths.wikiDir, "candidates", "entities"))
|
|
11244
11780
|
]);
|
|
11245
11781
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
11246
11782
|
schemas,
|
|
@@ -11387,7 +11923,7 @@ async function queryVault(rootDir, options) {
|
|
|
11387
11923
|
assetFiles: staged.assetFiles
|
|
11388
11924
|
}
|
|
11389
11925
|
]);
|
|
11390
|
-
stagedPath =
|
|
11926
|
+
stagedPath = path20.join(approval.approvalDir, "wiki", staged.page.path);
|
|
11391
11927
|
savedPageId = staged.page.id;
|
|
11392
11928
|
approvalId = approval.approvalId;
|
|
11393
11929
|
approvalDir = approval.approvalDir;
|
|
@@ -11643,9 +12179,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
11643
12179
|
approvalId = approval.approvalId;
|
|
11644
12180
|
approvalDir = approval.approvalDir;
|
|
11645
12181
|
stepResults.forEach((result, index) => {
|
|
11646
|
-
result.stagedPath =
|
|
12182
|
+
result.stagedPath = path20.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
11647
12183
|
});
|
|
11648
|
-
stagedHubPath =
|
|
12184
|
+
stagedHubPath = path20.join(approval.approvalDir, "wiki", hubPage.path);
|
|
11649
12185
|
} else {
|
|
11650
12186
|
await refreshVaultAfterOutputSave(rootDir);
|
|
11651
12187
|
}
|
|
@@ -11706,11 +12242,18 @@ async function ensureCompiledGraph(rootDir) {
|
|
|
11706
12242
|
}
|
|
11707
12243
|
return graph;
|
|
11708
12244
|
}
|
|
11709
|
-
async function
|
|
12245
|
+
async function runResolvedGraphQuery(rootDir, graph, question, options = {}) {
|
|
11710
12246
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11711
|
-
const graph = await ensureCompiledGraph(rootDir);
|
|
11712
12247
|
const searchResults = searchPages(paths.searchDbPath, question, { limit: Math.max(5, options.budget ?? 10) });
|
|
11713
|
-
|
|
12248
|
+
const semanticMatches = await semanticGraphMatches(rootDir, graph, question, Math.max(8, options.budget ?? 12)).catch(() => []);
|
|
12249
|
+
return queryGraph(graph, question, searchResults, {
|
|
12250
|
+
...options,
|
|
12251
|
+
semanticMatches
|
|
12252
|
+
});
|
|
12253
|
+
}
|
|
12254
|
+
async function queryGraphVault(rootDir, question, options = {}) {
|
|
12255
|
+
const graph = await ensureCompiledGraph(rootDir);
|
|
12256
|
+
return runResolvedGraphQuery(rootDir, graph, question, options);
|
|
11714
12257
|
}
|
|
11715
12258
|
async function benchmarkVault(rootDir, options = {}) {
|
|
11716
12259
|
const { config, paths } = await loadVaultConfig(rootDir);
|
|
@@ -11725,30 +12268,31 @@ async function benchmarkVault(rootDir, options = {}) {
|
|
|
11725
12268
|
}
|
|
11726
12269
|
}
|
|
11727
12270
|
for (const page of graph.pages) {
|
|
11728
|
-
const absolutePath =
|
|
12271
|
+
const absolutePath = path20.join(paths.wikiDir, page.path);
|
|
11729
12272
|
if (!await fileExists(absolutePath)) {
|
|
11730
12273
|
continue;
|
|
11731
12274
|
}
|
|
11732
|
-
const parsed = matter9(await
|
|
12275
|
+
const parsed = matter9(await fs16.readFile(absolutePath, "utf8"));
|
|
11733
12276
|
pageContentsById.set(page.id, parsed.content);
|
|
11734
12277
|
}
|
|
11735
12278
|
const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
11736
12279
|
const maxQuestions = Math.max(1, options.maxQuestions ?? config.benchmark?.maxQuestions ?? 3);
|
|
11737
12280
|
const questions = (options.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
11738
12281
|
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
|
-
|
|
12282
|
+
const perQuestion = await Promise.all(
|
|
12283
|
+
sampleQuestions.map(async (question) => {
|
|
12284
|
+
const result = await runResolvedGraphQuery(rootDir, graph, question, { budget: 12 });
|
|
12285
|
+
const metrics = benchmarkQueryTokens(graph, result, pageContentsById);
|
|
12286
|
+
return {
|
|
12287
|
+
question,
|
|
12288
|
+
queryTokens: metrics.queryTokens,
|
|
12289
|
+
reduction: metrics.reduction,
|
|
12290
|
+
visitedNodeIds: result.visitedNodeIds,
|
|
12291
|
+
visitedEdgeIds: result.visitedEdgeIds,
|
|
12292
|
+
pageIds: result.pageIds
|
|
12293
|
+
};
|
|
12294
|
+
})
|
|
12295
|
+
);
|
|
11752
12296
|
const artifact = buildBenchmarkArtifact({
|
|
11753
12297
|
graph,
|
|
11754
12298
|
corpusWords,
|
|
@@ -11773,7 +12317,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
|
|
|
11773
12317
|
}
|
|
11774
12318
|
async function readGraphReport(rootDir) {
|
|
11775
12319
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11776
|
-
return readJsonFile(
|
|
12320
|
+
return readJsonFile(path20.join(paths.wikiDir, "graph", "report.json"));
|
|
11777
12321
|
}
|
|
11778
12322
|
async function listGodNodes(rootDir, limit = 10) {
|
|
11779
12323
|
const graph = await ensureCompiledGraph(rootDir);
|
|
@@ -11786,15 +12330,15 @@ async function listPages(rootDir) {
|
|
|
11786
12330
|
}
|
|
11787
12331
|
async function readPage(rootDir, relativePath) {
|
|
11788
12332
|
const { paths } = await loadVaultConfig(rootDir);
|
|
11789
|
-
const absolutePath =
|
|
12333
|
+
const absolutePath = path20.resolve(paths.wikiDir, relativePath);
|
|
11790
12334
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
11791
12335
|
return null;
|
|
11792
12336
|
}
|
|
11793
|
-
const raw = await
|
|
12337
|
+
const raw = await fs16.readFile(absolutePath, "utf8");
|
|
11794
12338
|
const parsed = matter9(raw);
|
|
11795
12339
|
return {
|
|
11796
12340
|
path: relativePath,
|
|
11797
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
12341
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(relativePath, path20.extname(relativePath)),
|
|
11798
12342
|
frontmatter: parsed.data,
|
|
11799
12343
|
content: parsed.content
|
|
11800
12344
|
};
|
|
@@ -11830,7 +12374,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11830
12374
|
severity: "warning",
|
|
11831
12375
|
code: "stale_page",
|
|
11832
12376
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
11833
|
-
pagePath:
|
|
12377
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11834
12378
|
relatedPageIds: [page.id]
|
|
11835
12379
|
});
|
|
11836
12380
|
}
|
|
@@ -11841,7 +12385,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11841
12385
|
severity: "warning",
|
|
11842
12386
|
code: "stale_page",
|
|
11843
12387
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
11844
|
-
pagePath:
|
|
12388
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11845
12389
|
relatedSourceIds: [sourceId],
|
|
11846
12390
|
relatedPageIds: [page.id]
|
|
11847
12391
|
});
|
|
@@ -11852,13 +12396,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
11852
12396
|
severity: "info",
|
|
11853
12397
|
code: "orphan_page",
|
|
11854
12398
|
message: `Page ${page.title} has no backlinks.`,
|
|
11855
|
-
pagePath:
|
|
12399
|
+
pagePath: path20.join(paths.wikiDir, page.path),
|
|
11856
12400
|
relatedPageIds: [page.id]
|
|
11857
12401
|
});
|
|
11858
12402
|
}
|
|
11859
|
-
const absolutePath =
|
|
12403
|
+
const absolutePath = path20.join(paths.wikiDir, page.path);
|
|
11860
12404
|
if (await fileExists(absolutePath)) {
|
|
11861
|
-
const content = await
|
|
12405
|
+
const content = await fs16.readFile(absolutePath, "utf8");
|
|
11862
12406
|
if (content.includes("## Claims")) {
|
|
11863
12407
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
11864
12408
|
if (uncited.length) {
|
|
@@ -11938,7 +12482,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
11938
12482
|
}
|
|
11939
12483
|
|
|
11940
12484
|
// src/mcp.ts
|
|
11941
|
-
var SERVER_VERSION = "0.1.
|
|
12485
|
+
var SERVER_VERSION = "0.1.27";
|
|
11942
12486
|
async function createMcpServer(rootDir) {
|
|
11943
12487
|
const server = new McpServer({
|
|
11944
12488
|
name: "swarmvault",
|
|
@@ -12209,7 +12753,7 @@ async function createMcpServer(rootDir) {
|
|
|
12209
12753
|
},
|
|
12210
12754
|
async () => {
|
|
12211
12755
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12212
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
12756
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path21.relative(paths.sessionsDir, filePath))).sort();
|
|
12213
12757
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
12214
12758
|
}
|
|
12215
12759
|
);
|
|
@@ -12242,8 +12786,8 @@ async function createMcpServer(rootDir) {
|
|
|
12242
12786
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
12243
12787
|
}
|
|
12244
12788
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12245
|
-
const absolutePath =
|
|
12246
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
12789
|
+
const absolutePath = path21.resolve(paths.wikiDir, relativePath);
|
|
12790
|
+
return asTextResource(`swarmvault://pages/${encodedPath}`, await fs17.readFile(absolutePath, "utf8"));
|
|
12247
12791
|
}
|
|
12248
12792
|
);
|
|
12249
12793
|
server.registerResource(
|
|
@@ -12251,11 +12795,11 @@ async function createMcpServer(rootDir) {
|
|
|
12251
12795
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
12252
12796
|
list: async () => {
|
|
12253
12797
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12254
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
12798
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path21.relative(paths.sessionsDir, filePath))).sort();
|
|
12255
12799
|
return {
|
|
12256
12800
|
resources: files.map((relativePath) => ({
|
|
12257
12801
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
12258
|
-
name:
|
|
12802
|
+
name: path21.basename(relativePath, ".md"),
|
|
12259
12803
|
title: relativePath,
|
|
12260
12804
|
description: "SwarmVault session artifact",
|
|
12261
12805
|
mimeType: "text/markdown"
|
|
@@ -12272,11 +12816,11 @@ async function createMcpServer(rootDir) {
|
|
|
12272
12816
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12273
12817
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
12274
12818
|
const relativePath = decodeURIComponent(encodedPath);
|
|
12275
|
-
const absolutePath =
|
|
12819
|
+
const absolutePath = path21.resolve(paths.sessionsDir, relativePath);
|
|
12276
12820
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
12277
12821
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
12278
12822
|
}
|
|
12279
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
12823
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs17.readFile(absolutePath, "utf8"));
|
|
12280
12824
|
}
|
|
12281
12825
|
);
|
|
12282
12826
|
return server;
|
|
@@ -12324,13 +12868,13 @@ function asTextResource(uri, text) {
|
|
|
12324
12868
|
}
|
|
12325
12869
|
|
|
12326
12870
|
// src/schedule.ts
|
|
12327
|
-
import
|
|
12328
|
-
import
|
|
12871
|
+
import fs18 from "fs/promises";
|
|
12872
|
+
import path22 from "path";
|
|
12329
12873
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
12330
|
-
return
|
|
12874
|
+
return path22.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
12331
12875
|
}
|
|
12332
12876
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
12333
|
-
return
|
|
12877
|
+
return path22.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
12334
12878
|
}
|
|
12335
12879
|
function parseEveryDuration(value) {
|
|
12336
12880
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -12433,13 +12977,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
12433
12977
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12434
12978
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
12435
12979
|
await ensureDir(paths.schedulesDir);
|
|
12436
|
-
const handle = await
|
|
12980
|
+
const handle = await fs18.open(leasePath, "wx");
|
|
12437
12981
|
await handle.writeFile(`${process.pid}
|
|
12438
12982
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
12439
12983
|
`);
|
|
12440
12984
|
await handle.close();
|
|
12441
12985
|
return async () => {
|
|
12442
|
-
await
|
|
12986
|
+
await fs18.rm(leasePath, { force: true });
|
|
12443
12987
|
};
|
|
12444
12988
|
}
|
|
12445
12989
|
async function listSchedules(rootDir) {
|
|
@@ -12587,31 +13131,31 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
12587
13131
|
|
|
12588
13132
|
// src/viewer.ts
|
|
12589
13133
|
import { execFile } from "child_process";
|
|
12590
|
-
import
|
|
13134
|
+
import fs19 from "fs/promises";
|
|
12591
13135
|
import http from "http";
|
|
12592
|
-
import
|
|
13136
|
+
import path24 from "path";
|
|
12593
13137
|
import { promisify } from "util";
|
|
12594
13138
|
import matter10 from "gray-matter";
|
|
12595
13139
|
import mime2 from "mime-types";
|
|
12596
13140
|
|
|
12597
13141
|
// src/watch.ts
|
|
12598
|
-
import
|
|
13142
|
+
import path23 from "path";
|
|
12599
13143
|
import process2 from "process";
|
|
12600
13144
|
import chokidar from "chokidar";
|
|
12601
13145
|
var MAX_BACKOFF_MS = 3e4;
|
|
12602
13146
|
var BACKOFF_THRESHOLD = 3;
|
|
12603
13147
|
var CRITICAL_THRESHOLD = 10;
|
|
12604
|
-
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", "
|
|
13148
|
+
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
12605
13149
|
function withinRoot2(rootPath, targetPath) {
|
|
12606
|
-
const relative =
|
|
12607
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
13150
|
+
const relative = path23.relative(rootPath, targetPath);
|
|
13151
|
+
return relative === "" || !relative.startsWith("..") && !path23.isAbsolute(relative);
|
|
12608
13152
|
}
|
|
12609
13153
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
12610
|
-
const relativePath =
|
|
13154
|
+
const relativePath = path23.relative(baseDir, targetPath);
|
|
12611
13155
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
12612
13156
|
return false;
|
|
12613
13157
|
}
|
|
12614
|
-
return relativePath.split(
|
|
13158
|
+
return relativePath.split(path23.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
|
|
12615
13159
|
}
|
|
12616
13160
|
function workspaceIgnoreRoots(rootDir, paths) {
|
|
12617
13161
|
return [
|
|
@@ -12620,16 +13164,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
|
|
|
12620
13164
|
paths.stateDir,
|
|
12621
13165
|
paths.agentDir,
|
|
12622
13166
|
paths.inboxDir,
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
].map((candidate) =>
|
|
13167
|
+
path23.join(rootDir, ".claude"),
|
|
13168
|
+
path23.join(rootDir, ".cursor"),
|
|
13169
|
+
path23.join(rootDir, ".obsidian")
|
|
13170
|
+
].map((candidate) => path23.resolve(candidate));
|
|
12627
13171
|
}
|
|
12628
13172
|
async function resolveWatchTargets(rootDir, paths, options) {
|
|
12629
|
-
const targets = /* @__PURE__ */ new Set([
|
|
13173
|
+
const targets = /* @__PURE__ */ new Set([path23.resolve(paths.inboxDir)]);
|
|
12630
13174
|
if (options.repo) {
|
|
12631
13175
|
for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
|
|
12632
|
-
targets.add(
|
|
13176
|
+
targets.add(path23.resolve(repoRoot));
|
|
12633
13177
|
}
|
|
12634
13178
|
}
|
|
12635
13179
|
return [...targets].sort((left, right) => left.localeCompare(right));
|
|
@@ -12759,7 +13303,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12759
13303
|
const { paths } = await initWorkspace(rootDir);
|
|
12760
13304
|
const baseDebounceMs = options.debounceMs ?? 900;
|
|
12761
13305
|
const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
|
|
12762
|
-
const inboxWatchRoot =
|
|
13306
|
+
const inboxWatchRoot = path23.resolve(paths.inboxDir);
|
|
12763
13307
|
let watchTargets = await resolveWatchTargets(rootDir, paths, options);
|
|
12764
13308
|
let timer;
|
|
12765
13309
|
let running = false;
|
|
@@ -12773,7 +13317,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12773
13317
|
usePolling: true,
|
|
12774
13318
|
interval: 100,
|
|
12775
13319
|
ignored: (targetPath) => {
|
|
12776
|
-
const absolutePath =
|
|
13320
|
+
const absolutePath = path23.resolve(targetPath);
|
|
12777
13321
|
const primaryTarget = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
12778
13322
|
if (!primaryTarget) {
|
|
12779
13323
|
return false;
|
|
@@ -12945,8 +13489,8 @@ async function watchVault(rootDir, options = {}) {
|
|
|
12945
13489
|
}
|
|
12946
13490
|
};
|
|
12947
13491
|
const reasonForPath = (targetPath) => {
|
|
12948
|
-
const baseDir = watchTargets.filter((watchTarget) => withinRoot2(watchTarget,
|
|
12949
|
-
return
|
|
13492
|
+
const baseDir = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, path23.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
|
|
13493
|
+
return path23.relative(baseDir, targetPath) || ".";
|
|
12950
13494
|
};
|
|
12951
13495
|
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
13496
|
await new Promise((resolve, reject) => {
|
|
@@ -12987,15 +13531,15 @@ async function getWatchStatus(rootDir) {
|
|
|
12987
13531
|
var execFileAsync = promisify(execFile);
|
|
12988
13532
|
async function readViewerPage(rootDir, relativePath) {
|
|
12989
13533
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12990
|
-
const absolutePath =
|
|
13534
|
+
const absolutePath = path24.resolve(paths.wikiDir, relativePath);
|
|
12991
13535
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
12992
13536
|
return null;
|
|
12993
13537
|
}
|
|
12994
|
-
const raw = await
|
|
13538
|
+
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
12995
13539
|
const parsed = matter10(raw);
|
|
12996
13540
|
return {
|
|
12997
13541
|
path: relativePath,
|
|
12998
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
13542
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path24.basename(relativePath, path24.extname(relativePath)),
|
|
12999
13543
|
frontmatter: parsed.data,
|
|
13000
13544
|
content: parsed.content,
|
|
13001
13545
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -13003,12 +13547,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
13003
13547
|
}
|
|
13004
13548
|
async function readViewerAsset(rootDir, relativePath) {
|
|
13005
13549
|
const { paths } = await loadVaultConfig(rootDir);
|
|
13006
|
-
const absolutePath =
|
|
13550
|
+
const absolutePath = path24.resolve(paths.wikiDir, relativePath);
|
|
13007
13551
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
13008
13552
|
return null;
|
|
13009
13553
|
}
|
|
13010
13554
|
return {
|
|
13011
|
-
buffer: await
|
|
13555
|
+
buffer: await fs19.readFile(absolutePath),
|
|
13012
13556
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
13013
13557
|
};
|
|
13014
13558
|
}
|
|
@@ -13031,12 +13575,12 @@ async function readJsonBody(request) {
|
|
|
13031
13575
|
return JSON.parse(raw);
|
|
13032
13576
|
}
|
|
13033
13577
|
async function ensureViewerDist(viewerDistDir) {
|
|
13034
|
-
const indexPath =
|
|
13578
|
+
const indexPath = path24.join(viewerDistDir, "index.html");
|
|
13035
13579
|
if (await fileExists(indexPath)) {
|
|
13036
13580
|
return;
|
|
13037
13581
|
}
|
|
13038
|
-
const viewerProjectDir =
|
|
13039
|
-
if (await fileExists(
|
|
13582
|
+
const viewerProjectDir = path24.dirname(viewerDistDir);
|
|
13583
|
+
if (await fileExists(path24.join(viewerProjectDir, "package.json"))) {
|
|
13040
13584
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
13041
13585
|
}
|
|
13042
13586
|
}
|
|
@@ -13053,7 +13597,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
13053
13597
|
return;
|
|
13054
13598
|
}
|
|
13055
13599
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13056
|
-
response.end(await
|
|
13600
|
+
response.end(await fs19.readFile(paths.graphPath, "utf8"));
|
|
13057
13601
|
return;
|
|
13058
13602
|
}
|
|
13059
13603
|
if (url.pathname === "/api/graph/query") {
|
|
@@ -13096,26 +13640,28 @@ async function startGraphServer(rootDir, port) {
|
|
|
13096
13640
|
const status = url.searchParams.get("status") ?? "all";
|
|
13097
13641
|
const project = url.searchParams.get("project") ?? "all";
|
|
13098
13642
|
const sourceType = url.searchParams.get("sourceType") ?? "all";
|
|
13643
|
+
const sourceClass = url.searchParams.get("sourceClass") ?? "all";
|
|
13099
13644
|
const results = searchPages(paths.searchDbPath, query, {
|
|
13100
13645
|
limit: Number.isFinite(limit) ? limit : 10,
|
|
13101
13646
|
kind,
|
|
13102
13647
|
status,
|
|
13103
13648
|
project,
|
|
13104
|
-
sourceType
|
|
13649
|
+
sourceType,
|
|
13650
|
+
sourceClass
|
|
13105
13651
|
});
|
|
13106
13652
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13107
13653
|
response.end(JSON.stringify(results));
|
|
13108
13654
|
return;
|
|
13109
13655
|
}
|
|
13110
13656
|
if (url.pathname === "/api/graph-report") {
|
|
13111
|
-
const reportPath =
|
|
13657
|
+
const reportPath = path24.join(paths.wikiDir, "graph", "report.json");
|
|
13112
13658
|
if (!await fileExists(reportPath)) {
|
|
13113
13659
|
response.writeHead(404, { "content-type": "application/json" });
|
|
13114
13660
|
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
13115
13661
|
return;
|
|
13116
13662
|
}
|
|
13117
13663
|
response.writeHead(200, { "content-type": "application/json" });
|
|
13118
|
-
response.end(await
|
|
13664
|
+
response.end(await fs19.readFile(reportPath, "utf8"));
|
|
13119
13665
|
return;
|
|
13120
13666
|
}
|
|
13121
13667
|
if (url.pathname === "/api/watch-status") {
|
|
@@ -13198,8 +13744,8 @@ async function startGraphServer(rootDir, port) {
|
|
|
13198
13744
|
return;
|
|
13199
13745
|
}
|
|
13200
13746
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
13201
|
-
const target =
|
|
13202
|
-
const fallback =
|
|
13747
|
+
const target = path24.join(paths.viewerDistDir, relativePath);
|
|
13748
|
+
const fallback = path24.join(paths.viewerDistDir, "index.html");
|
|
13203
13749
|
const filePath = await fileExists(target) ? target : fallback;
|
|
13204
13750
|
if (!await fileExists(filePath)) {
|
|
13205
13751
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -13207,7 +13753,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
13207
13753
|
return;
|
|
13208
13754
|
}
|
|
13209
13755
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
13210
|
-
response.end(await
|
|
13756
|
+
response.end(await fs19.readFile(filePath));
|
|
13211
13757
|
});
|
|
13212
13758
|
await new Promise((resolve) => {
|
|
13213
13759
|
server.listen(effectivePort, resolve);
|
|
@@ -13234,7 +13780,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13234
13780
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
13235
13781
|
}
|
|
13236
13782
|
await ensureViewerDist(paths.viewerDistDir);
|
|
13237
|
-
const indexPath =
|
|
13783
|
+
const indexPath = path24.join(paths.viewerDistDir, "index.html");
|
|
13238
13784
|
if (!await fileExists(indexPath)) {
|
|
13239
13785
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
13240
13786
|
}
|
|
@@ -13248,6 +13794,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13248
13794
|
kind: page.kind,
|
|
13249
13795
|
status: page.status,
|
|
13250
13796
|
sourceType: page.sourceType,
|
|
13797
|
+
sourceClass: page.sourceClass,
|
|
13251
13798
|
projectIds: page.projectIds,
|
|
13252
13799
|
content: loaded.content,
|
|
13253
13800
|
assets: await Promise.all(
|
|
@@ -13259,17 +13806,17 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13259
13806
|
} : null;
|
|
13260
13807
|
})
|
|
13261
13808
|
);
|
|
13262
|
-
const rawHtml = await
|
|
13809
|
+
const rawHtml = await fs19.readFile(indexPath, "utf8");
|
|
13263
13810
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
13264
13811
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
13265
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
13266
|
-
const stylePath = styleMatch?.[1] ?
|
|
13812
|
+
const scriptPath = scriptMatch?.[1] ? path24.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
|
|
13813
|
+
const stylePath = styleMatch?.[1] ? path24.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
|
|
13267
13814
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
13268
13815
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
13269
13816
|
}
|
|
13270
|
-
const script = await
|
|
13271
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
13272
|
-
const report = await readJsonFile(
|
|
13817
|
+
const script = await fs19.readFile(scriptPath, "utf8");
|
|
13818
|
+
const style = stylePath && await fileExists(stylePath) ? await fs19.readFile(stylePath, "utf8") : "";
|
|
13819
|
+
const report = await readJsonFile(path24.join(paths.wikiDir, "graph", "report.json"));
|
|
13273
13820
|
const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean), report }, null, 2).replace(/</g, "\\u003c");
|
|
13274
13821
|
const html = [
|
|
13275
13822
|
"<!doctype html>",
|
|
@@ -13288,9 +13835,9 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
13288
13835
|
"</html>",
|
|
13289
13836
|
""
|
|
13290
13837
|
].filter(Boolean).join("\n");
|
|
13291
|
-
await
|
|
13292
|
-
await
|
|
13293
|
-
return
|
|
13838
|
+
await fs19.mkdir(path24.dirname(outputPath), { recursive: true });
|
|
13839
|
+
await fs19.writeFile(outputPath, html, "utf8");
|
|
13840
|
+
return path24.resolve(outputPath);
|
|
13294
13841
|
}
|
|
13295
13842
|
export {
|
|
13296
13843
|
acceptApproval,
|