@swarmvaultai/engine 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-B3FC4J3P.js +1214 -0
- package/dist/chunk-FD3LJQ4T.js +1216 -0
- package/dist/index.d.ts +84 -6
- package/dist/index.js +1378 -317
- package/dist/registry-KVJAO5DF.js +12 -0
- package/dist/registry-XOPLQNZY.js +12 -0
- package/package.json +1 -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-FD3LJQ4T.js";
|
|
25
25
|
|
|
26
26
|
// src/agents.ts
|
|
27
27
|
import crypto from "crypto";
|
|
@@ -8502,8 +8502,8 @@ async function readExtractionArtifact(rootDir, manifest) {
|
|
|
8502
8502
|
}
|
|
8503
8503
|
|
|
8504
8504
|
// src/mcp.ts
|
|
8505
|
-
import
|
|
8506
|
-
import
|
|
8505
|
+
import fs20 from "fs/promises";
|
|
8506
|
+
import path24 from "path";
|
|
8507
8507
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8508
8508
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8509
8509
|
import { z as z8 } from "zod";
|
|
@@ -8616,8 +8616,8 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
8616
8616
|
}
|
|
8617
8617
|
|
|
8618
8618
|
// src/vault.ts
|
|
8619
|
-
import
|
|
8620
|
-
import
|
|
8619
|
+
import fs19 from "fs/promises";
|
|
8620
|
+
import path23 from "path";
|
|
8621
8621
|
import matter9 from "gray-matter";
|
|
8622
8622
|
import { z as z7 } from "zod";
|
|
8623
8623
|
|
|
@@ -11137,15 +11137,15 @@ function sourceTypeForNode(node, pagesById) {
|
|
|
11137
11137
|
return pagesById.get(node.pageId)?.sourceType;
|
|
11138
11138
|
}
|
|
11139
11139
|
function supportingPathDetails(graph, edge) {
|
|
11140
|
-
const
|
|
11140
|
+
const path29 = shortestGraphPath(graph, edge.source, edge.target);
|
|
11141
11141
|
const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
|
|
11142
|
-
const pathEdges =
|
|
11142
|
+
const pathEdges = path29.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
|
|
11143
11143
|
return {
|
|
11144
|
-
pathNodeIds:
|
|
11145
|
-
pathEdgeIds:
|
|
11144
|
+
pathNodeIds: path29.nodeIds,
|
|
11145
|
+
pathEdgeIds: path29.edgeIds,
|
|
11146
11146
|
pathRelations: pathEdges.map((item) => item.relation),
|
|
11147
11147
|
pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
|
|
11148
|
-
pathSummary:
|
|
11148
|
+
pathSummary: path29.summary
|
|
11149
11149
|
};
|
|
11150
11150
|
}
|
|
11151
11151
|
function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
|
|
@@ -11214,7 +11214,7 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
11214
11214
|
}).map((edge) => {
|
|
11215
11215
|
const source = nodesById.get(edge.source);
|
|
11216
11216
|
const target = nodesById.get(edge.target);
|
|
11217
|
-
const
|
|
11217
|
+
const path29 = supportingPathDetails(graph, edge);
|
|
11218
11218
|
const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
|
|
11219
11219
|
return {
|
|
11220
11220
|
id: edge.id,
|
|
@@ -11225,11 +11225,11 @@ function topSurprisingConnections(graph, pagesById) {
|
|
|
11225
11225
|
relation: edge.relation,
|
|
11226
11226
|
evidenceClass: edge.evidenceClass,
|
|
11227
11227
|
confidence: edge.confidence,
|
|
11228
|
-
pathNodeIds:
|
|
11229
|
-
pathEdgeIds:
|
|
11230
|
-
pathRelations:
|
|
11231
|
-
pathEvidenceClasses:
|
|
11232
|
-
pathSummary:
|
|
11228
|
+
pathNodeIds: path29.pathNodeIds,
|
|
11229
|
+
pathEdgeIds: path29.pathEdgeIds,
|
|
11230
|
+
pathRelations: path29.pathRelations,
|
|
11231
|
+
pathEvidenceClasses: path29.pathEvidenceClasses,
|
|
11232
|
+
pathSummary: path29.pathSummary,
|
|
11233
11233
|
why: scored.why,
|
|
11234
11234
|
explanation: scored.explanation,
|
|
11235
11235
|
surpriseScore: scored.score
|
|
@@ -12691,6 +12691,58 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
12691
12691
|
}));
|
|
12692
12692
|
}
|
|
12693
12693
|
|
|
12694
|
+
// src/source-sessions.ts
|
|
12695
|
+
import fs18 from "fs/promises";
|
|
12696
|
+
import path22 from "path";
|
|
12697
|
+
function sessionStatePathFor(paths, sessionId) {
|
|
12698
|
+
return path22.join(paths.sourceSessionsDir, `${sessionId}.json`);
|
|
12699
|
+
}
|
|
12700
|
+
async function listGuidedSourceSessions(rootDir) {
|
|
12701
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
12702
|
+
const entries = await fs18.readdir(paths.sourceSessionsDir, { withFileTypes: true }).catch(() => []);
|
|
12703
|
+
const sessions = await Promise.all(
|
|
12704
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => await readJsonFile(path22.join(paths.sourceSessionsDir, entry.name)))
|
|
12705
|
+
);
|
|
12706
|
+
return sessions.filter((session) => Boolean(session)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
12707
|
+
}
|
|
12708
|
+
async function readGuidedSourceSession(rootDir, sessionId) {
|
|
12709
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
12710
|
+
return await readJsonFile(sessionStatePathFor(paths, sessionId));
|
|
12711
|
+
}
|
|
12712
|
+
async function findLatestGuidedSourceSessionByScope(rootDir, scopeId) {
|
|
12713
|
+
const sessions = await listGuidedSourceSessions(rootDir);
|
|
12714
|
+
return sessions.find((session) => session.scopeId === scopeId) ?? null;
|
|
12715
|
+
}
|
|
12716
|
+
async function writeGuidedSourceSession(rootDir, session) {
|
|
12717
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
12718
|
+
await ensureDir(paths.sourceSessionsDir);
|
|
12719
|
+
const next = {
|
|
12720
|
+
...session,
|
|
12721
|
+
updatedAt: session.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
12722
|
+
};
|
|
12723
|
+
const filePath = sessionStatePathFor(paths, session.sessionId);
|
|
12724
|
+
await fs18.writeFile(filePath, `${JSON.stringify(next, null, 2)}
|
|
12725
|
+
`, "utf8");
|
|
12726
|
+
return filePath;
|
|
12727
|
+
}
|
|
12728
|
+
async function updateGuidedSourceSessionStatus(rootDir, sessionId, status) {
|
|
12729
|
+
const existing = await readGuidedSourceSession(rootDir, sessionId);
|
|
12730
|
+
if (!existing) {
|
|
12731
|
+
return null;
|
|
12732
|
+
}
|
|
12733
|
+
const next = {
|
|
12734
|
+
...existing,
|
|
12735
|
+
status,
|
|
12736
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12737
|
+
};
|
|
12738
|
+
await writeGuidedSourceSession(rootDir, next);
|
|
12739
|
+
return next;
|
|
12740
|
+
}
|
|
12741
|
+
async function guidedSourceSessionStatePath(rootDir, sessionId) {
|
|
12742
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
12743
|
+
return sessionStatePathFor(paths, sessionId);
|
|
12744
|
+
}
|
|
12745
|
+
|
|
12694
12746
|
// src/vault.ts
|
|
12695
12747
|
var COMPILE_PROGRESS_THRESHOLD = 120;
|
|
12696
12748
|
var COMPILE_PROGRESS_UPDATE_INTERVAL = 50;
|
|
@@ -12745,7 +12797,7 @@ function outputFormatInstruction(format) {
|
|
|
12745
12797
|
}
|
|
12746
12798
|
}
|
|
12747
12799
|
function outputAssetPath(slug, fileName) {
|
|
12748
|
-
return toPosix(
|
|
12800
|
+
return toPosix(path23.join("outputs", "assets", slug, fileName));
|
|
12749
12801
|
}
|
|
12750
12802
|
function outputAssetId(slug, role) {
|
|
12751
12803
|
return `output:${slug}:asset:${role}`;
|
|
@@ -12885,7 +12937,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
12885
12937
|
if (!providerConfig) {
|
|
12886
12938
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
12887
12939
|
}
|
|
12888
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
12940
|
+
const { createProvider: createProvider2 } = await import("./registry-XOPLQNZY.js");
|
|
12889
12941
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
12890
12942
|
}
|
|
12891
12943
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -13083,7 +13135,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
13083
13135
|
};
|
|
13084
13136
|
}
|
|
13085
13137
|
function normalizeProjectRoot(root) {
|
|
13086
|
-
const normalized = toPosix(
|
|
13138
|
+
const normalized = toPosix(path23.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
13087
13139
|
return normalized;
|
|
13088
13140
|
}
|
|
13089
13141
|
function projectEntries(config) {
|
|
@@ -13109,10 +13161,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
13109
13161
|
if (!rawPath) {
|
|
13110
13162
|
return toPosix(manifest.storedPath);
|
|
13111
13163
|
}
|
|
13112
|
-
if (!
|
|
13164
|
+
if (!path23.isAbsolute(rawPath)) {
|
|
13113
13165
|
return normalizeProjectRoot(rawPath);
|
|
13114
13166
|
}
|
|
13115
|
-
const relative = toPosix(
|
|
13167
|
+
const relative = toPosix(path23.relative(rootDir, rawPath));
|
|
13116
13168
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
13117
13169
|
}
|
|
13118
13170
|
function prefixMatches(value, prefix) {
|
|
@@ -13263,6 +13315,9 @@ function approvalSummary(manifest) {
|
|
|
13263
13315
|
return {
|
|
13264
13316
|
approvalId: manifest.approvalId,
|
|
13265
13317
|
createdAt: manifest.createdAt,
|
|
13318
|
+
bundleType: manifest.bundleType,
|
|
13319
|
+
title: manifest.title,
|
|
13320
|
+
sourceSessionId: manifest.sourceSessionId,
|
|
13266
13321
|
entryCount: manifest.entries.length,
|
|
13267
13322
|
pendingCount: manifest.entries.filter((entry) => entry.status === "pending").length,
|
|
13268
13323
|
acceptedCount: manifest.entries.filter((entry) => entry.status === "accepted").length,
|
|
@@ -13286,7 +13341,7 @@ function pageHashes(pages) {
|
|
|
13286
13341
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
13287
13342
|
}
|
|
13288
13343
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
13289
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
13344
|
+
const existingContent = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
|
|
13290
13345
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
13291
13346
|
status: defaults.status ?? "active",
|
|
13292
13347
|
managedBy: defaults.managedBy
|
|
@@ -13324,7 +13379,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
13324
13379
|
return built;
|
|
13325
13380
|
}
|
|
13326
13381
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
13327
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
13382
|
+
const existingContent = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
|
|
13328
13383
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
13329
13384
|
status: defaults.status ?? "active",
|
|
13330
13385
|
managedBy: defaults.managedBy
|
|
@@ -13366,7 +13421,7 @@ function manifestDetailValue(manifest, key) {
|
|
|
13366
13421
|
}
|
|
13367
13422
|
async function loadAnalysesBySourceIds(paths, sourceIds) {
|
|
13368
13423
|
const analyses = await Promise.all(
|
|
13369
|
-
sourceIds.map(async (sourceId) => await readJsonFile(
|
|
13424
|
+
sourceIds.map(async (sourceId) => await readJsonFile(path23.join(paths.analysesDir, `${sourceId}.json`)))
|
|
13370
13425
|
);
|
|
13371
13426
|
return analyses.filter((analysis) => Boolean(analysis?.sourceId));
|
|
13372
13427
|
}
|
|
@@ -13374,6 +13429,10 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13374
13429
|
const sourcePages = graph.pages.filter((page) => page.kind === "source");
|
|
13375
13430
|
const reviewPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-reviews/"));
|
|
13376
13431
|
const briefPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-briefs/"));
|
|
13432
|
+
const guidePages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-guides/"));
|
|
13433
|
+
const sessionPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-sessions/"));
|
|
13434
|
+
const conceptPages = graph.pages.filter((page) => page.kind === "concept" && page.status !== "candidate").slice(0, 16);
|
|
13435
|
+
const entityPages = graph.pages.filter((page) => page.kind === "entity" && page.status !== "candidate").slice(0, 16);
|
|
13377
13436
|
const manifests = graph.sources;
|
|
13378
13437
|
const manifestBySourceId = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
13379
13438
|
const timelineManifests = manifests.filter((manifest) => manifestDetailValue(manifest, "occurred_at")).sort((left, right) => (manifestDetailValue(right, "occurred_at") ?? "").localeCompare(manifestDetailValue(left, "occurred_at") ?? "")).slice(0, 25);
|
|
@@ -13382,6 +13441,10 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13382
13441
|
const openQuestions = uniqueStrings3(
|
|
13383
13442
|
analyses.flatMap((analysis) => analysis.questions.map((question) => `${analysis.title}: ${question}`))
|
|
13384
13443
|
).slice(0, 20);
|
|
13444
|
+
const sourceSessions = await listGuidedSourceSessions(paths.rootDir);
|
|
13445
|
+
const stagedGuideBundles = (await Promise.all(
|
|
13446
|
+
(await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await readJsonFile(approvalManifestPath(paths, entry.name)))
|
|
13447
|
+
)).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "guided_source" || manifest.bundleType === "guided_session").sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, 12);
|
|
13385
13448
|
const dashboards = [
|
|
13386
13449
|
{
|
|
13387
13450
|
relativePath: "dashboards/index.md",
|
|
@@ -13391,7 +13454,11 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13391
13454
|
"# Dashboards",
|
|
13392
13455
|
"",
|
|
13393
13456
|
"- [[dashboards/recent-sources|Recent Sources]]",
|
|
13457
|
+
"- [[dashboards/reading-log|Reading Log]]",
|
|
13394
13458
|
"- [[dashboards/timeline|Timeline]]",
|
|
13459
|
+
"- [[dashboards/source-sessions|Source Sessions]]",
|
|
13460
|
+
"- [[dashboards/source-guides|Source Guides]]",
|
|
13461
|
+
"- [[dashboards/research-map|Research Map]]",
|
|
13395
13462
|
"- [[dashboards/contradictions|Contradictions]]",
|
|
13396
13463
|
"- [[dashboards/open-questions|Open Questions]]",
|
|
13397
13464
|
"",
|
|
@@ -13464,6 +13531,57 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13464
13531
|
}
|
|
13465
13532
|
)
|
|
13466
13533
|
},
|
|
13534
|
+
{
|
|
13535
|
+
relativePath: "dashboards/reading-log.md",
|
|
13536
|
+
title: "Reading Log",
|
|
13537
|
+
content: (metadata) => matter9.stringify(
|
|
13538
|
+
[
|
|
13539
|
+
"# Reading Log",
|
|
13540
|
+
"",
|
|
13541
|
+
...timelineManifests.length ? timelineManifests.map((manifest) => {
|
|
13542
|
+
const occurredAt = manifestDetailValue(manifest, "occurred_at") ?? manifest.updatedAt;
|
|
13543
|
+
const participants = manifestDetailValue(manifest, "participants");
|
|
13544
|
+
return `- ${occurredAt}: ${manifest.title}${participants ? ` (${participants})` : ""}`;
|
|
13545
|
+
}) : recentSourcePages.map((page) => `- ${page.updatedAt}: [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
|
|
13546
|
+
...sourceSessions.length ? [
|
|
13547
|
+
"",
|
|
13548
|
+
"## Active Guided Sessions",
|
|
13549
|
+
"",
|
|
13550
|
+
...sourceSessions.slice(0, 8).map(
|
|
13551
|
+
(session) => `- ${session.updatedAt}: \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
|
|
13552
|
+
)
|
|
13553
|
+
] : [],
|
|
13554
|
+
"",
|
|
13555
|
+
"```dataview",
|
|
13556
|
+
"TABLE occurred_at, source_type, participants, container_title",
|
|
13557
|
+
'FROM "sources"',
|
|
13558
|
+
"SORT occurred_at desc",
|
|
13559
|
+
"LIMIT 25",
|
|
13560
|
+
"```",
|
|
13561
|
+
""
|
|
13562
|
+
].join("\n"),
|
|
13563
|
+
{
|
|
13564
|
+
page_id: "dashboards:reading-log",
|
|
13565
|
+
kind: "index",
|
|
13566
|
+
title: "Reading Log",
|
|
13567
|
+
tags: ["index", "dashboard", "reading-log"],
|
|
13568
|
+
source_ids: timelineManifests.map((manifest) => manifest.sourceId),
|
|
13569
|
+
project_ids: [],
|
|
13570
|
+
node_ids: [],
|
|
13571
|
+
freshness: "fresh",
|
|
13572
|
+
status: metadata.status,
|
|
13573
|
+
confidence: 1,
|
|
13574
|
+
created_at: metadata.createdAt,
|
|
13575
|
+
updated_at: metadata.updatedAt,
|
|
13576
|
+
compiled_from: timelineManifests.map((manifest) => manifest.sourceId),
|
|
13577
|
+
managed_by: metadata.managedBy,
|
|
13578
|
+
backlinks: [],
|
|
13579
|
+
schema_hash: schemaHash,
|
|
13580
|
+
source_hashes: {},
|
|
13581
|
+
source_semantic_hashes: {}
|
|
13582
|
+
}
|
|
13583
|
+
)
|
|
13584
|
+
},
|
|
13467
13585
|
{
|
|
13468
13586
|
relativePath: "dashboards/timeline.md",
|
|
13469
13587
|
title: "Timeline",
|
|
@@ -13507,6 +13625,169 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13507
13625
|
}
|
|
13508
13626
|
)
|
|
13509
13627
|
},
|
|
13628
|
+
{
|
|
13629
|
+
relativePath: "dashboards/source-sessions.md",
|
|
13630
|
+
title: "Source Sessions",
|
|
13631
|
+
content: (metadata) => matter9.stringify(
|
|
13632
|
+
[
|
|
13633
|
+
"# Source Sessions",
|
|
13634
|
+
"",
|
|
13635
|
+
"## Active Sessions",
|
|
13636
|
+
"",
|
|
13637
|
+
...sourceSessions.length ? sourceSessions.slice(0, 16).map(
|
|
13638
|
+
(session) => `- ${session.updatedAt}: \`${session.status}\` \`${session.sessionId}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
|
|
13639
|
+
) : ["- No guided source sessions yet."],
|
|
13640
|
+
"",
|
|
13641
|
+
"## Pending Guided Bundles",
|
|
13642
|
+
"",
|
|
13643
|
+
...stagedGuideBundles.length ? stagedGuideBundles.map(
|
|
13644
|
+
(bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
|
|
13645
|
+
) : ["- No staged guided bundles right now."],
|
|
13646
|
+
"",
|
|
13647
|
+
"```dataview",
|
|
13648
|
+
'LIST FROM "outputs/source-sessions"',
|
|
13649
|
+
"SORT file.mtime desc",
|
|
13650
|
+
"```",
|
|
13651
|
+
""
|
|
13652
|
+
].join("\n"),
|
|
13653
|
+
{
|
|
13654
|
+
page_id: "dashboards:source-sessions",
|
|
13655
|
+
kind: "index",
|
|
13656
|
+
title: "Source Sessions",
|
|
13657
|
+
tags: ["index", "dashboard", "source-sessions"],
|
|
13658
|
+
source_ids: uniqueStrings3([
|
|
13659
|
+
...sessionPages.flatMap((page) => page.sourceIds),
|
|
13660
|
+
...sourceSessions.flatMap((session) => session.sourceIds)
|
|
13661
|
+
]),
|
|
13662
|
+
project_ids: [],
|
|
13663
|
+
node_ids: [],
|
|
13664
|
+
freshness: "fresh",
|
|
13665
|
+
status: metadata.status,
|
|
13666
|
+
confidence: 1,
|
|
13667
|
+
created_at: metadata.createdAt,
|
|
13668
|
+
updated_at: metadata.updatedAt,
|
|
13669
|
+
compiled_from: uniqueStrings3([
|
|
13670
|
+
...sessionPages.flatMap((page) => page.sourceIds),
|
|
13671
|
+
...sourceSessions.flatMap((session) => session.sourceIds)
|
|
13672
|
+
]),
|
|
13673
|
+
managed_by: metadata.managedBy,
|
|
13674
|
+
backlinks: [],
|
|
13675
|
+
schema_hash: schemaHash,
|
|
13676
|
+
source_hashes: {},
|
|
13677
|
+
source_semantic_hashes: {}
|
|
13678
|
+
}
|
|
13679
|
+
)
|
|
13680
|
+
},
|
|
13681
|
+
{
|
|
13682
|
+
relativePath: "dashboards/source-guides.md",
|
|
13683
|
+
title: "Source Guides",
|
|
13684
|
+
content: (metadata) => matter9.stringify(
|
|
13685
|
+
[
|
|
13686
|
+
"# Source Guides",
|
|
13687
|
+
"",
|
|
13688
|
+
...guidePages.length ? guidePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No accepted source guides yet."],
|
|
13689
|
+
"",
|
|
13690
|
+
"## Pending Guided Bundles",
|
|
13691
|
+
"",
|
|
13692
|
+
...stagedGuideBundles.length ? stagedGuideBundles.map(
|
|
13693
|
+
(bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
|
|
13694
|
+
) : ["- No staged guided bundles right now."],
|
|
13695
|
+
"",
|
|
13696
|
+
"```dataview",
|
|
13697
|
+
'LIST FROM "outputs/source-guides"',
|
|
13698
|
+
"SORT file.mtime desc",
|
|
13699
|
+
"```",
|
|
13700
|
+
""
|
|
13701
|
+
].join("\n"),
|
|
13702
|
+
{
|
|
13703
|
+
page_id: "dashboards:source-guides",
|
|
13704
|
+
kind: "index",
|
|
13705
|
+
title: "Source Guides",
|
|
13706
|
+
tags: ["index", "dashboard", "source-guides"],
|
|
13707
|
+
source_ids: uniqueStrings3([
|
|
13708
|
+
...guidePages.flatMap((page) => page.sourceIds),
|
|
13709
|
+
...stagedGuideBundles.flatMap((bundle) => bundle.entries.flatMap((entry) => entry.sourceIds))
|
|
13710
|
+
]),
|
|
13711
|
+
project_ids: [],
|
|
13712
|
+
node_ids: [],
|
|
13713
|
+
freshness: "fresh",
|
|
13714
|
+
status: metadata.status,
|
|
13715
|
+
confidence: 1,
|
|
13716
|
+
created_at: metadata.createdAt,
|
|
13717
|
+
updated_at: metadata.updatedAt,
|
|
13718
|
+
compiled_from: uniqueStrings3([
|
|
13719
|
+
...guidePages.flatMap((page) => page.sourceIds),
|
|
13720
|
+
...stagedGuideBundles.flatMap((bundle) => bundle.entries.flatMap((entry) => entry.sourceIds))
|
|
13721
|
+
]),
|
|
13722
|
+
managed_by: metadata.managedBy,
|
|
13723
|
+
backlinks: [],
|
|
13724
|
+
schema_hash: schemaHash,
|
|
13725
|
+
source_hashes: {},
|
|
13726
|
+
source_semantic_hashes: {}
|
|
13727
|
+
}
|
|
13728
|
+
)
|
|
13729
|
+
},
|
|
13730
|
+
{
|
|
13731
|
+
relativePath: "dashboards/research-map.md",
|
|
13732
|
+
title: "Research Map",
|
|
13733
|
+
content: (metadata) => matter9.stringify(
|
|
13734
|
+
[
|
|
13735
|
+
"# Research Map",
|
|
13736
|
+
"",
|
|
13737
|
+
"## Canonical Concept Pages",
|
|
13738
|
+
"",
|
|
13739
|
+
...conceptPages.length ? conceptPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No concept pages yet."],
|
|
13740
|
+
"",
|
|
13741
|
+
"## Canonical Entity Pages",
|
|
13742
|
+
"",
|
|
13743
|
+
...entityPages.length ? entityPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No entity pages yet."],
|
|
13744
|
+
"",
|
|
13745
|
+
"## Recently Guided Sources",
|
|
13746
|
+
"",
|
|
13747
|
+
...guidePages.length ? guidePages.slice(0, 8).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No accepted source guides yet."],
|
|
13748
|
+
"",
|
|
13749
|
+
"## Active Source Sessions",
|
|
13750
|
+
"",
|
|
13751
|
+
...sourceSessions.length ? sourceSessions.slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`) : ["- No active source sessions yet."],
|
|
13752
|
+
...report?.suggestedQuestions?.length ? ["", "## Suggested Questions", "", ...report.suggestedQuestions.slice(0, 8).map((question) => `- ${question}`)] : [],
|
|
13753
|
+
"",
|
|
13754
|
+
"```dataview",
|
|
13755
|
+
'TABLE file.folder, file.mtime FROM "concepts" OR "entities"',
|
|
13756
|
+
"SORT file.mtime desc",
|
|
13757
|
+
"LIMIT 30",
|
|
13758
|
+
"```",
|
|
13759
|
+
""
|
|
13760
|
+
].join("\n"),
|
|
13761
|
+
{
|
|
13762
|
+
page_id: "dashboards:research-map",
|
|
13763
|
+
kind: "index",
|
|
13764
|
+
title: "Research Map",
|
|
13765
|
+
tags: ["index", "dashboard", "research-map"],
|
|
13766
|
+
source_ids: uniqueStrings3([
|
|
13767
|
+
...conceptPages.flatMap((page) => page.sourceIds),
|
|
13768
|
+
...entityPages.flatMap((page) => page.sourceIds),
|
|
13769
|
+
...guidePages.flatMap((page) => page.sourceIds)
|
|
13770
|
+
]),
|
|
13771
|
+
project_ids: [],
|
|
13772
|
+
node_ids: [],
|
|
13773
|
+
freshness: "fresh",
|
|
13774
|
+
status: metadata.status,
|
|
13775
|
+
confidence: 1,
|
|
13776
|
+
created_at: metadata.createdAt,
|
|
13777
|
+
updated_at: metadata.updatedAt,
|
|
13778
|
+
compiled_from: uniqueStrings3([
|
|
13779
|
+
...conceptPages.flatMap((page) => page.sourceIds),
|
|
13780
|
+
...entityPages.flatMap((page) => page.sourceIds),
|
|
13781
|
+
...guidePages.flatMap((page) => page.sourceIds)
|
|
13782
|
+
]),
|
|
13783
|
+
managed_by: metadata.managedBy,
|
|
13784
|
+
backlinks: [],
|
|
13785
|
+
schema_hash: schemaHash,
|
|
13786
|
+
source_hashes: {},
|
|
13787
|
+
source_semantic_hashes: {}
|
|
13788
|
+
}
|
|
13789
|
+
)
|
|
13790
|
+
},
|
|
13510
13791
|
{
|
|
13511
13792
|
relativePath: "dashboards/contradictions.md",
|
|
13512
13793
|
title: "Contradictions",
|
|
@@ -13520,14 +13801,14 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13520
13801
|
return `- ${left} / ${right}: ${contradiction.claimA} <> ${contradiction.claimB}`;
|
|
13521
13802
|
}) : ["- No contradictions are currently flagged."],
|
|
13522
13803
|
"",
|
|
13523
|
-
...reviewPages.length || briefPages.length ? [
|
|
13804
|
+
...reviewPages.length || briefPages.length || guidePages.length ? [
|
|
13524
13805
|
"## Related Reviews",
|
|
13525
13806
|
"",
|
|
13526
|
-
...[...reviewPages, ...briefPages].slice(0, 12).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
|
|
13807
|
+
...[...guidePages, ...reviewPages, ...briefPages].slice(0, 12).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
|
|
13527
13808
|
""
|
|
13528
13809
|
] : [],
|
|
13529
13810
|
"```dataview",
|
|
13530
|
-
'LIST FROM "outputs/source-reviews"',
|
|
13811
|
+
'LIST FROM "outputs/source-reviews" OR "outputs/source-guides"',
|
|
13531
13812
|
"SORT file.mtime desc",
|
|
13532
13813
|
"```",
|
|
13533
13814
|
""
|
|
@@ -13562,9 +13843,15 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13562
13843
|
"# Open Questions",
|
|
13563
13844
|
"",
|
|
13564
13845
|
...openQuestions.length ? openQuestions.map((question) => `- ${question}`) : ["- No open questions are currently extracted."],
|
|
13846
|
+
...sourceSessions.length ? [
|
|
13847
|
+
"",
|
|
13848
|
+
"## Active Guided Sessions",
|
|
13849
|
+
"",
|
|
13850
|
+
...sourceSessions.filter((session) => session.status === "awaiting_input" || session.status === "staged").slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`)
|
|
13851
|
+
] : [],
|
|
13565
13852
|
"",
|
|
13566
13853
|
"```dataview",
|
|
13567
|
-
'LIST FROM "outputs/source-briefs" OR "outputs/source-reviews"',
|
|
13854
|
+
'LIST FROM "outputs/source-briefs" OR "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
|
|
13568
13855
|
"SORT file.mtime desc",
|
|
13569
13856
|
"```",
|
|
13570
13857
|
""
|
|
@@ -13594,7 +13881,7 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
|
|
|
13594
13881
|
];
|
|
13595
13882
|
const records = [];
|
|
13596
13883
|
for (const dashboard of dashboards) {
|
|
13597
|
-
const absolutePath =
|
|
13884
|
+
const absolutePath = path23.join(paths.wikiDir, dashboard.relativePath);
|
|
13598
13885
|
const compiledFrom = dashboard.relativePath === "dashboards/recent-sources.md" ? recentSourcePages.flatMap((page) => page.sourceIds) : [];
|
|
13599
13886
|
const content = await buildManagedContent(
|
|
13600
13887
|
absolutePath,
|
|
@@ -13708,7 +13995,7 @@ function resetGraphNodeMetrics(nodes) {
|
|
|
13708
13995
|
return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
|
|
13709
13996
|
}
|
|
13710
13997
|
function manifestRepoPath(manifest) {
|
|
13711
|
-
return toPosix(manifest.repoRelativePath ??
|
|
13998
|
+
return toPosix(manifest.repoRelativePath ?? path23.basename(manifest.originalPath ?? manifest.storedPath));
|
|
13712
13999
|
}
|
|
13713
14000
|
function goPackageScopeKey(manifest, analysis) {
|
|
13714
14001
|
if (analysis.code?.language !== "go") {
|
|
@@ -13718,7 +14005,7 @@ function goPackageScopeKey(manifest, analysis) {
|
|
|
13718
14005
|
if (!packageName) {
|
|
13719
14006
|
return null;
|
|
13720
14007
|
}
|
|
13721
|
-
return `${packageName}:${
|
|
14008
|
+
return `${packageName}:${path23.posix.dirname(manifestRepoPath(manifest))}`;
|
|
13722
14009
|
}
|
|
13723
14010
|
function buildGoPackageSymbolLookups(analyses, manifestsById) {
|
|
13724
14011
|
const lookups = /* @__PURE__ */ new Map();
|
|
@@ -14187,7 +14474,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
14187
14474
|
const benchmark = await readJsonFile(paths.benchmarkPath);
|
|
14188
14475
|
const communityRecords = [];
|
|
14189
14476
|
for (const community of graph.communities ?? []) {
|
|
14190
|
-
const absolutePath =
|
|
14477
|
+
const absolutePath = path23.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
|
|
14191
14478
|
communityRecords.push(
|
|
14192
14479
|
await buildManagedGraphPage(
|
|
14193
14480
|
absolutePath,
|
|
@@ -14216,7 +14503,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
14216
14503
|
graphHash: graphHash(graph),
|
|
14217
14504
|
contradictions
|
|
14218
14505
|
});
|
|
14219
|
-
const reportAbsolutePath =
|
|
14506
|
+
const reportAbsolutePath = path23.join(paths.wikiDir, "graph", "report.md");
|
|
14220
14507
|
const reportRecord = await buildManagedGraphPage(
|
|
14221
14508
|
reportAbsolutePath,
|
|
14222
14509
|
{
|
|
@@ -14237,7 +14524,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
14237
14524
|
};
|
|
14238
14525
|
}
|
|
14239
14526
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
14240
|
-
const absolutePath =
|
|
14527
|
+
const absolutePath = path23.resolve(wikiDir, relativePath);
|
|
14241
14528
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
14242
14529
|
if (changed) {
|
|
14243
14530
|
changedPages.push(relativePath);
|
|
@@ -14303,29 +14590,29 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
14303
14590
|
paths.graphPath,
|
|
14304
14591
|
paths.codeIndexPath,
|
|
14305
14592
|
paths.searchDbPath,
|
|
14306
|
-
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
|
|
14310
|
-
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
|
|
14593
|
+
path23.join(paths.wikiDir, "index.md"),
|
|
14594
|
+
path23.join(paths.wikiDir, "sources", "index.md"),
|
|
14595
|
+
path23.join(paths.wikiDir, "code", "index.md"),
|
|
14596
|
+
path23.join(paths.wikiDir, "concepts", "index.md"),
|
|
14597
|
+
path23.join(paths.wikiDir, "entities", "index.md"),
|
|
14598
|
+
path23.join(paths.wikiDir, "outputs", "index.md"),
|
|
14599
|
+
path23.join(paths.wikiDir, "projects", "index.md"),
|
|
14600
|
+
path23.join(paths.wikiDir, "candidates", "index.md")
|
|
14314
14601
|
];
|
|
14315
14602
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
14316
14603
|
return checks.every(Boolean);
|
|
14317
14604
|
}
|
|
14318
14605
|
async function loadAvailableCachedAnalyses(paths, manifests) {
|
|
14319
14606
|
const analyses = await Promise.all(
|
|
14320
|
-
manifests.map(async (manifest) => readJsonFile(
|
|
14607
|
+
manifests.map(async (manifest) => readJsonFile(path23.join(paths.analysesDir, `${manifest.sourceId}.json`)))
|
|
14321
14608
|
);
|
|
14322
14609
|
return analyses.filter((analysis) => Boolean(analysis));
|
|
14323
14610
|
}
|
|
14324
14611
|
function approvalManifestPath(paths, approvalId) {
|
|
14325
|
-
return
|
|
14612
|
+
return path23.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
14326
14613
|
}
|
|
14327
14614
|
function approvalGraphPath(paths, approvalId) {
|
|
14328
|
-
return
|
|
14615
|
+
return path23.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
14329
14616
|
}
|
|
14330
14617
|
async function readApprovalManifest(paths, approvalId) {
|
|
14331
14618
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -14335,10 +14622,10 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
14335
14622
|
return manifest;
|
|
14336
14623
|
}
|
|
14337
14624
|
async function writeApprovalManifest(paths, manifest) {
|
|
14338
|
-
await
|
|
14625
|
+
await fs19.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
14339
14626
|
`, "utf8");
|
|
14340
14627
|
}
|
|
14341
|
-
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
14628
|
+
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph, labelsByPath = /* @__PURE__ */ new Map()) {
|
|
14342
14629
|
const previousPagesById = new Map((previousGraph?.pages ?? []).map((page) => [page.id, page]));
|
|
14343
14630
|
const previousPagesByPath = new Map((previousGraph?.pages ?? []).map((page) => [page.path, page]));
|
|
14344
14631
|
const nextPagesByPath = new Map(graph.pages.map((page) => [page.path, page]));
|
|
@@ -14350,7 +14637,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
14350
14637
|
continue;
|
|
14351
14638
|
}
|
|
14352
14639
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
14353
|
-
const currentExists = await fileExists(
|
|
14640
|
+
const currentExists = await fileExists(path23.join(paths.wikiDir, file.relativePath));
|
|
14354
14641
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
14355
14642
|
entries.push({
|
|
14356
14643
|
pageId: nextPage.id,
|
|
@@ -14360,7 +14647,8 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
14360
14647
|
status: "pending",
|
|
14361
14648
|
sourceIds: nextPage.sourceIds,
|
|
14362
14649
|
nextPath: nextPage.path,
|
|
14363
|
-
previousPath: previousPage.path
|
|
14650
|
+
previousPath: previousPage.path,
|
|
14651
|
+
label: labelsByPath.get(nextPage.path) ?? labelsByPath.get(previousPage.path)
|
|
14364
14652
|
});
|
|
14365
14653
|
handledDeletedPaths.add(previousPage.path);
|
|
14366
14654
|
continue;
|
|
@@ -14373,7 +14661,8 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
14373
14661
|
status: "pending",
|
|
14374
14662
|
sourceIds: nextPage.sourceIds,
|
|
14375
14663
|
nextPath: nextPage.path,
|
|
14376
|
-
previousPath: previousPage?.path
|
|
14664
|
+
previousPath: previousPage?.path,
|
|
14665
|
+
label: labelsByPath.get(nextPage.path) ?? (previousPage?.path ? labelsByPath.get(previousPage.path) : void 0)
|
|
14377
14666
|
});
|
|
14378
14667
|
}
|
|
14379
14668
|
for (const deletedPath of deletedPaths.sort((left, right) => left.localeCompare(right))) {
|
|
@@ -14383,31 +14672,34 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
14383
14672
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
14384
14673
|
entries.push({
|
|
14385
14674
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
14386
|
-
title: previousPage?.title ??
|
|
14675
|
+
title: previousPage?.title ?? path23.basename(deletedPath, ".md"),
|
|
14387
14676
|
kind: previousPage?.kind ?? "index",
|
|
14388
14677
|
changeType: "delete",
|
|
14389
14678
|
status: "pending",
|
|
14390
14679
|
sourceIds: previousPage?.sourceIds ?? [],
|
|
14391
|
-
previousPath: deletedPath
|
|
14680
|
+
previousPath: deletedPath,
|
|
14681
|
+
label: labelsByPath.get(deletedPath)
|
|
14392
14682
|
});
|
|
14393
14683
|
}
|
|
14394
14684
|
return uniqueBy(entries, (entry) => `${entry.pageId}:${entry.changeType}:${entry.nextPath ?? ""}:${entry.previousPath ?? ""}`);
|
|
14395
14685
|
}
|
|
14396
14686
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
14397
14687
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
14398
|
-
const approvalDir =
|
|
14688
|
+
const approvalDir = path23.join(paths.approvalsDir, approvalId);
|
|
14399
14689
|
await ensureDir(approvalDir);
|
|
14400
|
-
await ensureDir(
|
|
14401
|
-
await ensureDir(
|
|
14690
|
+
await ensureDir(path23.join(approvalDir, "wiki"));
|
|
14691
|
+
await ensureDir(path23.join(approvalDir, "state"));
|
|
14402
14692
|
for (const file of changedFiles) {
|
|
14403
|
-
const targetPath =
|
|
14404
|
-
await ensureDir(
|
|
14405
|
-
await
|
|
14693
|
+
const targetPath = path23.join(approvalDir, "wiki", file.relativePath);
|
|
14694
|
+
await ensureDir(path23.dirname(targetPath));
|
|
14695
|
+
await fs19.writeFile(targetPath, file.content, "utf8");
|
|
14406
14696
|
}
|
|
14407
|
-
await
|
|
14697
|
+
await fs19.writeFile(path23.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
14408
14698
|
await writeApprovalManifest(paths, {
|
|
14409
14699
|
approvalId,
|
|
14410
14700
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14701
|
+
bundleType: "compile",
|
|
14702
|
+
title: "Compile Approval",
|
|
14411
14703
|
entries: await buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph)
|
|
14412
14704
|
});
|
|
14413
14705
|
return { approvalId, approvalDir };
|
|
@@ -14468,7 +14760,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14468
14760
|
confidence: 1
|
|
14469
14761
|
});
|
|
14470
14762
|
const sourceRecord = await buildManagedGraphPage(
|
|
14471
|
-
|
|
14763
|
+
path23.join(paths.wikiDir, preview.path),
|
|
14472
14764
|
{
|
|
14473
14765
|
managedBy: "system",
|
|
14474
14766
|
confidence: 1,
|
|
@@ -14514,7 +14806,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14514
14806
|
);
|
|
14515
14807
|
records.push(
|
|
14516
14808
|
await buildManagedGraphPage(
|
|
14517
|
-
|
|
14809
|
+
path23.join(paths.wikiDir, modulePreview.path),
|
|
14518
14810
|
{
|
|
14519
14811
|
managedBy: "system",
|
|
14520
14812
|
confidence: 1,
|
|
@@ -14548,8 +14840,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14548
14840
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
14549
14841
|
const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
|
|
14550
14842
|
const fallbackPaths = [
|
|
14551
|
-
|
|
14552
|
-
|
|
14843
|
+
path23.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
14844
|
+
path23.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
14553
14845
|
];
|
|
14554
14846
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
14555
14847
|
const preview = emptyGraphPage({
|
|
@@ -14567,7 +14859,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14567
14859
|
status: promoted ? "active" : "candidate"
|
|
14568
14860
|
});
|
|
14569
14861
|
const pageRecord = await buildManagedGraphPage(
|
|
14570
|
-
|
|
14862
|
+
path23.join(paths.wikiDir, relativePath),
|
|
14571
14863
|
{
|
|
14572
14864
|
status: promoted ? "active" : "candidate",
|
|
14573
14865
|
managedBy: "system",
|
|
@@ -14696,7 +14988,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14696
14988
|
confidence: 1
|
|
14697
14989
|
}),
|
|
14698
14990
|
content: await buildManagedContent(
|
|
14699
|
-
|
|
14991
|
+
path23.join(paths.wikiDir, "projects", "index.md"),
|
|
14700
14992
|
{
|
|
14701
14993
|
managedBy: "system",
|
|
14702
14994
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -14720,7 +15012,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14720
15012
|
records.push({
|
|
14721
15013
|
page: projectIndexRef,
|
|
14722
15014
|
content: await buildManagedContent(
|
|
14723
|
-
|
|
15015
|
+
path23.join(paths.wikiDir, projectIndexRef.path),
|
|
14724
15016
|
{
|
|
14725
15017
|
managedBy: "system",
|
|
14726
15018
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -14748,7 +15040,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14748
15040
|
confidence: 1
|
|
14749
15041
|
}),
|
|
14750
15042
|
content: await buildManagedContent(
|
|
14751
|
-
|
|
15043
|
+
path23.join(paths.wikiDir, "index.md"),
|
|
14752
15044
|
{
|
|
14753
15045
|
managedBy: "system",
|
|
14754
15046
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -14784,7 +15076,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14784
15076
|
confidence: 1
|
|
14785
15077
|
}),
|
|
14786
15078
|
content: await buildManagedContent(
|
|
14787
|
-
|
|
15079
|
+
path23.join(paths.wikiDir, relativePath),
|
|
14788
15080
|
{
|
|
14789
15081
|
managedBy: "system",
|
|
14790
15082
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -14795,12 +15087,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14795
15087
|
}
|
|
14796
15088
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
14797
15089
|
const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
14798
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
15090
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
14799
15091
|
const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
14800
15092
|
const changedFiles = [];
|
|
14801
15093
|
for (const record of records) {
|
|
14802
|
-
const absolutePath =
|
|
14803
|
-
const current = await fileExists(absolutePath) ? await
|
|
15094
|
+
const absolutePath = path23.join(paths.wikiDir, record.page.path);
|
|
15095
|
+
const current = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
|
|
14804
15096
|
if (current !== record.content) {
|
|
14805
15097
|
changedPages.push(record.page.path);
|
|
14806
15098
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -14825,10 +15117,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
14825
15117
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
14826
15118
|
}
|
|
14827
15119
|
for (const relativePath of obsoletePaths) {
|
|
14828
|
-
await
|
|
15120
|
+
await fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true });
|
|
14829
15121
|
}
|
|
14830
15122
|
await writeJsonFile(paths.graphPath, graph);
|
|
14831
|
-
await writeJsonFile(
|
|
15123
|
+
await writeJsonFile(path23.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
14832
15124
|
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
14833
15125
|
await writeJsonFile(paths.compileStatePath, {
|
|
14834
15126
|
generatedAt: graph.generatedAt,
|
|
@@ -14923,18 +15215,18 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
14923
15215
|
})
|
|
14924
15216
|
);
|
|
14925
15217
|
await Promise.all([
|
|
14926
|
-
ensureDir(
|
|
14927
|
-
ensureDir(
|
|
14928
|
-
ensureDir(
|
|
14929
|
-
ensureDir(
|
|
14930
|
-
ensureDir(
|
|
14931
|
-
ensureDir(
|
|
14932
|
-
ensureDir(
|
|
14933
|
-
ensureDir(
|
|
14934
|
-
ensureDir(
|
|
14935
|
-
ensureDir(
|
|
15218
|
+
ensureDir(path23.join(paths.wikiDir, "sources")),
|
|
15219
|
+
ensureDir(path23.join(paths.wikiDir, "code")),
|
|
15220
|
+
ensureDir(path23.join(paths.wikiDir, "concepts")),
|
|
15221
|
+
ensureDir(path23.join(paths.wikiDir, "entities")),
|
|
15222
|
+
ensureDir(path23.join(paths.wikiDir, "outputs")),
|
|
15223
|
+
ensureDir(path23.join(paths.wikiDir, "dashboards")),
|
|
15224
|
+
ensureDir(path23.join(paths.wikiDir, "graph")),
|
|
15225
|
+
ensureDir(path23.join(paths.wikiDir, "graph", "communities")),
|
|
15226
|
+
ensureDir(path23.join(paths.wikiDir, "projects")),
|
|
15227
|
+
ensureDir(path23.join(paths.wikiDir, "candidates"))
|
|
14936
15228
|
]);
|
|
14937
|
-
const projectsIndexPath =
|
|
15229
|
+
const projectsIndexPath = path23.join(paths.wikiDir, "projects", "index.md");
|
|
14938
15230
|
await writeFileIfChanged(
|
|
14939
15231
|
projectsIndexPath,
|
|
14940
15232
|
await buildManagedContent(
|
|
@@ -14955,7 +15247,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
14955
15247
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
14956
15248
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
14957
15249
|
};
|
|
14958
|
-
const absolutePath =
|
|
15250
|
+
const absolutePath = path23.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
14959
15251
|
await writeFileIfChanged(
|
|
14960
15252
|
absolutePath,
|
|
14961
15253
|
await buildManagedContent(
|
|
@@ -14973,7 +15265,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
14973
15265
|
)
|
|
14974
15266
|
);
|
|
14975
15267
|
}
|
|
14976
|
-
const rootIndexPath =
|
|
15268
|
+
const rootIndexPath = path23.join(paths.wikiDir, "index.md");
|
|
14977
15269
|
await writeFileIfChanged(
|
|
14978
15270
|
rootIndexPath,
|
|
14979
15271
|
await buildManagedContent(
|
|
@@ -14999,7 +15291,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
14999
15291
|
["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
|
|
15000
15292
|
["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
|
|
15001
15293
|
]) {
|
|
15002
|
-
const absolutePath =
|
|
15294
|
+
const absolutePath = path23.join(paths.wikiDir, relativePath);
|
|
15003
15295
|
await writeFileIfChanged(
|
|
15004
15296
|
absolutePath,
|
|
15005
15297
|
await buildManagedContent(
|
|
@@ -15013,31 +15305,31 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
15013
15305
|
);
|
|
15014
15306
|
}
|
|
15015
15307
|
for (const record of graphOrientation.records) {
|
|
15016
|
-
await writeFileIfChanged(
|
|
15308
|
+
await writeFileIfChanged(path23.join(paths.wikiDir, record.page.path), record.content);
|
|
15017
15309
|
}
|
|
15018
15310
|
for (const record of dashboardRecords) {
|
|
15019
|
-
await writeFileIfChanged(
|
|
15311
|
+
await writeFileIfChanged(path23.join(paths.wikiDir, record.page.path), record.content);
|
|
15020
15312
|
}
|
|
15021
15313
|
if (graphOrientation.report) {
|
|
15022
|
-
await writeJsonFile(
|
|
15314
|
+
await writeJsonFile(path23.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
|
|
15023
15315
|
}
|
|
15024
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
15316
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
|
|
15025
15317
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
15026
15318
|
"projects/index.md",
|
|
15027
15319
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
15028
15320
|
]);
|
|
15029
15321
|
await Promise.all(
|
|
15030
|
-
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) =>
|
|
15322
|
+
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
|
|
15031
15323
|
);
|
|
15032
|
-
const existingGraphPages = (await listFilesRecursive(
|
|
15324
|
+
const existingGraphPages = (await listFilesRecursive(path23.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
|
|
15033
15325
|
const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
|
|
15034
15326
|
await Promise.all(
|
|
15035
|
-
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) =>
|
|
15327
|
+
existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
|
|
15036
15328
|
);
|
|
15037
|
-
const existingDashboardPages = (await listFilesRecursive(
|
|
15329
|
+
const existingDashboardPages = (await listFilesRecursive(path23.join(paths.wikiDir, "dashboards")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
|
|
15038
15330
|
const allowedDashboardPages = /* @__PURE__ */ new Set(["dashboards/index.md", ...dashboardRecords.map((record) => record.page.path)]);
|
|
15039
15331
|
await Promise.all(
|
|
15040
|
-
existingDashboardPages.filter((relativePath) => !allowedDashboardPages.has(relativePath)).map((relativePath) =>
|
|
15332
|
+
existingDashboardPages.filter((relativePath) => !allowedDashboardPages.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
|
|
15041
15333
|
);
|
|
15042
15334
|
await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
|
|
15043
15335
|
}
|
|
@@ -15057,7 +15349,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
15057
15349
|
confidence: 0.74
|
|
15058
15350
|
}
|
|
15059
15351
|
});
|
|
15060
|
-
const absolutePath =
|
|
15352
|
+
const absolutePath = path23.join(paths.wikiDir, output.page.path);
|
|
15061
15353
|
return {
|
|
15062
15354
|
page: output.page,
|
|
15063
15355
|
savedPath: absolutePath,
|
|
@@ -15069,15 +15361,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
15069
15361
|
async function persistOutputPage(rootDir, input) {
|
|
15070
15362
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15071
15363
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
15072
|
-
await ensureDir(
|
|
15073
|
-
await
|
|
15364
|
+
await ensureDir(path23.dirname(prepared.savedPath));
|
|
15365
|
+
await fs19.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
15074
15366
|
for (const assetFile of prepared.assetFiles) {
|
|
15075
|
-
const assetPath =
|
|
15076
|
-
await ensureDir(
|
|
15367
|
+
const assetPath = path23.join(paths.wikiDir, assetFile.relativePath);
|
|
15368
|
+
await ensureDir(path23.dirname(assetPath));
|
|
15077
15369
|
if (typeof assetFile.content === "string") {
|
|
15078
|
-
await
|
|
15370
|
+
await fs19.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
15079
15371
|
} else {
|
|
15080
|
-
await
|
|
15372
|
+
await fs19.writeFile(assetPath, assetFile.content);
|
|
15081
15373
|
}
|
|
15082
15374
|
}
|
|
15083
15375
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -15098,7 +15390,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
15098
15390
|
confidence: 0.76
|
|
15099
15391
|
}
|
|
15100
15392
|
});
|
|
15101
|
-
const absolutePath =
|
|
15393
|
+
const absolutePath = path23.join(paths.wikiDir, hub.page.path);
|
|
15102
15394
|
return {
|
|
15103
15395
|
page: hub.page,
|
|
15104
15396
|
savedPath: absolutePath,
|
|
@@ -15110,20 +15402,20 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
15110
15402
|
async function persistExploreHub(rootDir, input) {
|
|
15111
15403
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15112
15404
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
15113
|
-
await ensureDir(
|
|
15114
|
-
await
|
|
15405
|
+
await ensureDir(path23.dirname(prepared.savedPath));
|
|
15406
|
+
await fs19.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
15115
15407
|
for (const assetFile of prepared.assetFiles) {
|
|
15116
|
-
const assetPath =
|
|
15117
|
-
await ensureDir(
|
|
15408
|
+
const assetPath = path23.join(paths.wikiDir, assetFile.relativePath);
|
|
15409
|
+
await ensureDir(path23.dirname(assetPath));
|
|
15118
15410
|
if (typeof assetFile.content === "string") {
|
|
15119
|
-
await
|
|
15411
|
+
await fs19.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
15120
15412
|
} else {
|
|
15121
|
-
await
|
|
15413
|
+
await fs19.writeFile(assetPath, assetFile.content);
|
|
15122
15414
|
}
|
|
15123
15415
|
}
|
|
15124
15416
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
15125
15417
|
}
|
|
15126
|
-
async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
15418
|
+
async function stageOutputApprovalBundle(rootDir, stagedPages, options = {}) {
|
|
15127
15419
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15128
15420
|
const previousGraph = await readJsonFile(paths.graphPath);
|
|
15129
15421
|
const changedFiles = stagedPages.flatMap((item) => [
|
|
@@ -15134,18 +15426,19 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
15134
15426
|
binary: typeof assetFile.content !== "string"
|
|
15135
15427
|
}))
|
|
15136
15428
|
]);
|
|
15429
|
+
const labelsByPath = new Map(stagedPages.filter((item) => item.label).map((item) => [item.page.path, item.label]));
|
|
15137
15430
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
15138
|
-
const approvalDir =
|
|
15431
|
+
const approvalDir = path23.join(paths.approvalsDir, approvalId);
|
|
15139
15432
|
await ensureDir(approvalDir);
|
|
15140
|
-
await ensureDir(
|
|
15141
|
-
await ensureDir(
|
|
15433
|
+
await ensureDir(path23.join(approvalDir, "wiki"));
|
|
15434
|
+
await ensureDir(path23.join(approvalDir, "state"));
|
|
15142
15435
|
for (const file of changedFiles) {
|
|
15143
|
-
const targetPath =
|
|
15144
|
-
await ensureDir(
|
|
15436
|
+
const targetPath = path23.join(approvalDir, "wiki", file.relativePath);
|
|
15437
|
+
await ensureDir(path23.dirname(targetPath));
|
|
15145
15438
|
if ("binary" in file && file.binary) {
|
|
15146
|
-
await
|
|
15439
|
+
await fs19.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
15147
15440
|
} else {
|
|
15148
|
-
await
|
|
15441
|
+
await fs19.writeFile(targetPath, file.content, "utf8");
|
|
15149
15442
|
}
|
|
15150
15443
|
}
|
|
15151
15444
|
const nextPages = sortGraphPages([
|
|
@@ -15160,22 +15453,26 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
15160
15453
|
sources: previousGraph?.sources ?? [],
|
|
15161
15454
|
pages: nextPages
|
|
15162
15455
|
};
|
|
15163
|
-
await
|
|
15456
|
+
await fs19.writeFile(path23.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
15164
15457
|
await writeApprovalManifest(paths, {
|
|
15165
15458
|
approvalId,
|
|
15166
15459
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15460
|
+
bundleType: options.bundleType ?? "generated_output",
|
|
15461
|
+
title: options.title,
|
|
15462
|
+
sourceSessionId: options.sourceSessionId,
|
|
15167
15463
|
entries: await buildApprovalEntries(
|
|
15168
15464
|
paths,
|
|
15169
15465
|
stagedPages.map((item) => ({ relativePath: item.page.path, content: item.content })),
|
|
15170
15466
|
[],
|
|
15171
15467
|
previousGraph ?? null,
|
|
15172
|
-
graph
|
|
15468
|
+
graph,
|
|
15469
|
+
labelsByPath
|
|
15173
15470
|
)
|
|
15174
15471
|
});
|
|
15175
15472
|
return { approvalId, approvalDir };
|
|
15176
15473
|
}
|
|
15177
|
-
async function stageGeneratedOutputPages(rootDir, stagedPages) {
|
|
15178
|
-
return await stageOutputApprovalBundle(rootDir, stagedPages);
|
|
15474
|
+
async function stageGeneratedOutputPages(rootDir, stagedPages, options = {}) {
|
|
15475
|
+
return await stageOutputApprovalBundle(rootDir, stagedPages, options);
|
|
15179
15476
|
}
|
|
15180
15477
|
async function executeQuery(rootDir, question, format) {
|
|
15181
15478
|
const { paths } = await loadVaultConfig(rootDir);
|
|
@@ -15192,9 +15489,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
15192
15489
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
15193
15490
|
const excerpts = await Promise.all(
|
|
15194
15491
|
searchResults.map(async (result) => {
|
|
15195
|
-
const absolutePath =
|
|
15492
|
+
const absolutePath = path23.join(paths.wikiDir, result.path);
|
|
15196
15493
|
try {
|
|
15197
|
-
const content = await
|
|
15494
|
+
const content = await fs19.readFile(absolutePath, "utf8");
|
|
15198
15495
|
const parsed = matter9(content);
|
|
15199
15496
|
return `# ${result.title}
|
|
15200
15497
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -15429,7 +15726,7 @@ function computeChangeSummary(current, staged, changeType) {
|
|
|
15429
15726
|
async function listApprovals(rootDir) {
|
|
15430
15727
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15431
15728
|
const manifests = await Promise.all(
|
|
15432
|
-
(await
|
|
15729
|
+
(await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
15433
15730
|
try {
|
|
15434
15731
|
return await readApprovalManifest(paths, entry.name);
|
|
15435
15732
|
} catch {
|
|
@@ -15445,8 +15742,8 @@ async function readApproval(rootDir, approvalId, options) {
|
|
|
15445
15742
|
const details = await Promise.all(
|
|
15446
15743
|
manifest.entries.map(async (entry) => {
|
|
15447
15744
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
15448
|
-
const currentContent = currentPath ? await
|
|
15449
|
-
const stagedContent = entry.nextPath ? await
|
|
15745
|
+
const currentContent = currentPath ? await fs19.readFile(path23.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
15746
|
+
const stagedContent = entry.nextPath ? await fs19.readFile(path23.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
15450
15747
|
const detail = {
|
|
15451
15748
|
...entry,
|
|
15452
15749
|
currentContent,
|
|
@@ -15479,26 +15776,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
15479
15776
|
if (!entry.nextPath) {
|
|
15480
15777
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
15481
15778
|
}
|
|
15482
|
-
const stagedAbsolutePath =
|
|
15483
|
-
const stagedContent = await
|
|
15484
|
-
const targetAbsolutePath =
|
|
15485
|
-
await ensureDir(
|
|
15486
|
-
await
|
|
15779
|
+
const stagedAbsolutePath = path23.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
|
|
15780
|
+
const stagedContent = await fs19.readFile(stagedAbsolutePath, "utf8");
|
|
15781
|
+
const targetAbsolutePath = path23.join(paths.wikiDir, entry.nextPath);
|
|
15782
|
+
await ensureDir(path23.dirname(targetAbsolutePath));
|
|
15783
|
+
await fs19.writeFile(targetAbsolutePath, stagedContent, "utf8");
|
|
15487
15784
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
15488
|
-
await
|
|
15785
|
+
await fs19.rm(path23.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
15489
15786
|
}
|
|
15490
15787
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
15491
15788
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
15492
|
-
const outputAssetDir =
|
|
15493
|
-
await
|
|
15789
|
+
const outputAssetDir = path23.join(paths.wikiDir, "outputs", "assets", path23.basename(nextPage.path, ".md"));
|
|
15790
|
+
await fs19.rm(outputAssetDir, { recursive: true, force: true });
|
|
15494
15791
|
for (const asset of nextPage.outputAssets) {
|
|
15495
|
-
const stagedAssetPath =
|
|
15792
|
+
const stagedAssetPath = path23.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
15496
15793
|
if (!await fileExists(stagedAssetPath)) {
|
|
15497
15794
|
continue;
|
|
15498
15795
|
}
|
|
15499
|
-
const targetAssetPath =
|
|
15500
|
-
await ensureDir(
|
|
15501
|
-
await
|
|
15796
|
+
const targetAssetPath = path23.join(paths.wikiDir, asset.path);
|
|
15797
|
+
await ensureDir(path23.dirname(targetAssetPath));
|
|
15798
|
+
await fs19.copyFile(stagedAssetPath, targetAssetPath);
|
|
15502
15799
|
}
|
|
15503
15800
|
}
|
|
15504
15801
|
nextPages = nextPages.filter(
|
|
@@ -15509,10 +15806,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
15509
15806
|
} else {
|
|
15510
15807
|
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;
|
|
15511
15808
|
if (entry.previousPath) {
|
|
15512
|
-
await
|
|
15809
|
+
await fs19.rm(path23.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
15513
15810
|
}
|
|
15514
15811
|
if (deletedPage?.kind === "output") {
|
|
15515
|
-
await
|
|
15812
|
+
await fs19.rm(path23.join(paths.wikiDir, "outputs", "assets", path23.basename(deletedPage.path, ".md")), {
|
|
15516
15813
|
recursive: true,
|
|
15517
15814
|
force: true
|
|
15518
15815
|
});
|
|
@@ -15535,6 +15832,9 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
15535
15832
|
await writeJsonFile(paths.compileStatePath, compileState);
|
|
15536
15833
|
await refreshIndexesAndSearch(rootDir, nextGraph.pages);
|
|
15537
15834
|
await writeApprovalManifest(paths, manifest);
|
|
15835
|
+
if (manifest.sourceSessionId) {
|
|
15836
|
+
await updateGuidedSourceSessionStatus(rootDir, manifest.sourceSessionId, "accepted");
|
|
15837
|
+
}
|
|
15538
15838
|
await recordSession(rootDir, {
|
|
15539
15839
|
operation: "review",
|
|
15540
15840
|
title: `Accepted review entries from ${approvalId}`,
|
|
@@ -15561,6 +15861,9 @@ async function rejectApproval(rootDir, approvalId, targets = []) {
|
|
|
15561
15861
|
entry.status = "rejected";
|
|
15562
15862
|
}
|
|
15563
15863
|
await writeApprovalManifest(paths, manifest);
|
|
15864
|
+
if (manifest.sourceSessionId) {
|
|
15865
|
+
await updateGuidedSourceSessionStatus(rootDir, manifest.sourceSessionId, "rejected");
|
|
15866
|
+
}
|
|
15564
15867
|
await recordSession(rootDir, {
|
|
15565
15868
|
operation: "review",
|
|
15566
15869
|
title: `Rejected review entries from ${approvalId}`,
|
|
@@ -15603,7 +15906,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
15603
15906
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15604
15907
|
const graph = await readJsonFile(paths.graphPath);
|
|
15605
15908
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
15606
|
-
const raw = await
|
|
15909
|
+
const raw = await fs19.readFile(path23.join(paths.wikiDir, candidate.path), "utf8");
|
|
15607
15910
|
const parsed = matter9(raw);
|
|
15608
15911
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15609
15912
|
const nextContent = matter9.stringify(parsed.content, {
|
|
@@ -15615,10 +15918,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
15615
15918
|
)
|
|
15616
15919
|
});
|
|
15617
15920
|
const nextPath = candidateActivePath(candidate);
|
|
15618
|
-
const nextAbsolutePath =
|
|
15619
|
-
await ensureDir(
|
|
15620
|
-
await
|
|
15621
|
-
await
|
|
15921
|
+
const nextAbsolutePath = path23.join(paths.wikiDir, nextPath);
|
|
15922
|
+
await ensureDir(path23.dirname(nextAbsolutePath));
|
|
15923
|
+
await fs19.writeFile(nextAbsolutePath, nextContent, "utf8");
|
|
15924
|
+
await fs19.rm(path23.join(paths.wikiDir, candidate.path), { force: true });
|
|
15622
15925
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
15623
15926
|
const nextPages = sortGraphPages(
|
|
15624
15927
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -15663,7 +15966,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
15663
15966
|
const { paths } = await loadVaultConfig(rootDir);
|
|
15664
15967
|
const graph = await readJsonFile(paths.graphPath);
|
|
15665
15968
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
15666
|
-
await
|
|
15969
|
+
await fs19.rm(path23.join(paths.wikiDir, candidate.path), { force: true });
|
|
15667
15970
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
15668
15971
|
const nextGraph = {
|
|
15669
15972
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -15702,18 +16005,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
15702
16005
|
}
|
|
15703
16006
|
async function ensureObsidianWorkspace(rootDir) {
|
|
15704
16007
|
const { config } = await loadVaultConfig(rootDir);
|
|
15705
|
-
const obsidianDir =
|
|
16008
|
+
const obsidianDir = path23.join(rootDir, ".obsidian");
|
|
15706
16009
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
15707
16010
|
await ensureDir(obsidianDir);
|
|
15708
16011
|
await Promise.all([
|
|
15709
|
-
writeJsonFile(
|
|
16012
|
+
writeJsonFile(path23.join(obsidianDir, "app.json"), {
|
|
15710
16013
|
alwaysUpdateLinks: true,
|
|
15711
16014
|
newFileLocation: "folder",
|
|
15712
16015
|
newFileFolderPath: "wiki/insights",
|
|
15713
16016
|
useMarkdownLinks: false,
|
|
15714
16017
|
attachmentFolderPath: "raw/assets"
|
|
15715
16018
|
}),
|
|
15716
|
-
writeJsonFile(
|
|
16019
|
+
writeJsonFile(path23.join(obsidianDir, "core-plugins.json"), [
|
|
15717
16020
|
"file-explorer",
|
|
15718
16021
|
"global-search",
|
|
15719
16022
|
"switcher",
|
|
@@ -15723,7 +16026,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
15723
16026
|
"tag-pane",
|
|
15724
16027
|
"page-preview"
|
|
15725
16028
|
]),
|
|
15726
|
-
writeJsonFile(
|
|
16029
|
+
writeJsonFile(path23.join(obsidianDir, "graph.json"), {
|
|
15727
16030
|
"collapse-filter": false,
|
|
15728
16031
|
search: "",
|
|
15729
16032
|
showTags: true,
|
|
@@ -15735,7 +16038,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
15735
16038
|
})),
|
|
15736
16039
|
localJumps: false
|
|
15737
16040
|
}),
|
|
15738
|
-
writeJsonFile(
|
|
16041
|
+
writeJsonFile(path23.join(obsidianDir, "workspace.json"), {
|
|
15739
16042
|
active: "root",
|
|
15740
16043
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
15741
16044
|
left: {
|
|
@@ -15748,22 +16051,32 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
15748
16051
|
]);
|
|
15749
16052
|
}
|
|
15750
16053
|
async function initVault(rootDir, options = {}) {
|
|
15751
|
-
const
|
|
16054
|
+
const profile = options.profile ?? "default";
|
|
16055
|
+
const { paths } = await initWorkspace(rootDir, { profile });
|
|
15752
16056
|
await installConfiguredAgents(rootDir);
|
|
15753
|
-
const insightsIndexPath =
|
|
16057
|
+
const insightsIndexPath = path23.join(paths.wikiDir, "insights", "index.md");
|
|
15754
16058
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15755
16059
|
await writeFileIfChanged(
|
|
15756
16060
|
insightsIndexPath,
|
|
15757
16061
|
matter9.stringify(
|
|
15758
|
-
[
|
|
16062
|
+
(profile === "personal-research" ? [
|
|
16063
|
+
"# Insights",
|
|
16064
|
+
"",
|
|
16065
|
+
"Human-authored research notes live here.",
|
|
16066
|
+
"",
|
|
16067
|
+
"- Use this folder for thesis notes, reading reflections, synthesis drafts, and decisions you want to keep explicitly human-authored.",
|
|
16068
|
+
"- Guided sessions can stage `wiki/insights/` updates through the approval queue, but SwarmVault never applies them without review.",
|
|
16069
|
+
"- Treat these pages as the human judgment layer for your vault.",
|
|
16070
|
+
""
|
|
16071
|
+
] : [
|
|
15759
16072
|
"# Insights",
|
|
15760
16073
|
"",
|
|
15761
16074
|
"Human-authored notes live here.",
|
|
15762
16075
|
"",
|
|
15763
16076
|
"- SwarmVault can read these pages during compile and query.",
|
|
15764
|
-
"- SwarmVault
|
|
16077
|
+
"- SwarmVault can stage insight-page updates through guided sessions, but it never applies them without review.",
|
|
15765
16078
|
""
|
|
15766
|
-
].join("\n"),
|
|
16079
|
+
]).join("\n"),
|
|
15767
16080
|
{
|
|
15768
16081
|
page_id: "insights:index",
|
|
15769
16082
|
kind: "index",
|
|
@@ -15787,7 +16100,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
15787
16100
|
)
|
|
15788
16101
|
);
|
|
15789
16102
|
await writeFileIfChanged(
|
|
15790
|
-
|
|
16103
|
+
path23.join(paths.wikiDir, "projects", "index.md"),
|
|
15791
16104
|
matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
15792
16105
|
page_id: "projects:index",
|
|
15793
16106
|
kind: "index",
|
|
@@ -15810,7 +16123,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
15810
16123
|
})
|
|
15811
16124
|
);
|
|
15812
16125
|
await writeFileIfChanged(
|
|
15813
|
-
|
|
16126
|
+
path23.join(paths.wikiDir, "candidates", "index.md"),
|
|
15814
16127
|
matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
15815
16128
|
page_id: "candidates:index",
|
|
15816
16129
|
kind: "index",
|
|
@@ -15835,6 +16148,43 @@ async function initVault(rootDir, options = {}) {
|
|
|
15835
16148
|
if (options.obsidian) {
|
|
15836
16149
|
await ensureObsidianWorkspace(rootDir);
|
|
15837
16150
|
}
|
|
16151
|
+
if (profile === "personal-research") {
|
|
16152
|
+
await writeFileIfChanged(
|
|
16153
|
+
path23.join(paths.wikiDir, "insights", "research-playbook.md"),
|
|
16154
|
+
matter9.stringify(
|
|
16155
|
+
[
|
|
16156
|
+
"# Personal Research Playbook",
|
|
16157
|
+
"",
|
|
16158
|
+
"- Add one source at a time with `swarmvault ingest <input> --guide` or `swarmvault source add <input> --guide`.",
|
|
16159
|
+
"- Resume a guided session with `swarmvault source session <source-id-or-session-id>` whenever you want to answer the session prompts directly.",
|
|
16160
|
+
"- Review `wiki/outputs/source-briefs/`, `wiki/outputs/source-reviews/`, `wiki/outputs/source-guides/`, and `wiki/outputs/source-sessions/` before accepting staged updates.",
|
|
16161
|
+
"- Keep unresolved questions visible in `wiki/dashboards/open-questions.md`.",
|
|
16162
|
+
"- Use `swarmvault review list` and `swarmvault review show --diff` to decide what becomes canonical.",
|
|
16163
|
+
""
|
|
16164
|
+
].join("\n"),
|
|
16165
|
+
{
|
|
16166
|
+
page_id: "insights:research-playbook",
|
|
16167
|
+
kind: "insight",
|
|
16168
|
+
title: "Personal Research Playbook",
|
|
16169
|
+
tags: ["insight", "research", "playbook"],
|
|
16170
|
+
source_ids: [],
|
|
16171
|
+
project_ids: [],
|
|
16172
|
+
node_ids: [],
|
|
16173
|
+
freshness: "fresh",
|
|
16174
|
+
status: "active",
|
|
16175
|
+
confidence: 1,
|
|
16176
|
+
created_at: now,
|
|
16177
|
+
updated_at: now,
|
|
16178
|
+
compiled_from: [],
|
|
16179
|
+
managed_by: "human",
|
|
16180
|
+
backlinks: [],
|
|
16181
|
+
schema_hash: "",
|
|
16182
|
+
source_hashes: {},
|
|
16183
|
+
source_semantic_hashes: {}
|
|
16184
|
+
}
|
|
16185
|
+
)
|
|
16186
|
+
);
|
|
16187
|
+
}
|
|
15838
16188
|
}
|
|
15839
16189
|
async function runConfiguredBenchmark(rootDir, config) {
|
|
15840
16190
|
if (config.benchmark?.enabled === false) {
|
|
@@ -15950,7 +16300,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
15950
16300
|
),
|
|
15951
16301
|
Promise.all(
|
|
15952
16302
|
clean.map(async (manifest) => {
|
|
15953
|
-
const cached = await readJsonFile(
|
|
16303
|
+
const cached = await readJsonFile(path23.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
15954
16304
|
if (cached) {
|
|
15955
16305
|
analysisProgress.tick(manifest.title);
|
|
15956
16306
|
return cached;
|
|
@@ -15978,22 +16328,22 @@ async function compileVault(rootDir, options = {}) {
|
|
|
15978
16328
|
}
|
|
15979
16329
|
const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
|
|
15980
16330
|
if (analysisSignature(enriched) !== analysisSignature(analysis)) {
|
|
15981
|
-
await writeJsonFile(
|
|
16331
|
+
await writeJsonFile(path23.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
|
|
15982
16332
|
}
|
|
15983
16333
|
return enriched;
|
|
15984
16334
|
})
|
|
15985
16335
|
);
|
|
15986
16336
|
await Promise.all([
|
|
15987
|
-
ensureDir(
|
|
15988
|
-
ensureDir(
|
|
15989
|
-
ensureDir(
|
|
15990
|
-
ensureDir(
|
|
15991
|
-
ensureDir(
|
|
15992
|
-
ensureDir(
|
|
15993
|
-
ensureDir(
|
|
15994
|
-
ensureDir(
|
|
15995
|
-
ensureDir(
|
|
15996
|
-
ensureDir(
|
|
16337
|
+
ensureDir(path23.join(paths.wikiDir, "sources")),
|
|
16338
|
+
ensureDir(path23.join(paths.wikiDir, "code")),
|
|
16339
|
+
ensureDir(path23.join(paths.wikiDir, "concepts")),
|
|
16340
|
+
ensureDir(path23.join(paths.wikiDir, "entities")),
|
|
16341
|
+
ensureDir(path23.join(paths.wikiDir, "outputs")),
|
|
16342
|
+
ensureDir(path23.join(paths.wikiDir, "projects")),
|
|
16343
|
+
ensureDir(path23.join(paths.wikiDir, "insights")),
|
|
16344
|
+
ensureDir(path23.join(paths.wikiDir, "candidates")),
|
|
16345
|
+
ensureDir(path23.join(paths.wikiDir, "candidates", "concepts")),
|
|
16346
|
+
ensureDir(path23.join(paths.wikiDir, "candidates", "entities"))
|
|
15997
16347
|
]);
|
|
15998
16348
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
15999
16349
|
schemas,
|
|
@@ -16140,7 +16490,7 @@ async function queryVault(rootDir, options) {
|
|
|
16140
16490
|
assetFiles: staged.assetFiles
|
|
16141
16491
|
}
|
|
16142
16492
|
]);
|
|
16143
|
-
stagedPath =
|
|
16493
|
+
stagedPath = path23.join(approval.approvalDir, "wiki", staged.page.path);
|
|
16144
16494
|
savedPageId = staged.page.id;
|
|
16145
16495
|
approvalId = approval.approvalId;
|
|
16146
16496
|
approvalDir = approval.approvalDir;
|
|
@@ -16396,9 +16746,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
16396
16746
|
approvalId = approval.approvalId;
|
|
16397
16747
|
approvalDir = approval.approvalDir;
|
|
16398
16748
|
stepResults.forEach((result, index) => {
|
|
16399
|
-
result.stagedPath =
|
|
16749
|
+
result.stagedPath = path23.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
16400
16750
|
});
|
|
16401
|
-
stagedHubPath =
|
|
16751
|
+
stagedHubPath = path23.join(approval.approvalDir, "wiki", hubPage.path);
|
|
16402
16752
|
} else {
|
|
16403
16753
|
await refreshVaultAfterOutputSave(rootDir);
|
|
16404
16754
|
}
|
|
@@ -16485,11 +16835,11 @@ async function benchmarkVault(rootDir, options = {}) {
|
|
|
16485
16835
|
}
|
|
16486
16836
|
}
|
|
16487
16837
|
for (const page of graph.pages) {
|
|
16488
|
-
const absolutePath =
|
|
16838
|
+
const absolutePath = path23.join(paths.wikiDir, page.path);
|
|
16489
16839
|
if (!await fileExists(absolutePath)) {
|
|
16490
16840
|
continue;
|
|
16491
16841
|
}
|
|
16492
|
-
const parsed = matter9(await
|
|
16842
|
+
const parsed = matter9(await fs19.readFile(absolutePath, "utf8"));
|
|
16493
16843
|
pageContentsById.set(page.id, parsed.content);
|
|
16494
16844
|
}
|
|
16495
16845
|
const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
|
|
@@ -16544,7 +16894,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
|
|
|
16544
16894
|
}
|
|
16545
16895
|
async function readGraphReport(rootDir) {
|
|
16546
16896
|
const { paths } = await loadVaultConfig(rootDir);
|
|
16547
|
-
return readJsonFile(
|
|
16897
|
+
return readJsonFile(path23.join(paths.wikiDir, "graph", "report.json"));
|
|
16548
16898
|
}
|
|
16549
16899
|
async function listGodNodes(rootDir, limit = 10) {
|
|
16550
16900
|
const graph = await ensureCompiledGraph(rootDir);
|
|
@@ -16557,15 +16907,15 @@ async function listPages(rootDir) {
|
|
|
16557
16907
|
}
|
|
16558
16908
|
async function readPage(rootDir, relativePath) {
|
|
16559
16909
|
const { paths } = await loadVaultConfig(rootDir);
|
|
16560
|
-
const absolutePath =
|
|
16910
|
+
const absolutePath = path23.resolve(paths.wikiDir, relativePath);
|
|
16561
16911
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
16562
16912
|
return null;
|
|
16563
16913
|
}
|
|
16564
|
-
const raw = await
|
|
16914
|
+
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
16565
16915
|
const parsed = matter9(raw);
|
|
16566
16916
|
return {
|
|
16567
16917
|
path: relativePath,
|
|
16568
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
16918
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path23.basename(relativePath, path23.extname(relativePath)),
|
|
16569
16919
|
frontmatter: parsed.data,
|
|
16570
16920
|
content: parsed.content
|
|
16571
16921
|
};
|
|
@@ -16601,7 +16951,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
16601
16951
|
severity: "warning",
|
|
16602
16952
|
code: "stale_page",
|
|
16603
16953
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
16604
|
-
pagePath:
|
|
16954
|
+
pagePath: path23.join(paths.wikiDir, page.path),
|
|
16605
16955
|
relatedPageIds: [page.id]
|
|
16606
16956
|
});
|
|
16607
16957
|
}
|
|
@@ -16614,7 +16964,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
16614
16964
|
severity: "warning",
|
|
16615
16965
|
code: "stale_page",
|
|
16616
16966
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
16617
|
-
pagePath:
|
|
16967
|
+
pagePath: path23.join(paths.wikiDir, page.path),
|
|
16618
16968
|
relatedSourceIds: [sourceId],
|
|
16619
16969
|
relatedPageIds: [page.id]
|
|
16620
16970
|
});
|
|
@@ -16625,13 +16975,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
16625
16975
|
severity: "info",
|
|
16626
16976
|
code: "orphan_page",
|
|
16627
16977
|
message: `Page ${page.title} has no backlinks.`,
|
|
16628
|
-
pagePath:
|
|
16978
|
+
pagePath: path23.join(paths.wikiDir, page.path),
|
|
16629
16979
|
relatedPageIds: [page.id]
|
|
16630
16980
|
});
|
|
16631
16981
|
}
|
|
16632
|
-
const absolutePath =
|
|
16982
|
+
const absolutePath = path23.join(paths.wikiDir, page.path);
|
|
16633
16983
|
if (await fileExists(absolutePath)) {
|
|
16634
|
-
const content = await
|
|
16984
|
+
const content = await fs19.readFile(absolutePath, "utf8");
|
|
16635
16985
|
if (content.includes("## Claims")) {
|
|
16636
16986
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
16637
16987
|
if (uncited.length) {
|
|
@@ -16748,7 +17098,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
16748
17098
|
}
|
|
16749
17099
|
|
|
16750
17100
|
// src/mcp.ts
|
|
16751
|
-
var SERVER_VERSION = "0.
|
|
17101
|
+
var SERVER_VERSION = "0.6.0";
|
|
16752
17102
|
async function createMcpServer(rootDir) {
|
|
16753
17103
|
const server = new McpServer({
|
|
16754
17104
|
name: "swarmvault",
|
|
@@ -17019,7 +17369,7 @@ async function createMcpServer(rootDir) {
|
|
|
17019
17369
|
},
|
|
17020
17370
|
async () => {
|
|
17021
17371
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17022
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
17372
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path24.relative(paths.sessionsDir, filePath))).sort();
|
|
17023
17373
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
17024
17374
|
}
|
|
17025
17375
|
);
|
|
@@ -17052,8 +17402,8 @@ async function createMcpServer(rootDir) {
|
|
|
17052
17402
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
17053
17403
|
}
|
|
17054
17404
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17055
|
-
const absolutePath =
|
|
17056
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
17405
|
+
const absolutePath = path24.resolve(paths.wikiDir, relativePath);
|
|
17406
|
+
return asTextResource(`swarmvault://pages/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
|
|
17057
17407
|
}
|
|
17058
17408
|
);
|
|
17059
17409
|
server.registerResource(
|
|
@@ -17061,11 +17411,11 @@ async function createMcpServer(rootDir) {
|
|
|
17061
17411
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
17062
17412
|
list: async () => {
|
|
17063
17413
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17064
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
17414
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path24.relative(paths.sessionsDir, filePath))).sort();
|
|
17065
17415
|
return {
|
|
17066
17416
|
resources: files.map((relativePath) => ({
|
|
17067
17417
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
17068
|
-
name:
|
|
17418
|
+
name: path24.basename(relativePath, ".md"),
|
|
17069
17419
|
title: relativePath,
|
|
17070
17420
|
description: "SwarmVault session artifact",
|
|
17071
17421
|
mimeType: "text/markdown"
|
|
@@ -17082,11 +17432,11 @@ async function createMcpServer(rootDir) {
|
|
|
17082
17432
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17083
17433
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
17084
17434
|
const relativePath = decodeURIComponent(encodedPath);
|
|
17085
|
-
const absolutePath =
|
|
17435
|
+
const absolutePath = path24.resolve(paths.sessionsDir, relativePath);
|
|
17086
17436
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
17087
17437
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
17088
17438
|
}
|
|
17089
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
17439
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
|
|
17090
17440
|
}
|
|
17091
17441
|
);
|
|
17092
17442
|
return server;
|
|
@@ -17134,13 +17484,13 @@ function asTextResource(uri, text) {
|
|
|
17134
17484
|
}
|
|
17135
17485
|
|
|
17136
17486
|
// src/schedule.ts
|
|
17137
|
-
import
|
|
17138
|
-
import
|
|
17487
|
+
import fs21 from "fs/promises";
|
|
17488
|
+
import path25 from "path";
|
|
17139
17489
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
17140
|
-
return
|
|
17490
|
+
return path25.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
17141
17491
|
}
|
|
17142
17492
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
17143
|
-
return
|
|
17493
|
+
return path25.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
17144
17494
|
}
|
|
17145
17495
|
function parseEveryDuration(value) {
|
|
17146
17496
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -17243,13 +17593,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
17243
17593
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17244
17594
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
17245
17595
|
await ensureDir(paths.schedulesDir);
|
|
17246
|
-
const handle = await
|
|
17596
|
+
const handle = await fs21.open(leasePath, "wx");
|
|
17247
17597
|
await handle.writeFile(`${process.pid}
|
|
17248
17598
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
17249
17599
|
`);
|
|
17250
17600
|
await handle.close();
|
|
17251
17601
|
return async () => {
|
|
17252
|
-
await
|
|
17602
|
+
await fs21.rm(leasePath, { force: true });
|
|
17253
17603
|
};
|
|
17254
17604
|
}
|
|
17255
17605
|
async function listSchedules(rootDir) {
|
|
@@ -17397,8 +17747,9 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
17397
17747
|
|
|
17398
17748
|
// src/sources.ts
|
|
17399
17749
|
import { spawn as spawn2 } from "child_process";
|
|
17400
|
-
import
|
|
17401
|
-
import
|
|
17750
|
+
import fs22 from "fs/promises";
|
|
17751
|
+
import path26 from "path";
|
|
17752
|
+
import matter10 from "gray-matter";
|
|
17402
17753
|
import { JSDOM as JSDOM3 } from "jsdom";
|
|
17403
17754
|
var DEFAULT_CRAWL_MAX_PAGES = 12;
|
|
17404
17755
|
var DEFAULT_CRAWL_MAX_DEPTH = 2;
|
|
@@ -17425,24 +17776,24 @@ function normalizeManagedStatus(value) {
|
|
|
17425
17776
|
return value === "missing" || value === "error" ? value : "ready";
|
|
17426
17777
|
}
|
|
17427
17778
|
function withinRoot2(rootPath, targetPath) {
|
|
17428
|
-
const relative =
|
|
17429
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
17779
|
+
const relative = path26.relative(rootPath, targetPath);
|
|
17780
|
+
return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
|
|
17430
17781
|
}
|
|
17431
17782
|
async function findNearestGitRoot3(startPath) {
|
|
17432
|
-
let current =
|
|
17783
|
+
let current = path26.resolve(startPath);
|
|
17433
17784
|
try {
|
|
17434
|
-
const stat = await
|
|
17785
|
+
const stat = await fs22.stat(current);
|
|
17435
17786
|
if (!stat.isDirectory()) {
|
|
17436
|
-
current =
|
|
17787
|
+
current = path26.dirname(current);
|
|
17437
17788
|
}
|
|
17438
17789
|
} catch {
|
|
17439
|
-
current =
|
|
17790
|
+
current = path26.dirname(current);
|
|
17440
17791
|
}
|
|
17441
17792
|
while (true) {
|
|
17442
|
-
if (await fileExists(
|
|
17793
|
+
if (await fileExists(path26.join(current, ".git"))) {
|
|
17443
17794
|
return current;
|
|
17444
17795
|
}
|
|
17445
|
-
const parent =
|
|
17796
|
+
const parent = path26.dirname(current);
|
|
17446
17797
|
if (parent === current) {
|
|
17447
17798
|
return null;
|
|
17448
17799
|
}
|
|
@@ -17516,7 +17867,7 @@ function isAllowedDocsCandidate(candidate, startUrl) {
|
|
|
17516
17867
|
if (candidate.origin !== startUrl.origin) {
|
|
17517
17868
|
return false;
|
|
17518
17869
|
}
|
|
17519
|
-
const extension =
|
|
17870
|
+
const extension = path26.extname(candidate.pathname).toLowerCase();
|
|
17520
17871
|
if (extension && extension !== ".html" && extension !== ".htm" && extension !== ".md") {
|
|
17521
17872
|
return false;
|
|
17522
17873
|
}
|
|
@@ -17605,14 +17956,14 @@ function matchesManagedSourceSpec(existing, input) {
|
|
|
17605
17956
|
return false;
|
|
17606
17957
|
}
|
|
17607
17958
|
if (input.kind === "directory" || input.kind === "file") {
|
|
17608
|
-
return
|
|
17959
|
+
return path26.resolve(existing.path ?? "") === path26.resolve(input.path);
|
|
17609
17960
|
}
|
|
17610
17961
|
return (existing.url ?? "") === input.url;
|
|
17611
17962
|
}
|
|
17612
17963
|
async function resolveManagedSourceInput(rootDir, input) {
|
|
17613
|
-
const absoluteInput =
|
|
17964
|
+
const absoluteInput = path26.resolve(rootDir, input);
|
|
17614
17965
|
if (!(input.startsWith("http://") || input.startsWith("https://"))) {
|
|
17615
|
-
const stat = await
|
|
17966
|
+
const stat = await fs22.stat(absoluteInput).catch(() => null);
|
|
17616
17967
|
if (!stat) {
|
|
17617
17968
|
throw new Error(`Source not found: ${input}`);
|
|
17618
17969
|
}
|
|
@@ -17620,7 +17971,7 @@ async function resolveManagedSourceInput(rootDir, input) {
|
|
|
17620
17971
|
return {
|
|
17621
17972
|
kind: "file",
|
|
17622
17973
|
path: absoluteInput,
|
|
17623
|
-
title:
|
|
17974
|
+
title: path26.basename(absoluteInput, path26.extname(absoluteInput)) || absoluteInput
|
|
17624
17975
|
};
|
|
17625
17976
|
}
|
|
17626
17977
|
if (!stat.isDirectory()) {
|
|
@@ -17632,7 +17983,7 @@ async function resolveManagedSourceInput(rootDir, input) {
|
|
|
17632
17983
|
kind: "directory",
|
|
17633
17984
|
path: absoluteInput,
|
|
17634
17985
|
repoRoot,
|
|
17635
|
-
title:
|
|
17986
|
+
title: path26.basename(absoluteInput) || absoluteInput
|
|
17636
17987
|
};
|
|
17637
17988
|
}
|
|
17638
17989
|
const github = normalizeGitHubRepoRootUrl(input);
|
|
@@ -17655,16 +18006,16 @@ async function resolveManagedSourceInput(rootDir, input) {
|
|
|
17655
18006
|
};
|
|
17656
18007
|
}
|
|
17657
18008
|
function directorySourceIdsFor(manifests, inputPath) {
|
|
17658
|
-
return manifests.filter((manifest) => manifest.originalPath && withinRoot2(
|
|
18009
|
+
return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path26.resolve(inputPath), path26.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
|
|
17659
18010
|
}
|
|
17660
18011
|
function fileSourceIdsFor(manifests, inputPath) {
|
|
17661
|
-
const absoluteInput =
|
|
17662
|
-
return manifests.filter((manifest) => manifest.originalPath &&
|
|
18012
|
+
const absoluteInput = path26.resolve(inputPath);
|
|
18013
|
+
return manifests.filter((manifest) => manifest.originalPath && path26.resolve(manifest.originalPath) === absoluteInput).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
|
|
17663
18014
|
}
|
|
17664
18015
|
async function syncDirectorySource(rootDir, inputPath, repoRoot) {
|
|
17665
18016
|
const manifestsBefore = await listManifests(rootDir);
|
|
17666
18017
|
const previousInScope = manifestsBefore.filter(
|
|
17667
|
-
(manifest) => manifest.originalPath && withinRoot2(
|
|
18018
|
+
(manifest) => manifest.originalPath && withinRoot2(path26.resolve(inputPath), path26.resolve(manifest.originalPath))
|
|
17668
18019
|
);
|
|
17669
18020
|
const result = await ingestDirectory(rootDir, inputPath, { repoRoot });
|
|
17670
18021
|
const removed = [];
|
|
@@ -17672,7 +18023,7 @@ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
|
|
|
17672
18023
|
if (!manifest.originalPath) {
|
|
17673
18024
|
continue;
|
|
17674
18025
|
}
|
|
17675
|
-
if (await fileExists(
|
|
18026
|
+
if (await fileExists(path26.resolve(manifest.originalPath))) {
|
|
17676
18027
|
continue;
|
|
17677
18028
|
}
|
|
17678
18029
|
const removedManifest = await removeManifestBySourceId(rootDir, manifest.sourceId);
|
|
@@ -17682,7 +18033,7 @@ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
|
|
|
17682
18033
|
}
|
|
17683
18034
|
const manifestsAfter = await listManifests(rootDir);
|
|
17684
18035
|
return {
|
|
17685
|
-
title:
|
|
18036
|
+
title: path26.basename(inputPath) || inputPath,
|
|
17686
18037
|
sourceIds: directorySourceIdsFor(manifestsAfter, inputPath),
|
|
17687
18038
|
counts: {
|
|
17688
18039
|
scannedCount: result.scannedCount,
|
|
@@ -17698,7 +18049,7 @@ async function syncFileSource(rootDir, inputPath) {
|
|
|
17698
18049
|
const result = await ingestInputDetailed(rootDir, inputPath);
|
|
17699
18050
|
const manifestsAfter = await listManifests(rootDir);
|
|
17700
18051
|
return {
|
|
17701
|
-
title:
|
|
18052
|
+
title: path26.basename(inputPath, path26.extname(inputPath)) || inputPath,
|
|
17702
18053
|
sourceIds: fileSourceIdsFor(manifestsAfter, inputPath),
|
|
17703
18054
|
counts: {
|
|
17704
18055
|
scannedCount: result.scannedCount,
|
|
@@ -17732,8 +18083,8 @@ async function runGitCommand(cwd, args) {
|
|
|
17732
18083
|
}
|
|
17733
18084
|
async function syncGitHubRepoSource(rootDir, entry) {
|
|
17734
18085
|
const workingDir = await managedSourceWorkingDir(rootDir, entry.id);
|
|
17735
|
-
const checkoutDir =
|
|
17736
|
-
await
|
|
18086
|
+
const checkoutDir = path26.join(workingDir, "checkout");
|
|
18087
|
+
await fs22.rm(checkoutDir, { recursive: true, force: true });
|
|
17737
18088
|
await ensureDir(workingDir);
|
|
17738
18089
|
if (!entry.url) {
|
|
17739
18090
|
throw new Error(`Managed source ${entry.id} is missing its repository URL.`);
|
|
@@ -17860,7 +18211,7 @@ function scopedNodeIds(graph, sourceIds) {
|
|
|
17860
18211
|
async function loadSourceAnalyses(rootDir, sourceIds) {
|
|
17861
18212
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17862
18213
|
const analyses = await Promise.all(
|
|
17863
|
-
sourceIds.map(async (sourceId) => await readJsonFile(
|
|
18214
|
+
sourceIds.map(async (sourceId) => await readJsonFile(path26.join(paths.analysesDir, `${sourceId}.json`)))
|
|
17864
18215
|
);
|
|
17865
18216
|
return analyses.filter((analysis) => Boolean(analysis?.sourceId));
|
|
17866
18217
|
}
|
|
@@ -17927,7 +18278,7 @@ function renderDeterministicSourceBrief(input) {
|
|
|
17927
18278
|
""
|
|
17928
18279
|
].join("\n");
|
|
17929
18280
|
}
|
|
17930
|
-
async function
|
|
18281
|
+
async function generateSourceBriefMarkdownForScope(rootDir, source) {
|
|
17931
18282
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17932
18283
|
const graph = await readJsonFile(paths.graphPath);
|
|
17933
18284
|
if (!graph) {
|
|
@@ -17964,7 +18315,7 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
|
|
|
17964
18315
|
),
|
|
17965
18316
|
prompt: [
|
|
17966
18317
|
`Source title: ${source.title}`,
|
|
17967
|
-
`Source kind: ${source.kind}`,
|
|
18318
|
+
`Source kind: ${source.kind ?? "source"}`,
|
|
17968
18319
|
`Tracked source ids: ${source.sourceIds.join(", ") || "none"}`,
|
|
17969
18320
|
"",
|
|
17970
18321
|
"Pages:",
|
|
@@ -17982,12 +18333,12 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
|
|
|
17982
18333
|
return fallback;
|
|
17983
18334
|
}
|
|
17984
18335
|
}
|
|
17985
|
-
async function
|
|
18336
|
+
async function writeSourceBriefForScope(rootDir, source) {
|
|
17986
18337
|
if (!source.sourceIds.length) {
|
|
17987
18338
|
return null;
|
|
17988
18339
|
}
|
|
17989
18340
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17990
|
-
const markdown = await
|
|
18341
|
+
const markdown = await generateSourceBriefMarkdownForScope(rootDir, source);
|
|
17991
18342
|
if (!markdown) {
|
|
17992
18343
|
return null;
|
|
17993
18344
|
}
|
|
@@ -18020,11 +18371,14 @@ async function writeSourceBrief(rootDir, source) {
|
|
|
18020
18371
|
confidence: 0.82
|
|
18021
18372
|
}
|
|
18022
18373
|
});
|
|
18023
|
-
const absolutePath =
|
|
18024
|
-
await ensureDir(
|
|
18025
|
-
await
|
|
18374
|
+
const absolutePath = path26.join(paths.wikiDir, output.page.path);
|
|
18375
|
+
await ensureDir(path26.dirname(absolutePath));
|
|
18376
|
+
await fs22.writeFile(absolutePath, output.content, "utf8");
|
|
18026
18377
|
return absolutePath;
|
|
18027
18378
|
}
|
|
18379
|
+
async function writeSourceBrief(rootDir, source) {
|
|
18380
|
+
return await writeSourceBriefForScope(rootDir, scopeFromManagedSource(source));
|
|
18381
|
+
}
|
|
18028
18382
|
async function generateBriefsForSources(rootDir, sources) {
|
|
18029
18383
|
const briefPaths = /* @__PURE__ */ new Map();
|
|
18030
18384
|
for (const source of sources) {
|
|
@@ -18038,6 +18392,59 @@ async function generateBriefsForSources(rootDir, sources) {
|
|
|
18038
18392
|
}
|
|
18039
18393
|
return briefPaths;
|
|
18040
18394
|
}
|
|
18395
|
+
var GUIDED_SESSION_QUESTIONS = [
|
|
18396
|
+
{
|
|
18397
|
+
id: "importance",
|
|
18398
|
+
prompt: "What matters most from this source for your wiki right now?"
|
|
18399
|
+
},
|
|
18400
|
+
{
|
|
18401
|
+
id: "exclude",
|
|
18402
|
+
prompt: "What should stay provisional, be ignored, or be kept out for now?"
|
|
18403
|
+
},
|
|
18404
|
+
{
|
|
18405
|
+
id: "targets",
|
|
18406
|
+
prompt: "Which canonical pages or topics should this source update?"
|
|
18407
|
+
},
|
|
18408
|
+
{
|
|
18409
|
+
id: "conflicts",
|
|
18410
|
+
prompt: "What feels new, reinforcing, or conflicting compared with what you already believe?"
|
|
18411
|
+
},
|
|
18412
|
+
{
|
|
18413
|
+
id: "followups",
|
|
18414
|
+
prompt: "What follow-up questions or next sources should stay open?"
|
|
18415
|
+
}
|
|
18416
|
+
];
|
|
18417
|
+
function defaultGuidedSessionQuestions() {
|
|
18418
|
+
return GUIDED_SESSION_QUESTIONS.map((question) => ({ ...question }));
|
|
18419
|
+
}
|
|
18420
|
+
function normalizeGuidedAnswerValue(value) {
|
|
18421
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
18422
|
+
}
|
|
18423
|
+
function normalizeGuidedAnswers(input) {
|
|
18424
|
+
if (!input) {
|
|
18425
|
+
return {};
|
|
18426
|
+
}
|
|
18427
|
+
if (Array.isArray(input)) {
|
|
18428
|
+
return Object.fromEntries(
|
|
18429
|
+
GUIDED_SESSION_QUESTIONS.map((question, index) => [question.id, normalizeGuidedAnswerValue(input[index])]).filter(
|
|
18430
|
+
(entry) => Boolean(entry[1])
|
|
18431
|
+
)
|
|
18432
|
+
);
|
|
18433
|
+
}
|
|
18434
|
+
return Object.fromEntries(
|
|
18435
|
+
Object.entries(input).map(([key, value]) => [key, normalizeGuidedAnswerValue(value)]).filter((entry) => Boolean(entry[1]))
|
|
18436
|
+
);
|
|
18437
|
+
}
|
|
18438
|
+
function mergeGuidedSessionQuestions(questions, answers) {
|
|
18439
|
+
const normalizedAnswers = normalizeGuidedAnswers(answers);
|
|
18440
|
+
return questions.map((question) => ({
|
|
18441
|
+
...question,
|
|
18442
|
+
answer: normalizedAnswers[question.id] ?? question.answer
|
|
18443
|
+
}));
|
|
18444
|
+
}
|
|
18445
|
+
function answeredGuidedSessionQuestions(questions) {
|
|
18446
|
+
return questions.filter((question) => typeof question.answer === "string" && question.answer.trim().length > 0);
|
|
18447
|
+
}
|
|
18041
18448
|
function renderDeterministicSourceReview(input) {
|
|
18042
18449
|
const canonicalPages = input.sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 10);
|
|
18043
18450
|
const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 8);
|
|
@@ -18137,7 +18544,7 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
|
|
|
18137
18544
|
return fallback;
|
|
18138
18545
|
}
|
|
18139
18546
|
}
|
|
18140
|
-
async function
|
|
18547
|
+
async function buildSourceReviewStagedPage(rootDir, scope) {
|
|
18141
18548
|
const { paths } = await loadVaultConfig(rootDir);
|
|
18142
18549
|
const markdown = await generateSourceReviewMarkdown(rootDir, scope);
|
|
18143
18550
|
if (!markdown) {
|
|
@@ -18172,44 +18579,671 @@ async function stageSourceReviewForScope(rootDir, scope) {
|
|
|
18172
18579
|
confidence: 0.79
|
|
18173
18580
|
}
|
|
18174
18581
|
});
|
|
18175
|
-
|
|
18176
|
-
|
|
18177
|
-
|
|
18178
|
-
|
|
18179
|
-
|
|
18180
|
-
|
|
18181
|
-
|
|
18182
|
-
|
|
18183
|
-
|
|
18582
|
+
return { page: output.page, content: output.content };
|
|
18583
|
+
}
|
|
18584
|
+
function classifySourceGuidePageBuckets(sourcePages, scopeSourceIds) {
|
|
18585
|
+
const scopeSet = new Set(scopeSourceIds);
|
|
18586
|
+
const canonicalPages = sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 12);
|
|
18587
|
+
const newPages = canonicalPages.filter((page) => page.sourceIds.every((sourceId) => scopeSet.has(sourceId))).slice(0, 6);
|
|
18588
|
+
const reinforcingPages = canonicalPages.filter((page) => page.sourceIds.some((sourceId) => !scopeSet.has(sourceId))).slice(0, 6);
|
|
18589
|
+
return { canonicalPages, newPages, reinforcingPages };
|
|
18590
|
+
}
|
|
18591
|
+
function findContradictionsForScope(scope, report) {
|
|
18592
|
+
return report?.contradictions.filter(
|
|
18593
|
+
(contradiction) => scope.sourceIds.includes(contradiction.sourceIdA) || scope.sourceIds.includes(contradiction.sourceIdB)
|
|
18594
|
+
) ?? [];
|
|
18184
18595
|
}
|
|
18185
|
-
function
|
|
18186
|
-
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
|
|
18596
|
+
function selectGuidedTargetPages(scope, sourcePages, questions) {
|
|
18597
|
+
const { canonicalPages } = classifySourceGuidePageBuckets(sourcePages, scope.sourceIds);
|
|
18598
|
+
if (!canonicalPages.length) {
|
|
18599
|
+
return [];
|
|
18600
|
+
}
|
|
18601
|
+
const desiredTargets = normalizeWhitespace(
|
|
18602
|
+
questions.find((question) => question.id === "targets")?.answer ?? questions.find((question) => question.id === "importance")?.answer ?? ""
|
|
18603
|
+
).toLowerCase();
|
|
18604
|
+
const matchedTargets = desiredTargets ? canonicalPages.filter((page) => {
|
|
18605
|
+
const title = page.title.toLowerCase();
|
|
18606
|
+
const relative = page.path.replace(/\.md$/, "").toLowerCase();
|
|
18607
|
+
return desiredTargets.includes(title) || desiredTargets.includes(relative) || title.includes(desiredTargets);
|
|
18608
|
+
}) : [];
|
|
18609
|
+
return (matchedTargets.length ? matchedTargets : canonicalPages).slice(0, 6);
|
|
18191
18610
|
}
|
|
18192
|
-
|
|
18193
|
-
|
|
18611
|
+
function insightRelativePathForTarget(page, scope) {
|
|
18612
|
+
const basename = path26.basename(page.path);
|
|
18613
|
+
if (page.kind === "concept") {
|
|
18614
|
+
return `insights/concepts/${basename}`;
|
|
18615
|
+
}
|
|
18616
|
+
if (page.kind === "entity") {
|
|
18617
|
+
return `insights/entities/${basename}`;
|
|
18618
|
+
}
|
|
18619
|
+
if (page.kind === "source") {
|
|
18620
|
+
return `insights/sources/${slugify(page.title || scope.title)}.md`;
|
|
18621
|
+
}
|
|
18622
|
+
return `insights/topics/${slugify(page.title || scope.title)}.md`;
|
|
18194
18623
|
}
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
if (managedSource) {
|
|
18199
|
-
if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
|
|
18200
|
-
await compileVault(rootDir, {});
|
|
18201
|
-
}
|
|
18202
|
-
return await stageSourceReviewForScope(rootDir, scopeFromManagedSource(managedSource));
|
|
18624
|
+
function insightTitleForTarget(page, scope) {
|
|
18625
|
+
if (page.kind === "concept" || page.kind === "entity") {
|
|
18626
|
+
return page.title;
|
|
18203
18627
|
}
|
|
18204
|
-
|
|
18205
|
-
|
|
18206
|
-
throw new Error(`Managed source or source id not found: ${id}`);
|
|
18628
|
+
if (page.kind === "source") {
|
|
18629
|
+
return `Source Notes: ${page.title}`;
|
|
18207
18630
|
}
|
|
18208
|
-
return
|
|
18209
|
-
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18631
|
+
return `${scope.title} Notes`;
|
|
18632
|
+
}
|
|
18633
|
+
function insightTagsForTarget(page) {
|
|
18634
|
+
return uniqueStrings4(["insight", "guided-session", `guided/${page?.kind ?? "topic"}`]);
|
|
18635
|
+
}
|
|
18636
|
+
function guidedUpdateMarker(scopeId) {
|
|
18637
|
+
return {
|
|
18638
|
+
start: `<!-- swarmvault-guided-source:${scopeId}:start -->`,
|
|
18639
|
+
end: `<!-- swarmvault-guided-source:${scopeId}:end -->`
|
|
18640
|
+
};
|
|
18641
|
+
}
|
|
18642
|
+
function replaceMarkedSection(content, scopeId, replacement) {
|
|
18643
|
+
const marker = guidedUpdateMarker(scopeId);
|
|
18644
|
+
const block = `${marker.start}
|
|
18645
|
+
${replacement.trim()}
|
|
18646
|
+
${marker.end}`;
|
|
18647
|
+
const startIndex = content.indexOf(marker.start);
|
|
18648
|
+
const endIndex = content.indexOf(marker.end);
|
|
18649
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
18650
|
+
return `${content.slice(0, startIndex).trimEnd()}
|
|
18651
|
+
|
|
18652
|
+
${block}
|
|
18653
|
+
`;
|
|
18654
|
+
}
|
|
18655
|
+
return `${content.trimEnd()}
|
|
18656
|
+
|
|
18657
|
+
${block}
|
|
18658
|
+
`;
|
|
18659
|
+
}
|
|
18660
|
+
function renderDeterministicSourceGuide(input) {
|
|
18661
|
+
const { canonicalPages, newPages, reinforcingPages } = classifySourceGuidePageBuckets(input.sourcePages, input.scope.sourceIds);
|
|
18662
|
+
const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 6);
|
|
18663
|
+
const takeaways = uniqueStrings4(
|
|
18664
|
+
input.analyses.flatMap((analysis) => [
|
|
18665
|
+
analysis.summary,
|
|
18666
|
+
...analysis.concepts.map((concept) => concept.description),
|
|
18667
|
+
...analysis.entities.map((entity) => entity.description)
|
|
18668
|
+
]).filter(Boolean).map((value) => normalizeWhitespace(value))
|
|
18669
|
+
).slice(0, 7).map((value) => truncate(value, 180));
|
|
18670
|
+
const questions = uniqueStrings4(input.analyses.flatMap((analysis) => analysis.questions)).slice(0, 6);
|
|
18671
|
+
const contradictions = input.report?.contradictions.filter(
|
|
18672
|
+
(contradiction) => input.scope.sourceIds.includes(contradiction.sourceIdA) || input.scope.sourceIds.includes(contradiction.sourceIdB)
|
|
18673
|
+
) ?? [];
|
|
18674
|
+
return [
|
|
18675
|
+
`# Source Guide: ${input.scope.title}`,
|
|
18676
|
+
"",
|
|
18677
|
+
"## What This Source Is",
|
|
18678
|
+
"",
|
|
18679
|
+
takeaways.length ? takeaways[0] : `${input.scope.title} has been compiled into the vault and is ready for guided review.`,
|
|
18680
|
+
"",
|
|
18681
|
+
"## Key Takeaways",
|
|
18682
|
+
"",
|
|
18683
|
+
...takeaways.length ? takeaways.map((takeaway) => `- ${takeaway}`) : ["- No takeaways are available until the source is compiled."],
|
|
18684
|
+
"",
|
|
18685
|
+
"## Proposed Canonical Pages To Update",
|
|
18686
|
+
"",
|
|
18687
|
+
...canonicalPages.length ? canonicalPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No likely canonical pages were identified yet."],
|
|
18688
|
+
"",
|
|
18689
|
+
"## New, Reinforcing, And Conflicting Claims",
|
|
18690
|
+
"",
|
|
18691
|
+
...newPages.length ? ["New or source-local pages:", ...newPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`), ""] : [],
|
|
18692
|
+
...reinforcingPages.length ? ["Reinforcing existing pages:", ...reinforcingPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`), ""] : [],
|
|
18693
|
+
...contradictions.length ? ["Conflicts to judge:", ...contradictions.map((contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB}`), ""] : ["Conflicts to judge:", "- No contradictions are currently flagged for this source scope.", ""],
|
|
18694
|
+
"## What Should Probably Stay Out For Now",
|
|
18695
|
+
"",
|
|
18696
|
+
...modulePages.length ? ["- Avoid promoting narrow implementation details unless they matter to your thesis or recurring questions."] : ["- Avoid promoting incidental details that are not yet supported by multiple sources or clear research goals."],
|
|
18697
|
+
...contradictions.length ? ["- Keep contested claims provisional until you review the conflicting evidence side by side."] : [],
|
|
18698
|
+
"",
|
|
18699
|
+
"## Needs Human Judgment",
|
|
18700
|
+
"",
|
|
18701
|
+
...questions.length ? questions.map((question) => `- ${question}`) : ["- Decide which proposed canonical pages deserve durable summary updates."],
|
|
18702
|
+
"",
|
|
18703
|
+
"## Suggested Follow-up Questions",
|
|
18704
|
+
"",
|
|
18705
|
+
...questions.length ? questions.map((question) => `- ${question}`) : ["- What changed in your understanding after reading this source?"],
|
|
18706
|
+
""
|
|
18707
|
+
].join("\n");
|
|
18708
|
+
}
|
|
18709
|
+
async function generateSourceGuideMarkdown(rootDir, scope) {
|
|
18710
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
18711
|
+
let graph = await readJsonFile(paths.graphPath);
|
|
18712
|
+
if (!graph) {
|
|
18713
|
+
await compileVault(rootDir, {});
|
|
18714
|
+
graph = await readJsonFile(paths.graphPath);
|
|
18715
|
+
}
|
|
18716
|
+
if (!graph) {
|
|
18717
|
+
return null;
|
|
18718
|
+
}
|
|
18719
|
+
const sourcePages = scopedSourcePages(graph, scope.sourceIds);
|
|
18720
|
+
const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
|
|
18721
|
+
const report = await readGraphReport(rootDir);
|
|
18722
|
+
const fallback = renderDeterministicSourceGuide({
|
|
18723
|
+
scope,
|
|
18724
|
+
sourcePages,
|
|
18725
|
+
analyses,
|
|
18726
|
+
report
|
|
18727
|
+
});
|
|
18728
|
+
const provider = await getProviderForTask(rootDir, "queryProvider");
|
|
18729
|
+
if (provider.type === "heuristic") {
|
|
18730
|
+
return fallback;
|
|
18731
|
+
}
|
|
18732
|
+
try {
|
|
18733
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
18734
|
+
const { canonicalPages, newPages, reinforcingPages } = classifySourceGuidePageBuckets(sourcePages, scope.sourceIds);
|
|
18735
|
+
const pageContext = sourcePages.slice(0, 12).map((page) => `- ${page.title} (${page.kind}) -> ${page.path}`).join("\n");
|
|
18736
|
+
const analysisContext = analyses.slice(0, 8).map(
|
|
18737
|
+
(analysis) => `# ${analysis.title}
|
|
18738
|
+
Summary: ${analysis.summary}
|
|
18739
|
+
Questions: ${analysis.questions.join(" | ") || "none"}
|
|
18740
|
+
Concepts: ${analysis.concepts.map((concept) => concept.name).join(", ") || "none"}
|
|
18741
|
+
Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}`
|
|
18742
|
+
).join("\n\n---\n\n");
|
|
18743
|
+
const response = await provider.generateText({
|
|
18744
|
+
system: buildSchemaPrompt(
|
|
18745
|
+
schemas.effective.global,
|
|
18746
|
+
"Write a concise markdown source guide with sections: What This Source Is, Key Takeaways, Proposed Canonical Pages To Update, New Reinforcing And Conflicting Claims, What Should Probably Stay Out For Now, Needs Human Judgment, Suggested Follow-up Questions. Focus on helping a human integrate one source into an evolving research wiki."
|
|
18747
|
+
),
|
|
18748
|
+
prompt: [
|
|
18749
|
+
`Source scope: ${scope.title}`,
|
|
18750
|
+
`Scope id: ${scope.id}`,
|
|
18751
|
+
`Tracked source ids: ${scope.sourceIds.join(", ") || "none"}`,
|
|
18752
|
+
`Current brief path: ${scope.briefPath ?? "none"}`,
|
|
18753
|
+
"",
|
|
18754
|
+
"Likely canonical pages:",
|
|
18755
|
+
canonicalPages.length ? canonicalPages.map((page) => `- ${page.title} -> ${page.path}`).join("\n") : "- none",
|
|
18756
|
+
"",
|
|
18757
|
+
"Likely source-local pages:",
|
|
18758
|
+
newPages.length ? newPages.map((page) => `- ${page.title} -> ${page.path}`).join("\n") : "- none",
|
|
18759
|
+
"",
|
|
18760
|
+
"Likely reinforcing pages:",
|
|
18761
|
+
reinforcingPages.length ? reinforcingPages.map((page) => `- ${page.title} -> ${page.path}`).join("\n") : "- none",
|
|
18762
|
+
"",
|
|
18763
|
+
"Pages:",
|
|
18764
|
+
pageContext || "- none",
|
|
18765
|
+
"",
|
|
18766
|
+
"Analyses:",
|
|
18767
|
+
analysisContext || "No analysis context available.",
|
|
18768
|
+
"",
|
|
18769
|
+
"Deterministic fallback draft:",
|
|
18770
|
+
fallback
|
|
18771
|
+
].join("\n")
|
|
18772
|
+
});
|
|
18773
|
+
return response.text?.trim() ? response.text.trim() : fallback;
|
|
18774
|
+
} catch {
|
|
18775
|
+
return fallback;
|
|
18776
|
+
}
|
|
18777
|
+
}
|
|
18778
|
+
async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
18779
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
18780
|
+
const markdown = await generateSourceGuideMarkdown(rootDir, scope);
|
|
18781
|
+
if (!markdown) {
|
|
18782
|
+
throw new Error(`Could not generate a source guide for ${scope.id}.`);
|
|
18783
|
+
}
|
|
18784
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
18785
|
+
const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
18786
|
+
const relatedPageIds = relatedPages.slice(0, 18).map((page) => page.id);
|
|
18787
|
+
const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
|
|
18788
|
+
const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
|
|
18789
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18790
|
+
const output = buildOutputPage({
|
|
18791
|
+
title: `Source Guide: ${scope.title}`,
|
|
18792
|
+
question: `Guide ${scope.title}`,
|
|
18793
|
+
answer: markdown,
|
|
18794
|
+
citations: scope.sourceIds,
|
|
18795
|
+
schemaHash: graph?.generatedAt ?? "",
|
|
18796
|
+
outputFormat: "report",
|
|
18797
|
+
relatedPageIds,
|
|
18798
|
+
relatedNodeIds,
|
|
18799
|
+
relatedSourceIds: scope.sourceIds,
|
|
18800
|
+
projectIds,
|
|
18801
|
+
extraTags: ["source-guide", "guided-ingest"],
|
|
18802
|
+
origin: "query",
|
|
18803
|
+
slug: `source-guides/${scope.id}`,
|
|
18804
|
+
metadata: {
|
|
18805
|
+
status: "draft",
|
|
18806
|
+
createdAt: now,
|
|
18807
|
+
updatedAt: now,
|
|
18808
|
+
compiledFrom: scope.sourceIds,
|
|
18809
|
+
managedBy: "system",
|
|
18810
|
+
confidence: 0.8
|
|
18811
|
+
}
|
|
18812
|
+
});
|
|
18813
|
+
return { page: output.page, content: output.content };
|
|
18814
|
+
}
|
|
18815
|
+
async function stageSourceReviewForScope(rootDir, scope) {
|
|
18816
|
+
const output = await buildSourceReviewStagedPage(rootDir, scope);
|
|
18817
|
+
const approval = await stageGeneratedOutputPages(rootDir, [{ page: output.page, content: output.content, label: "source-review" }], {
|
|
18818
|
+
bundleType: "source_review",
|
|
18819
|
+
title: `Source Review: ${scope.title}`
|
|
18820
|
+
});
|
|
18821
|
+
return {
|
|
18822
|
+
sourceId: scope.id,
|
|
18823
|
+
pageId: output.page.id,
|
|
18824
|
+
reviewPath: path26.join(approval.approvalDir, "wiki", output.page.path),
|
|
18825
|
+
staged: true,
|
|
18826
|
+
approvalId: approval.approvalId,
|
|
18827
|
+
approvalDir: approval.approvalDir
|
|
18828
|
+
};
|
|
18829
|
+
}
|
|
18830
|
+
function nextGuidedSourceSessionId(scope) {
|
|
18831
|
+
return `source-session-${slugify(scope.id)}-${sha256(`${scope.id}:${(/* @__PURE__ */ new Date()).toISOString()}`).slice(0, 8)}`;
|
|
18832
|
+
}
|
|
18833
|
+
function shouldReuseGuidedSourceSession(session) {
|
|
18834
|
+
return Boolean(session && session.status === "awaiting_input");
|
|
18835
|
+
}
|
|
18836
|
+
function questionAnswer(questions, id, fallback) {
|
|
18837
|
+
return normalizeGuidedAnswerValue(questions.find((question) => question.id === id)?.answer) ?? fallback;
|
|
18838
|
+
}
|
|
18839
|
+
async function prepareGuidedSourceSession(rootDir, scope, answers) {
|
|
18840
|
+
const existing = await findLatestGuidedSourceSessionByScope(rootDir, scope.id);
|
|
18841
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18842
|
+
const session = shouldReuseGuidedSourceSession(existing) ? {
|
|
18843
|
+
...existing,
|
|
18844
|
+
scopeTitle: scope.title,
|
|
18845
|
+
sourceIds: scope.sourceIds,
|
|
18846
|
+
kind: scope.kind,
|
|
18847
|
+
questions: mergeGuidedSessionQuestions(existing.questions, answers),
|
|
18848
|
+
updatedAt: now
|
|
18849
|
+
} : {
|
|
18850
|
+
sessionId: nextGuidedSourceSessionId(scope),
|
|
18851
|
+
scopeId: scope.id,
|
|
18852
|
+
scopeTitle: scope.title,
|
|
18853
|
+
sourceIds: scope.sourceIds,
|
|
18854
|
+
kind: scope.kind,
|
|
18855
|
+
status: "awaiting_input",
|
|
18856
|
+
createdAt: now,
|
|
18857
|
+
updatedAt: now,
|
|
18858
|
+
questions: mergeGuidedSessionQuestions(defaultGuidedSessionQuestions(), answers),
|
|
18859
|
+
briefPath: scope.briefPath,
|
|
18860
|
+
targetedPagePaths: [],
|
|
18861
|
+
stagedUpdatePaths: []
|
|
18862
|
+
};
|
|
18863
|
+
const statePath = await guidedSourceSessionStatePath(rootDir, session.sessionId);
|
|
18864
|
+
return { session, statePath };
|
|
18865
|
+
}
|
|
18866
|
+
async function buildSourceSessionSavedPage(rootDir, scope, session) {
|
|
18867
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
18868
|
+
let graph = await readJsonFile(paths.graphPath);
|
|
18869
|
+
if (!graph) {
|
|
18870
|
+
await compileVault(rootDir, {});
|
|
18871
|
+
graph = await readJsonFile(paths.graphPath);
|
|
18872
|
+
}
|
|
18873
|
+
const sourcePages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
18874
|
+
const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
|
|
18875
|
+
const report = await readGraphReport(rootDir);
|
|
18876
|
+
const contradictions = findContradictionsForScope(scope, report);
|
|
18877
|
+
const relatedPageIds = uniqueStrings4([
|
|
18878
|
+
...sourcePages.slice(0, 18).map((page) => page.id),
|
|
18879
|
+
...session.targetedPagePaths.map((relativePath) => {
|
|
18880
|
+
const page = graph?.pages.find((candidate) => candidate.path === relativePath);
|
|
18881
|
+
return page?.id ?? "";
|
|
18882
|
+
})
|
|
18883
|
+
]);
|
|
18884
|
+
const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
|
|
18885
|
+
const projectIds = uniqueStrings4(sourcePages.flatMap((page) => page.projectIds));
|
|
18886
|
+
const relativeBriefPath = session.briefPath && path26.isAbsolute(session.briefPath) ? path26.relative(paths.wikiDir, session.briefPath) : session.briefPath;
|
|
18887
|
+
const sessionMarkdown = [
|
|
18888
|
+
`# Guided Session: ${scope.title}`,
|
|
18889
|
+
"",
|
|
18890
|
+
`Status: \`${session.status}\``,
|
|
18891
|
+
`Session ID: \`${session.sessionId}\``,
|
|
18892
|
+
...session.approvalId ? [`Approval Bundle: \`${session.approvalId}\``] : [],
|
|
18893
|
+
...relativeBriefPath ? [`Brief: \`${relativeBriefPath}\``] : [],
|
|
18894
|
+
"",
|
|
18895
|
+
"## What This Source Is",
|
|
18896
|
+
"",
|
|
18897
|
+
...analyses.length ? analyses.slice(0, 6).map((analysis) => `- ${analysis.title}: ${analysis.summary}`) : ["- Awaiting compile context."],
|
|
18898
|
+
"",
|
|
18899
|
+
"## Guided Questions",
|
|
18900
|
+
"",
|
|
18901
|
+
...session.questions.flatMap((question) => [`### ${question.prompt}`, "", question.answer ?? "_Awaiting input._", ""]),
|
|
18902
|
+
"## Proposed Wiki Targets",
|
|
18903
|
+
"",
|
|
18904
|
+
...session.targetedPagePaths.length ? session.targetedPagePaths.map((targetPath) => `- [[${targetPath.replace(/\.md$/, "")}]]`) : ["- No canonical update targets selected yet."],
|
|
18905
|
+
"",
|
|
18906
|
+
"## Conflicts And Judgment Calls",
|
|
18907
|
+
"",
|
|
18908
|
+
...contradictions.length ? contradictions.map((contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB}`) : ["- No contradictions are currently flagged for this source scope."],
|
|
18909
|
+
"",
|
|
18910
|
+
"## Follow-up Questions",
|
|
18911
|
+
"",
|
|
18912
|
+
...(() => {
|
|
18913
|
+
const followups = questionAnswer(session.questions, "followups", "");
|
|
18914
|
+
if (followups) {
|
|
18915
|
+
return followups.split(/\n+/).map((line) => line.trim()).filter(Boolean).map((line) => `- ${line.replace(/^-+\s*/, "")}`);
|
|
18916
|
+
}
|
|
18917
|
+
const analysisQuestions = uniqueStrings4(analyses.flatMap((analysis) => analysis.questions)).slice(0, 6);
|
|
18918
|
+
return analysisQuestions.length ? analysisQuestions.map((question) => `- ${question}`) : ["- No follow-up questions recorded yet."];
|
|
18919
|
+
})(),
|
|
18920
|
+
"",
|
|
18921
|
+
"## Related Artifacts",
|
|
18922
|
+
"",
|
|
18923
|
+
`- [[outputs/source-briefs/${scope.id}|Source Brief]]`,
|
|
18924
|
+
`- [[outputs/source-reviews/${scope.id}|Source Review]]`,
|
|
18925
|
+
`- [[outputs/source-guides/${scope.id}|Source Guide]]`,
|
|
18926
|
+
""
|
|
18927
|
+
].join("\n");
|
|
18928
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18929
|
+
const output = buildOutputPage({
|
|
18930
|
+
title: `Guided Session: ${scope.title}`,
|
|
18931
|
+
question: `Guided Session ${scope.title}`,
|
|
18932
|
+
answer: sessionMarkdown,
|
|
18933
|
+
citations: scope.sourceIds,
|
|
18934
|
+
schemaHash: graph?.generatedAt ?? "",
|
|
18935
|
+
outputFormat: "report",
|
|
18936
|
+
relatedPageIds,
|
|
18937
|
+
relatedNodeIds,
|
|
18938
|
+
relatedSourceIds: scope.sourceIds,
|
|
18939
|
+
projectIds,
|
|
18940
|
+
extraTags: ["source-session", "guided-session"],
|
|
18941
|
+
origin: "query",
|
|
18942
|
+
slug: `source-sessions/${scope.id}`,
|
|
18943
|
+
metadata: {
|
|
18944
|
+
status: "active",
|
|
18945
|
+
createdAt: now,
|
|
18946
|
+
updatedAt: now,
|
|
18947
|
+
compiledFrom: scope.sourceIds,
|
|
18948
|
+
managedBy: "system",
|
|
18949
|
+
confidence: 0.81
|
|
18950
|
+
}
|
|
18951
|
+
});
|
|
18952
|
+
return { page: output.page, content: output.content };
|
|
18953
|
+
}
|
|
18954
|
+
async function persistSourceSessionPage(rootDir, scope, session) {
|
|
18955
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
18956
|
+
const output = await buildSourceSessionSavedPage(rootDir, scope, session);
|
|
18957
|
+
const absolutePath = path26.join(paths.wikiDir, output.page.path);
|
|
18958
|
+
await ensureDir(path26.dirname(absolutePath));
|
|
18959
|
+
await fs22.writeFile(absolutePath, output.content, "utf8");
|
|
18960
|
+
return { pageId: output.page.id, sessionPath: absolutePath };
|
|
18961
|
+
}
|
|
18962
|
+
async function buildGuidedInsightUpdatePages(rootDir, scope, session) {
|
|
18963
|
+
const { paths } = await loadVaultConfig(rootDir);
|
|
18964
|
+
let graph = await readJsonFile(paths.graphPath);
|
|
18965
|
+
if (!graph) {
|
|
18966
|
+
await compileVault(rootDir, {});
|
|
18967
|
+
graph = await readJsonFile(paths.graphPath);
|
|
18968
|
+
}
|
|
18969
|
+
if (!graph) {
|
|
18970
|
+
return [];
|
|
18971
|
+
}
|
|
18972
|
+
const sourcePages = scopedSourcePages(graph, scope.sourceIds);
|
|
18973
|
+
const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
|
|
18974
|
+
const report = await readGraphReport(rootDir);
|
|
18975
|
+
const contradictions = findContradictionsForScope(scope, report);
|
|
18976
|
+
const selectedTargets = selectGuidedTargetPages(scope, sourcePages, session.questions);
|
|
18977
|
+
const targetPages = selectedTargets.length ? selectedTargets : [null];
|
|
18978
|
+
return await Promise.all(
|
|
18979
|
+
targetPages.map(async (targetPage, index) => {
|
|
18980
|
+
const relativePath = targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
|
|
18981
|
+
const absolutePath = path26.join(paths.wikiDir, relativePath);
|
|
18982
|
+
const existingContent = await fileExists(absolutePath) ? await fs22.readFile(absolutePath, "utf8") : "";
|
|
18983
|
+
const parsed = existingContent ? matter10(existingContent) : { data: {}, content: "" };
|
|
18984
|
+
const existingData = parsed.data;
|
|
18985
|
+
const existingSourceIds = Array.isArray(existingData.source_ids) ? existingData.source_ids.filter((value) => typeof value === "string") : [];
|
|
18986
|
+
const existingProjectIds = Array.isArray(existingData.project_ids) ? existingData.project_ids.filter((value) => typeof value === "string") : [];
|
|
18987
|
+
const existingNodeIds = Array.isArray(existingData.node_ids) ? existingData.node_ids.filter((value) => typeof value === "string") : [];
|
|
18988
|
+
const existingBacklinks = Array.isArray(existingData.backlinks) ? existingData.backlinks.filter((value) => typeof value === "string") : [];
|
|
18989
|
+
const createdAt = typeof existingData.created_at === "string" && existingData.created_at.trim() ? existingData.created_at : (/* @__PURE__ */ new Date()).toISOString();
|
|
18990
|
+
const title = typeof existingData.title === "string" && existingData.title.trim() || (targetPage ? insightTitleForTarget(targetPage, scope) : `${scope.title} Notes`);
|
|
18991
|
+
const baseBody = parsed.content.trim() ? parsed.content.trim() : [`# ${title}`, "", "Human-curated insight page. Guided sessions stage replaceable update blocks here.", ""].join("\n");
|
|
18992
|
+
const importance = questionAnswer(
|
|
18993
|
+
session.questions,
|
|
18994
|
+
"importance",
|
|
18995
|
+
"Capture the most important new ideas from this source before treating them as canonical."
|
|
18996
|
+
);
|
|
18997
|
+
const exclude = questionAnswer(
|
|
18998
|
+
session.questions,
|
|
18999
|
+
"exclude",
|
|
19000
|
+
"Keep uncertain or incidental details provisional until they matter to the research thread."
|
|
19001
|
+
);
|
|
19002
|
+
const conflictNotes = questionAnswer(
|
|
19003
|
+
session.questions,
|
|
19004
|
+
"conflicts",
|
|
19005
|
+
contradictions.length ? "Review the conflicting evidence before accepting any canonical summary changes." : "No explicit conflicts were called out."
|
|
19006
|
+
);
|
|
19007
|
+
const followups = questionAnswer(session.questions, "followups", "Track follow-up questions on the source session page.");
|
|
19008
|
+
const updateBlock = [
|
|
19009
|
+
`## Guided Session Update: ${scope.title}`,
|
|
19010
|
+
"",
|
|
19011
|
+
`Session: [[outputs/source-sessions/${scope.id}|Guided Session]]`,
|
|
19012
|
+
`Source Guide: [[outputs/source-guides/${scope.id}|Source Guide]]`,
|
|
19013
|
+
"",
|
|
19014
|
+
"### What Matters Now",
|
|
19015
|
+
"",
|
|
19016
|
+
importance,
|
|
19017
|
+
"",
|
|
19018
|
+
"### Proposed Integration",
|
|
19019
|
+
"",
|
|
19020
|
+
targetPage ? `- Fold the strongest source-backed takeaways into [[${targetPage.path.replace(/\.md$/, "")}|${targetPage.title}]].` : `- Start a durable topic note for ${scope.title}.`,
|
|
19021
|
+
...analyses.slice(0, 5).map((analysis) => `- ${truncate(normalizeWhitespace(analysis.summary), 180)}`),
|
|
19022
|
+
"",
|
|
19023
|
+
"### Keep Provisional Or Out",
|
|
19024
|
+
"",
|
|
19025
|
+
exclude,
|
|
19026
|
+
"",
|
|
19027
|
+
"### Reinforcing Or Conflicting Notes",
|
|
19028
|
+
"",
|
|
19029
|
+
conflictNotes,
|
|
19030
|
+
...contradictions.length ? ["", ...contradictions.slice(0, 4).map((contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB}`)] : [],
|
|
19031
|
+
"",
|
|
19032
|
+
"### Follow-up Questions",
|
|
19033
|
+
"",
|
|
19034
|
+
followups,
|
|
19035
|
+
""
|
|
19036
|
+
].join("\n");
|
|
19037
|
+
const nextBody = replaceMarkedSection(baseBody, scope.id, updateBlock);
|
|
19038
|
+
const content = matter10.stringify(`${nextBody.trimEnd()}
|
|
19039
|
+
`, {
|
|
19040
|
+
...existingData,
|
|
19041
|
+
page_id: typeof existingData.page_id === "string" && existingData.page_id.trim() || `insight:${slugify(relativePath.replace(/\.md$/, ""))}`,
|
|
19042
|
+
kind: "insight",
|
|
19043
|
+
title,
|
|
19044
|
+
tags: uniqueStrings4([
|
|
19045
|
+
...Array.isArray(existingData.tags) ? existingData.tags.filter((value) => typeof value === "string") : [],
|
|
19046
|
+
...insightTagsForTarget(targetPage)
|
|
19047
|
+
]),
|
|
19048
|
+
source_ids: uniqueStrings4([...existingSourceIds, ...scope.sourceIds]),
|
|
19049
|
+
project_ids: uniqueStrings4([...existingProjectIds, ...targetPage?.projectIds ?? []]),
|
|
19050
|
+
node_ids: uniqueStrings4([...existingNodeIds, ...targetPage?.nodeIds ?? []]),
|
|
19051
|
+
freshness: "fresh",
|
|
19052
|
+
status: existingData.status === "archived" ? "archived" : "active",
|
|
19053
|
+
confidence: 0.83,
|
|
19054
|
+
created_at: createdAt,
|
|
19055
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19056
|
+
compiled_from: uniqueStrings4([
|
|
19057
|
+
...Array.isArray(existingData.compiled_from) ? existingData.compiled_from.filter((value) => typeof value === "string") : [],
|
|
19058
|
+
...scope.sourceIds
|
|
19059
|
+
]),
|
|
19060
|
+
managed_by: "human",
|
|
19061
|
+
backlinks: uniqueStrings4([
|
|
19062
|
+
...existingBacklinks,
|
|
19063
|
+
...targetPage ? [targetPage.id] : [],
|
|
19064
|
+
`output:source-sessions/${scope.id}`,
|
|
19065
|
+
`output:source-guides/${scope.id}`
|
|
19066
|
+
]),
|
|
19067
|
+
schema_hash: typeof existingData.schema_hash === "string" ? existingData.schema_hash : "",
|
|
19068
|
+
source_hashes: existingData.source_hashes && typeof existingData.source_hashes === "object" ? existingData.source_hashes : {},
|
|
19069
|
+
source_semantic_hashes: existingData.source_semantic_hashes && typeof existingData.source_semantic_hashes === "object" ? existingData.source_semantic_hashes : {}
|
|
19070
|
+
});
|
|
19071
|
+
const page = parseStoredPage(relativePath, content, {
|
|
19072
|
+
createdAt,
|
|
19073
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19074
|
+
});
|
|
19075
|
+
if (index === 0) {
|
|
19076
|
+
session.targetedPagePaths = uniqueStrings4([
|
|
19077
|
+
...session.targetedPagePaths,
|
|
19078
|
+
...selectedTargets.length ? selectedTargets.map((page2) => page2.path) : [relativePath]
|
|
19079
|
+
]);
|
|
19080
|
+
}
|
|
19081
|
+
return { page, content, label: "guided-update" };
|
|
19082
|
+
})
|
|
19083
|
+
);
|
|
19084
|
+
}
|
|
19085
|
+
async function stageSourceGuideForScope(rootDir, scope, options = {}) {
|
|
19086
|
+
const { session, statePath } = await prepareGuidedSourceSession(rootDir, scope, options.answers);
|
|
19087
|
+
const briefPath = scope.briefPath ?? session.briefPath ?? await writeSourceBriefForScope(rootDir, scope) ?? void 0;
|
|
19088
|
+
session.briefPath = briefPath;
|
|
19089
|
+
if (briefPath) {
|
|
19090
|
+
await refreshVaultAfterOutputSave(rootDir);
|
|
19091
|
+
}
|
|
19092
|
+
if (answeredGuidedSessionQuestions(session.questions).length === 0) {
|
|
19093
|
+
session.status = "awaiting_input";
|
|
19094
|
+
const persisted2 = await persistSourceSessionPage(rootDir, scope, session);
|
|
19095
|
+
session.sessionPath = persisted2.sessionPath;
|
|
19096
|
+
await writeGuidedSourceSession(rootDir, session);
|
|
19097
|
+
await refreshVaultAfterOutputSave(rootDir);
|
|
19098
|
+
return {
|
|
19099
|
+
sourceId: scope.id,
|
|
19100
|
+
sessionId: session.sessionId,
|
|
19101
|
+
sessionPath: persisted2.sessionPath,
|
|
19102
|
+
sessionStatePath: statePath,
|
|
19103
|
+
status: session.status,
|
|
19104
|
+
questions: session.questions,
|
|
19105
|
+
awaitingInput: true,
|
|
19106
|
+
targetedPagePaths: session.targetedPagePaths,
|
|
19107
|
+
stagedUpdatePaths: session.stagedUpdatePaths,
|
|
19108
|
+
briefPath,
|
|
19109
|
+
staged: false
|
|
19110
|
+
};
|
|
19111
|
+
}
|
|
19112
|
+
session.status = "ready_to_stage";
|
|
19113
|
+
await writeGuidedSourceSession(rootDir, session);
|
|
19114
|
+
const reviewOutput = await buildSourceReviewStagedPage(rootDir, scope);
|
|
19115
|
+
const guideOutput = await buildSourceGuideStagedPage(rootDir, {
|
|
19116
|
+
...scope,
|
|
19117
|
+
briefPath
|
|
19118
|
+
});
|
|
19119
|
+
const guidedUpdates = await buildGuidedInsightUpdatePages(rootDir, scope, session);
|
|
19120
|
+
session.stagedUpdatePaths = guidedUpdates.map((item) => item.page.path);
|
|
19121
|
+
const approval = await stageGeneratedOutputPages(
|
|
19122
|
+
rootDir,
|
|
19123
|
+
[
|
|
19124
|
+
{ page: reviewOutput.page, content: reviewOutput.content, label: "source-review" },
|
|
19125
|
+
{ page: guideOutput.page, content: guideOutput.content, label: "source-guide" },
|
|
19126
|
+
...guidedUpdates
|
|
19127
|
+
],
|
|
19128
|
+
{
|
|
19129
|
+
bundleType: "guided_session",
|
|
19130
|
+
title: `Guided Session: ${scope.title}`,
|
|
19131
|
+
sourceSessionId: session.sessionId
|
|
19132
|
+
}
|
|
19133
|
+
);
|
|
19134
|
+
session.status = "staged";
|
|
19135
|
+
session.reviewPath = path26.join(approval.approvalDir, "wiki", reviewOutput.page.path);
|
|
19136
|
+
session.guidePath = path26.join(approval.approvalDir, "wiki", guideOutput.page.path);
|
|
19137
|
+
session.approvalId = approval.approvalId;
|
|
19138
|
+
session.approvalDir = approval.approvalDir;
|
|
19139
|
+
const persisted = await persistSourceSessionPage(rootDir, scope, session);
|
|
19140
|
+
session.sessionPath = persisted.sessionPath;
|
|
19141
|
+
await writeGuidedSourceSession(rootDir, session);
|
|
19142
|
+
await refreshVaultAfterOutputSave(rootDir);
|
|
19143
|
+
return {
|
|
19144
|
+
sourceId: scope.id,
|
|
19145
|
+
pageId: guideOutput.page.id,
|
|
19146
|
+
guidePath: session.guidePath,
|
|
19147
|
+
reviewPageId: reviewOutput.page.id,
|
|
19148
|
+
reviewPath: session.reviewPath,
|
|
19149
|
+
sessionId: session.sessionId,
|
|
19150
|
+
sessionPath: persisted.sessionPath,
|
|
19151
|
+
sessionStatePath: statePath,
|
|
19152
|
+
status: session.status,
|
|
19153
|
+
questions: session.questions,
|
|
19154
|
+
targetedPagePaths: session.targetedPagePaths,
|
|
19155
|
+
stagedUpdatePaths: session.stagedUpdatePaths,
|
|
19156
|
+
briefPath,
|
|
19157
|
+
staged: true,
|
|
19158
|
+
approvalId: approval.approvalId,
|
|
19159
|
+
approvalDir: approval.approvalDir
|
|
19160
|
+
};
|
|
19161
|
+
}
|
|
19162
|
+
function scopeFromManagedSource(source) {
|
|
19163
|
+
return {
|
|
19164
|
+
id: source.id,
|
|
19165
|
+
title: source.title,
|
|
19166
|
+
sourceIds: source.sourceIds,
|
|
19167
|
+
kind: source.kind,
|
|
19168
|
+
briefPath: source.briefPath
|
|
19169
|
+
};
|
|
19170
|
+
}
|
|
19171
|
+
function scopeFromManifest(manifest, manifests) {
|
|
19172
|
+
const groupId = manifest.sourceGroupId ?? manifest.sourceId;
|
|
19173
|
+
return {
|
|
19174
|
+
id: groupId,
|
|
19175
|
+
title: manifest.sourceGroupTitle ?? manifest.title,
|
|
19176
|
+
sourceIds: manifest.sourceGroupId ? manifests.filter((candidate) => candidate.sourceGroupId === manifest.sourceGroupId).map((candidate) => candidate.sourceId) : [manifest.sourceId],
|
|
19177
|
+
kind: manifest.sourceKind
|
|
19178
|
+
};
|
|
19179
|
+
}
|
|
19180
|
+
async function resolveSourceScope(rootDir, id) {
|
|
19181
|
+
const managedSources = await loadManagedSources(rootDir);
|
|
19182
|
+
const managedSource = managedSources.find((source) => source.id === id);
|
|
19183
|
+
if (managedSource) {
|
|
19184
|
+
return scopeFromManagedSource(managedSource);
|
|
19185
|
+
}
|
|
19186
|
+
const latestSession = await findLatestGuidedSourceSessionByScope(rootDir, id);
|
|
19187
|
+
if (latestSession) {
|
|
19188
|
+
return {
|
|
19189
|
+
id: latestSession.scopeId,
|
|
19190
|
+
title: latestSession.scopeTitle,
|
|
19191
|
+
sourceIds: latestSession.sourceIds
|
|
19192
|
+
};
|
|
19193
|
+
}
|
|
19194
|
+
const manifests = await listManifests(rootDir);
|
|
19195
|
+
const manifest = manifests.find((candidate) => candidate.sourceId === id) ?? manifests.find((candidate) => candidate.sourceGroupId === id);
|
|
19196
|
+
if (!manifest) {
|
|
19197
|
+
return null;
|
|
19198
|
+
}
|
|
19199
|
+
return scopeFromManifest(manifest, manifests);
|
|
19200
|
+
}
|
|
19201
|
+
async function reviewSourceScope(rootDir, scope) {
|
|
19202
|
+
return await stageSourceReviewForScope(rootDir, scope);
|
|
19203
|
+
}
|
|
19204
|
+
async function guideSourceScope(rootDir, scope, options = {}) {
|
|
19205
|
+
return await stageSourceGuideForScope(rootDir, scope, options);
|
|
19206
|
+
}
|
|
19207
|
+
async function reviewManagedSource(rootDir, id) {
|
|
19208
|
+
const scope = await resolveSourceScope(rootDir, id);
|
|
19209
|
+
if (!scope) {
|
|
19210
|
+
throw new Error(`Managed source or source id not found: ${id}`);
|
|
19211
|
+
}
|
|
19212
|
+
if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
|
|
19213
|
+
await compileVault(rootDir, {});
|
|
19214
|
+
}
|
|
19215
|
+
return await stageSourceReviewForScope(rootDir, scope);
|
|
19216
|
+
}
|
|
19217
|
+
async function guideManagedSource(rootDir, id, options = {}) {
|
|
19218
|
+
const scope = await resolveSourceScope(rootDir, id);
|
|
19219
|
+
if (!scope) {
|
|
19220
|
+
throw new Error(`Managed source or source id not found: ${id}`);
|
|
19221
|
+
}
|
|
19222
|
+
if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
|
|
19223
|
+
await compileVault(rootDir, {});
|
|
19224
|
+
}
|
|
19225
|
+
return await stageSourceGuideForScope(rootDir, scope, options);
|
|
19226
|
+
}
|
|
19227
|
+
async function resumeSourceSession(rootDir, id, options = {}) {
|
|
19228
|
+
const existingSession = await readGuidedSourceSession(rootDir, id);
|
|
19229
|
+
if (existingSession) {
|
|
19230
|
+
return await stageSourceGuideForScope(
|
|
19231
|
+
rootDir,
|
|
19232
|
+
{
|
|
19233
|
+
id: existingSession.scopeId,
|
|
19234
|
+
title: existingSession.scopeTitle,
|
|
19235
|
+
sourceIds: existingSession.sourceIds,
|
|
19236
|
+
kind: existingSession.kind,
|
|
19237
|
+
briefPath: existingSession.briefPath
|
|
19238
|
+
},
|
|
19239
|
+
options
|
|
19240
|
+
);
|
|
19241
|
+
}
|
|
19242
|
+
const scope = await resolveSourceScope(rootDir, id);
|
|
19243
|
+
if (!scope) {
|
|
19244
|
+
throw new Error(`Managed source, source scope, or guided session not found: ${id}`);
|
|
19245
|
+
}
|
|
19246
|
+
return await stageSourceGuideForScope(rootDir, scope, options);
|
|
18213
19247
|
}
|
|
18214
19248
|
function shouldCompile(changedSources, graphExists, compileRequested) {
|
|
18215
19249
|
return compileRequested && (!graphExists || changedSources.length > 0);
|
|
@@ -18220,14 +19254,15 @@ async function listManagedSourceRecords(rootDir) {
|
|
|
18220
19254
|
}
|
|
18221
19255
|
async function addManagedSource(rootDir, input, options = {}) {
|
|
18222
19256
|
const compileRequested = options.compile ?? true;
|
|
18223
|
-
const
|
|
18224
|
-
const
|
|
19257
|
+
const guideRequested = options.guide ?? false;
|
|
19258
|
+
const briefRequested = guideRequested ? true : options.brief ?? true;
|
|
19259
|
+
const reviewRequested = guideRequested ? false : options.review ?? false;
|
|
18225
19260
|
const sources = await loadManagedSources(rootDir);
|
|
18226
19261
|
const resolved = await resolveManagedSourceInput(rootDir, input);
|
|
18227
19262
|
const existing = sources.find((candidate) => matchesManagedSourceSpec(candidate, resolved));
|
|
18228
19263
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18229
19264
|
const source = existing ?? {
|
|
18230
|
-
id: resolved.kind === "directory" || resolved.kind === "file" ? stableManagedSourceId(resolved.kind,
|
|
19265
|
+
id: resolved.kind === "directory" || resolved.kind === "file" ? stableManagedSourceId(resolved.kind, path26.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
|
|
18231
19266
|
kind: resolved.kind,
|
|
18232
19267
|
title: resolved.title,
|
|
18233
19268
|
path: resolved.kind === "directory" || resolved.kind === "file" ? resolved.path : void 0,
|
|
@@ -18262,17 +19297,27 @@ async function addManagedSource(rootDir, input, options = {}) {
|
|
|
18262
19297
|
const nextSources = existing ? sources.map((candidate) => candidate.id === nextSource.id ? nextSource : candidate) : [...sources, nextSource];
|
|
18263
19298
|
await saveManagedSources(rootDir, nextSources);
|
|
18264
19299
|
const review = reviewRequested && nextSource.status === "ready" ? await stageSourceReviewForScope(rootDir, scopeFromManagedSource(nextSource)) : void 0;
|
|
19300
|
+
const guide = guideRequested && nextSource.status === "ready" ? await stageSourceGuideForScope(
|
|
19301
|
+
rootDir,
|
|
19302
|
+
{
|
|
19303
|
+
...scopeFromManagedSource(nextSource),
|
|
19304
|
+
briefPath: nextSource.briefPath
|
|
19305
|
+
},
|
|
19306
|
+
{ answers: options.guideAnswers }
|
|
19307
|
+
) : void 0;
|
|
18265
19308
|
return {
|
|
18266
19309
|
source: nextSource,
|
|
18267
19310
|
compile,
|
|
18268
19311
|
briefGenerated,
|
|
18269
|
-
review
|
|
19312
|
+
review,
|
|
19313
|
+
guide
|
|
18270
19314
|
};
|
|
18271
19315
|
}
|
|
18272
19316
|
async function reloadManagedSources(rootDir, options = {}) {
|
|
18273
19317
|
const compileRequested = options.compile ?? true;
|
|
18274
|
-
const
|
|
18275
|
-
const
|
|
19318
|
+
const guideRequested = options.guide ?? false;
|
|
19319
|
+
const briefRequested = guideRequested ? true : options.brief ?? true;
|
|
19320
|
+
const reviewRequested = guideRequested ? false : options.review ?? false;
|
|
18276
19321
|
const sources = await loadManagedSources(rootDir);
|
|
18277
19322
|
const selected = options.all || !options.id ? sources : sources.filter((source) => source.id === options.id);
|
|
18278
19323
|
if (!selected.length) {
|
|
@@ -18311,11 +19356,24 @@ async function reloadManagedSources(rootDir, options = {}) {
|
|
|
18311
19356
|
const reviews = reviewRequested ? await Promise.all(
|
|
18312
19357
|
nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)).filter((source) => source.status === "ready").map(async (source) => await stageSourceReviewForScope(rootDir, scopeFromManagedSource(source)))
|
|
18313
19358
|
) : [];
|
|
19359
|
+
const guides = guideRequested ? await Promise.all(
|
|
19360
|
+
nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)).filter((source) => source.status === "ready").map(
|
|
19361
|
+
async (source) => await stageSourceGuideForScope(
|
|
19362
|
+
rootDir,
|
|
19363
|
+
{
|
|
19364
|
+
...scopeFromManagedSource(source),
|
|
19365
|
+
briefPath: source.briefPath
|
|
19366
|
+
},
|
|
19367
|
+
{ answers: options.guideAnswers }
|
|
19368
|
+
)
|
|
19369
|
+
)
|
|
19370
|
+
) : [];
|
|
18314
19371
|
return {
|
|
18315
19372
|
sources: nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)),
|
|
18316
19373
|
compile,
|
|
18317
19374
|
briefPaths: [...briefPaths.values()],
|
|
18318
|
-
reviews
|
|
19375
|
+
reviews,
|
|
19376
|
+
guides
|
|
18319
19377
|
};
|
|
18320
19378
|
}
|
|
18321
19379
|
async function deleteManagedSource(rootDir, id) {
|
|
@@ -18329,17 +19387,17 @@ async function deleteManagedSource(rootDir, id) {
|
|
|
18329
19387
|
sources.filter((source) => source.id !== id)
|
|
18330
19388
|
);
|
|
18331
19389
|
const workingDir = await managedSourceWorkingDir(rootDir, id);
|
|
18332
|
-
await
|
|
19390
|
+
await fs22.rm(workingDir, { recursive: true, force: true });
|
|
18333
19391
|
return { removed: target };
|
|
18334
19392
|
}
|
|
18335
19393
|
|
|
18336
19394
|
// src/viewer.ts
|
|
18337
19395
|
import { execFile } from "child_process";
|
|
18338
|
-
import
|
|
19396
|
+
import fs23 from "fs/promises";
|
|
18339
19397
|
import http from "http";
|
|
18340
|
-
import
|
|
19398
|
+
import path28 from "path";
|
|
18341
19399
|
import { promisify } from "util";
|
|
18342
|
-
import
|
|
19400
|
+
import matter11 from "gray-matter";
|
|
18343
19401
|
import mime2 from "mime-types";
|
|
18344
19402
|
|
|
18345
19403
|
// src/graph-presentation.ts
|
|
@@ -18461,7 +19519,7 @@ function buildViewerGraphArtifact(graph, options = {}) {
|
|
|
18461
19519
|
}
|
|
18462
19520
|
|
|
18463
19521
|
// src/watch.ts
|
|
18464
|
-
import
|
|
19522
|
+
import path27 from "path";
|
|
18465
19523
|
import process3 from "process";
|
|
18466
19524
|
import chokidar from "chokidar";
|
|
18467
19525
|
var MAX_BACKOFF_MS = 3e4;
|
|
@@ -18469,15 +19527,15 @@ var BACKOFF_THRESHOLD = 3;
|
|
|
18469
19527
|
var CRITICAL_THRESHOLD = 10;
|
|
18470
19528
|
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
18471
19529
|
function withinRoot3(rootPath, targetPath) {
|
|
18472
|
-
const relative =
|
|
18473
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
19530
|
+
const relative = path27.relative(rootPath, targetPath);
|
|
19531
|
+
return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
|
|
18474
19532
|
}
|
|
18475
19533
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
18476
|
-
const relativePath =
|
|
19534
|
+
const relativePath = path27.relative(baseDir, targetPath);
|
|
18477
19535
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
18478
19536
|
return false;
|
|
18479
19537
|
}
|
|
18480
|
-
return relativePath.split(
|
|
19538
|
+
return relativePath.split(path27.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
|
|
18481
19539
|
}
|
|
18482
19540
|
function workspaceIgnoreRoots(rootDir, paths) {
|
|
18483
19541
|
return [
|
|
@@ -18486,16 +19544,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
|
|
|
18486
19544
|
paths.stateDir,
|
|
18487
19545
|
paths.agentDir,
|
|
18488
19546
|
paths.inboxDir,
|
|
18489
|
-
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
].map((candidate) =>
|
|
19547
|
+
path27.join(rootDir, ".claude"),
|
|
19548
|
+
path27.join(rootDir, ".cursor"),
|
|
19549
|
+
path27.join(rootDir, ".obsidian")
|
|
19550
|
+
].map((candidate) => path27.resolve(candidate));
|
|
18493
19551
|
}
|
|
18494
19552
|
async function resolveWatchTargets(rootDir, paths, options) {
|
|
18495
|
-
const targets = /* @__PURE__ */ new Set([
|
|
19553
|
+
const targets = /* @__PURE__ */ new Set([path27.resolve(paths.inboxDir)]);
|
|
18496
19554
|
if (options.repo) {
|
|
18497
19555
|
for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
|
|
18498
|
-
targets.add(
|
|
19556
|
+
targets.add(path27.resolve(repoRoot));
|
|
18499
19557
|
}
|
|
18500
19558
|
}
|
|
18501
19559
|
return [...targets].sort((left, right) => left.localeCompare(right));
|
|
@@ -18625,7 +19683,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
18625
19683
|
const { paths } = await initWorkspace(rootDir);
|
|
18626
19684
|
const baseDebounceMs = options.debounceMs ?? 900;
|
|
18627
19685
|
const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
|
|
18628
|
-
const inboxWatchRoot =
|
|
19686
|
+
const inboxWatchRoot = path27.resolve(paths.inboxDir);
|
|
18629
19687
|
let watchTargets = await resolveWatchTargets(rootDir, paths, options);
|
|
18630
19688
|
let timer;
|
|
18631
19689
|
let running = false;
|
|
@@ -18640,7 +19698,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
18640
19698
|
usePolling: true,
|
|
18641
19699
|
interval: 100,
|
|
18642
19700
|
ignored: (targetPath) => {
|
|
18643
|
-
const absolutePath =
|
|
19701
|
+
const absolutePath = path27.resolve(targetPath);
|
|
18644
19702
|
const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
18645
19703
|
if (!primaryTarget) {
|
|
18646
19704
|
return false;
|
|
@@ -18829,8 +19887,8 @@ async function watchVault(rootDir, options = {}) {
|
|
|
18829
19887
|
}
|
|
18830
19888
|
};
|
|
18831
19889
|
const reasonForPath = (targetPath) => {
|
|
18832
|
-
const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget,
|
|
18833
|
-
return
|
|
19890
|
+
const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path27.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
|
|
19891
|
+
return path27.relative(baseDir, targetPath) || ".";
|
|
18834
19892
|
};
|
|
18835
19893
|
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)}`));
|
|
18836
19894
|
await new Promise((resolve, reject) => {
|
|
@@ -18872,15 +19930,15 @@ async function getWatchStatus(rootDir) {
|
|
|
18872
19930
|
var execFileAsync = promisify(execFile);
|
|
18873
19931
|
async function readViewerPage(rootDir, relativePath) {
|
|
18874
19932
|
const { paths } = await loadVaultConfig(rootDir);
|
|
18875
|
-
const absolutePath =
|
|
19933
|
+
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
18876
19934
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
18877
19935
|
return null;
|
|
18878
19936
|
}
|
|
18879
|
-
const raw = await
|
|
18880
|
-
const parsed =
|
|
19937
|
+
const raw = await fs23.readFile(absolutePath, "utf8");
|
|
19938
|
+
const parsed = matter11(raw);
|
|
18881
19939
|
return {
|
|
18882
19940
|
path: relativePath,
|
|
18883
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
19941
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path28.basename(relativePath, path28.extname(relativePath)),
|
|
18884
19942
|
frontmatter: parsed.data,
|
|
18885
19943
|
content: parsed.content,
|
|
18886
19944
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -18888,12 +19946,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
18888
19946
|
}
|
|
18889
19947
|
async function readViewerAsset(rootDir, relativePath) {
|
|
18890
19948
|
const { paths } = await loadVaultConfig(rootDir);
|
|
18891
|
-
const absolutePath =
|
|
19949
|
+
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
18892
19950
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
18893
19951
|
return null;
|
|
18894
19952
|
}
|
|
18895
19953
|
return {
|
|
18896
|
-
buffer: await
|
|
19954
|
+
buffer: await fs23.readFile(absolutePath),
|
|
18897
19955
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
18898
19956
|
};
|
|
18899
19957
|
}
|
|
@@ -18916,12 +19974,12 @@ async function readJsonBody(request) {
|
|
|
18916
19974
|
return JSON.parse(raw);
|
|
18917
19975
|
}
|
|
18918
19976
|
async function ensureViewerDist(viewerDistDir) {
|
|
18919
|
-
const indexPath =
|
|
19977
|
+
const indexPath = path28.join(viewerDistDir, "index.html");
|
|
18920
19978
|
if (await fileExists(indexPath)) {
|
|
18921
19979
|
return;
|
|
18922
19980
|
}
|
|
18923
|
-
const viewerProjectDir =
|
|
18924
|
-
if (await fileExists(
|
|
19981
|
+
const viewerProjectDir = path28.dirname(viewerDistDir);
|
|
19982
|
+
if (await fileExists(path28.join(viewerProjectDir, "package.json"))) {
|
|
18925
19983
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
18926
19984
|
}
|
|
18927
19985
|
}
|
|
@@ -18943,7 +20001,7 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
18943
20001
|
response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
|
|
18944
20002
|
return;
|
|
18945
20003
|
}
|
|
18946
|
-
const reportPath =
|
|
20004
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
18947
20005
|
const report = await readJsonFile(reportPath) ?? null;
|
|
18948
20006
|
response.writeHead(200, { "content-type": "application/json" });
|
|
18949
20007
|
response.end(JSON.stringify(buildViewerGraphArtifact(graph, { report, full: options.full ?? false })));
|
|
@@ -19003,14 +20061,14 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
19003
20061
|
return;
|
|
19004
20062
|
}
|
|
19005
20063
|
if (url.pathname === "/api/graph-report") {
|
|
19006
|
-
const reportPath =
|
|
20064
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
19007
20065
|
if (!await fileExists(reportPath)) {
|
|
19008
20066
|
response.writeHead(404, { "content-type": "application/json" });
|
|
19009
20067
|
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
19010
20068
|
return;
|
|
19011
20069
|
}
|
|
19012
20070
|
response.writeHead(200, { "content-type": "application/json" });
|
|
19013
|
-
response.end(await
|
|
20071
|
+
response.end(await fs23.readFile(reportPath, "utf8"));
|
|
19014
20072
|
return;
|
|
19015
20073
|
}
|
|
19016
20074
|
if (url.pathname === "/api/watch-status") {
|
|
@@ -19093,8 +20151,8 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
19093
20151
|
return;
|
|
19094
20152
|
}
|
|
19095
20153
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
19096
|
-
const target =
|
|
19097
|
-
const fallback =
|
|
20154
|
+
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
20155
|
+
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
19098
20156
|
const filePath = await fileExists(target) ? target : fallback;
|
|
19099
20157
|
if (!await fileExists(filePath)) {
|
|
19100
20158
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -19102,7 +20160,7 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
19102
20160
|
return;
|
|
19103
20161
|
}
|
|
19104
20162
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
19105
|
-
response.end(await
|
|
20163
|
+
response.end(await fs23.readFile(filePath));
|
|
19106
20164
|
});
|
|
19107
20165
|
await new Promise((resolve) => {
|
|
19108
20166
|
server.listen(effectivePort, resolve);
|
|
@@ -19129,7 +20187,7 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
|
|
|
19129
20187
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
19130
20188
|
}
|
|
19131
20189
|
await ensureViewerDist(paths.viewerDistDir);
|
|
19132
|
-
const indexPath =
|
|
20190
|
+
const indexPath = path28.join(paths.viewerDistDir, "index.html");
|
|
19133
20191
|
if (!await fileExists(indexPath)) {
|
|
19134
20192
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
19135
20193
|
}
|
|
@@ -19155,17 +20213,17 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
|
|
|
19155
20213
|
} : null;
|
|
19156
20214
|
})
|
|
19157
20215
|
);
|
|
19158
|
-
const rawHtml = await
|
|
20216
|
+
const rawHtml = await fs23.readFile(indexPath, "utf8");
|
|
19159
20217
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
19160
20218
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
19161
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
19162
|
-
const stylePath = styleMatch?.[1] ?
|
|
20219
|
+
const scriptPath = scriptMatch?.[1] ? path28.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
|
|
20220
|
+
const stylePath = styleMatch?.[1] ? path28.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
|
|
19163
20221
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
19164
20222
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
19165
20223
|
}
|
|
19166
|
-
const script = await
|
|
19167
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
19168
|
-
const report = await readJsonFile(
|
|
20224
|
+
const script = await fs23.readFile(scriptPath, "utf8");
|
|
20225
|
+
const style = stylePath && await fileExists(stylePath) ? await fs23.readFile(stylePath, "utf8") : "";
|
|
20226
|
+
const report = await readJsonFile(path28.join(paths.wikiDir, "graph", "report.json"));
|
|
19169
20227
|
const embeddedData = JSON.stringify(
|
|
19170
20228
|
{ graph: buildViewerGraphArtifact(graph, { report, full: options.full ?? false }), pages: pages.filter(Boolean), report },
|
|
19171
20229
|
null,
|
|
@@ -19188,9 +20246,9 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
|
|
|
19188
20246
|
"</html>",
|
|
19189
20247
|
""
|
|
19190
20248
|
].filter(Boolean).join("\n");
|
|
19191
|
-
await
|
|
19192
|
-
await
|
|
19193
|
-
return
|
|
20249
|
+
await fs23.mkdir(path28.dirname(outputPath), { recursive: true });
|
|
20250
|
+
await fs23.writeFile(outputPath, html, "utf8");
|
|
20251
|
+
return path28.resolve(outputPath);
|
|
19194
20252
|
}
|
|
19195
20253
|
export {
|
|
19196
20254
|
acceptApproval,
|
|
@@ -19216,6 +20274,8 @@ export {
|
|
|
19216
20274
|
getWatchStatus,
|
|
19217
20275
|
getWebSearchAdapterForTask,
|
|
19218
20276
|
getWorkspaceInfo,
|
|
20277
|
+
guideManagedSource,
|
|
20278
|
+
guideSourceScope,
|
|
19219
20279
|
importInbox,
|
|
19220
20280
|
ingestDirectory,
|
|
19221
20281
|
ingestInput,
|
|
@@ -19250,6 +20310,7 @@ export {
|
|
|
19250
20310
|
rejectApproval,
|
|
19251
20311
|
reloadManagedSources,
|
|
19252
20312
|
resolvePaths,
|
|
20313
|
+
resumeSourceSession,
|
|
19253
20314
|
reviewManagedSource,
|
|
19254
20315
|
reviewSourceScope,
|
|
19255
20316
|
runSchedule,
|