@swarmvaultai/engine 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +224 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -184,6 +184,7 @@ This matters because many "OpenAI-compatible" backends only implement part of th
|
|
|
184
184
|
|
|
185
185
|
- `compileVault(rootDir, { approve })` writes wiki pages, graph data, and search state using the vault schema as guidance, or stages a review bundle
|
|
186
186
|
- compile also writes graph orientation pages such as `wiki/graph/report.md`, `wiki/graph/report.json`, and `wiki/graph/communities/<community>.md`
|
|
187
|
+
- compile propagates semantic tags onto page frontmatter and source-backed graph nodes, and records deterministic `contradicts` edges plus a Contradictions section in the graph report when conflicting claims are found
|
|
187
188
|
- `benchmarkVault(rootDir, { questions })` writes `state/benchmark.json` and folds the latest benchmark summary into `wiki/graph/report.md` and `wiki/graph/report.json`
|
|
188
189
|
- semantic graph query and embedding-backed similarity enrichment cache vectors under `state/embeddings.json` so graph-semantic refresh stays incremental
|
|
189
190
|
- `queryVault(rootDir, { question, save, format, review })` answers against the compiled vault using the same schema layer and saves by default
|
|
@@ -207,7 +208,7 @@ This matters because many "OpenAI-compatible" backends only implement part of th
|
|
|
207
208
|
- `syncTrackedReposForWatch(rootDir)` is the repo-watch sync path that defers non-code semantic refresh into `state/watch/`
|
|
208
209
|
- `installGitHooks(rootDir)`, `uninstallGitHooks(rootDir)`, and `getGitHookStatus(rootDir)` manage local `post-commit` and `post-checkout` hook blocks for the nearest git repository
|
|
209
210
|
- `installAgent(rootDir, agent, { hook })` writes agent instructions and returns the primary `target`, all touched `targets`, and optional merge warnings for agents such as Aider
|
|
210
|
-
- `lintVault(rootDir, options)` runs structural lint, optional deep lint, and optional web-augmented evidence gathering
|
|
211
|
+
- `lintVault(rootDir, options)` runs structural lint, optional deep lint, optional contradiction-only filtering through `{ conflicts: true }`, and optional web-augmented evidence gathering
|
|
211
212
|
- `listSchedules(rootDir)`, `runSchedule(rootDir, jobId)`, and `serveSchedules(rootDir)` manage recurring local jobs from config
|
|
212
213
|
- compile, query, explore, lint, and watch also write canonical markdown session artifacts to `state/sessions/`
|
|
213
214
|
- scheduled `query` and `explore` jobs stage saved outputs through approvals when they write artifacts
|
package/dist/index.d.ts
CHANGED
|
@@ -386,6 +386,7 @@ interface SourceAnalysis {
|
|
|
386
386
|
entities: AnalyzedTerm[];
|
|
387
387
|
claims: SourceClaim[];
|
|
388
388
|
questions: string[];
|
|
389
|
+
tags: string[];
|
|
389
390
|
rationales: SourceRationale[];
|
|
390
391
|
code?: CodeAnalysis;
|
|
391
392
|
producedAt: string;
|
|
@@ -407,6 +408,7 @@ interface GraphNode {
|
|
|
407
408
|
degree?: number;
|
|
408
409
|
bridgeScore?: number;
|
|
409
410
|
isGodNode?: boolean;
|
|
411
|
+
tags?: string[];
|
|
410
412
|
}
|
|
411
413
|
interface GraphEdge {
|
|
412
414
|
id: string;
|
|
@@ -549,6 +551,8 @@ interface ApprovalSummary {
|
|
|
549
551
|
interface ApprovalEntryDetail extends ApprovalEntry {
|
|
550
552
|
currentContent?: string;
|
|
551
553
|
stagedContent?: string;
|
|
554
|
+
changeSummary?: string;
|
|
555
|
+
diff?: string;
|
|
552
556
|
}
|
|
553
557
|
interface ApprovalDetail extends ApprovalSummary {
|
|
554
558
|
entries: ApprovalEntryDetail[];
|
|
@@ -731,6 +735,7 @@ interface CompileState {
|
|
|
731
735
|
interface LintOptions {
|
|
732
736
|
deep?: boolean;
|
|
733
737
|
web?: boolean;
|
|
738
|
+
conflicts?: boolean;
|
|
734
739
|
}
|
|
735
740
|
interface ExploreOptions {
|
|
736
741
|
question: string;
|
|
@@ -992,6 +997,13 @@ interface GraphReportArtifact {
|
|
|
992
997
|
sourceType: SourceCaptureType;
|
|
993
998
|
updatedAt: string;
|
|
994
999
|
}>;
|
|
1000
|
+
contradictions: Array<{
|
|
1001
|
+
sourceIdA: string;
|
|
1002
|
+
sourceIdB: string;
|
|
1003
|
+
claimA: string;
|
|
1004
|
+
claimB: string;
|
|
1005
|
+
confidenceDelta: number;
|
|
1006
|
+
}>;
|
|
995
1007
|
}
|
|
996
1008
|
interface ScheduledCompileTask {
|
|
997
1009
|
type: "compile";
|
|
@@ -1166,7 +1178,9 @@ declare function loadVaultSchemas(rootDir: string): Promise<LoadedVaultSchemas>;
|
|
|
1166
1178
|
declare function loadVaultSchema(rootDir: string): Promise<VaultSchema>;
|
|
1167
1179
|
|
|
1168
1180
|
declare function listApprovals(rootDir: string): Promise<ApprovalSummary[]>;
|
|
1169
|
-
declare function readApproval(rootDir: string, approvalId: string
|
|
1181
|
+
declare function readApproval(rootDir: string, approvalId: string, options?: {
|
|
1182
|
+
diff?: boolean;
|
|
1183
|
+
}): Promise<ApprovalDetail>;
|
|
1170
1184
|
declare function acceptApproval(rootDir: string, approvalId: string, targets?: string[]): Promise<ReviewActionResult>;
|
|
1171
1185
|
declare function rejectApproval(rootDir: string, approvalId: string, targets?: string[]): Promise<ReviewActionResult>;
|
|
1172
1186
|
declare function listCandidates(rootDir: string): Promise<CandidateRecord[]>;
|
package/dist/index.js
CHANGED
|
@@ -3910,7 +3910,7 @@ async function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
|
3910
3910
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType) ?? "typescript";
|
|
3911
3911
|
const { code, rationales } = language === "javascript" || language === "jsx" || language === "typescript" || language === "tsx" ? analyzeTypeScriptLikeCode(manifest, extractedText) : await analyzeTreeSitterCode(manifest, extractedText, language);
|
|
3912
3912
|
return {
|
|
3913
|
-
analysisVersion:
|
|
3913
|
+
analysisVersion: 6,
|
|
3914
3914
|
sourceId: manifest.sourceId,
|
|
3915
3915
|
sourceHash: manifest.contentHash,
|
|
3916
3916
|
extractionHash: manifest.extractionHash,
|
|
@@ -3921,6 +3921,7 @@ async function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
|
3921
3921
|
entities: [],
|
|
3922
3922
|
claims: codeClaims(manifest, code),
|
|
3923
3923
|
questions: codeQuestions(manifest, code),
|
|
3924
|
+
tags: [],
|
|
3924
3925
|
rationales,
|
|
3925
3926
|
code,
|
|
3926
3927
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6323,7 +6324,7 @@ import { z as z7 } from "zod";
|
|
|
6323
6324
|
// src/analysis.ts
|
|
6324
6325
|
import path13 from "path";
|
|
6325
6326
|
import { z as z2 } from "zod";
|
|
6326
|
-
var ANALYSIS_FORMAT_VERSION =
|
|
6327
|
+
var ANALYSIS_FORMAT_VERSION = 6;
|
|
6327
6328
|
var sourceAnalysisSchema = z2.object({
|
|
6328
6329
|
title: z2.string().min(1),
|
|
6329
6330
|
summary: z2.string().min(1),
|
|
@@ -6338,7 +6339,8 @@ var sourceAnalysisSchema = z2.object({
|
|
|
6338
6339
|
citation: z2.string().min(1)
|
|
6339
6340
|
})
|
|
6340
6341
|
).max(8).default([]),
|
|
6341
|
-
questions: z2.array(z2.string()).max(6).default([])
|
|
6342
|
+
questions: z2.array(z2.string()).max(6).default([]),
|
|
6343
|
+
tags: z2.array(z2.string()).max(5).default([])
|
|
6342
6344
|
});
|
|
6343
6345
|
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
6344
6346
|
"about",
|
|
@@ -6442,6 +6444,7 @@ function heuristicAnalysis(manifest, text, schemaHash) {
|
|
|
6442
6444
|
citation: manifest.sourceId
|
|
6443
6445
|
})),
|
|
6444
6446
|
questions: concepts.slice(0, 3).map((term) => `How does ${term.name} relate to ${manifest.title}?`),
|
|
6447
|
+
tags: [],
|
|
6445
6448
|
rationales: [],
|
|
6446
6449
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6447
6450
|
};
|
|
@@ -6454,6 +6457,8 @@ async function providerAnalysis(manifest, text, provider, schema) {
|
|
|
6454
6457
|
"",
|
|
6455
6458
|
"Follow the vault schema when choosing titles, categories, relationships, and summaries.",
|
|
6456
6459
|
"",
|
|
6460
|
+
"Return up to 5 broad domain tags that categorize this source. Tags should be lowercase kebab-case (e.g., cryptography, distributed-systems, machine-learning). These are broader categories, not specific concepts or entity names.",
|
|
6461
|
+
"",
|
|
6457
6462
|
`Vault schema path: ${schema.path}`,
|
|
6458
6463
|
"",
|
|
6459
6464
|
"Vault schema instructions:",
|
|
@@ -6497,6 +6502,7 @@ ${truncate(text, 18e3)}`
|
|
|
6497
6502
|
citation: claim.citation
|
|
6498
6503
|
})),
|
|
6499
6504
|
questions: parsed.questions,
|
|
6505
|
+
tags: parsed.tags,
|
|
6500
6506
|
rationales: [],
|
|
6501
6507
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6502
6508
|
};
|
|
@@ -6532,6 +6538,7 @@ function analysisFromVisionExtraction(manifest, extraction, schemaHash) {
|
|
|
6532
6538
|
citation: manifest.sourceId
|
|
6533
6539
|
})),
|
|
6534
6540
|
questions: extraction.vision.questions,
|
|
6541
|
+
tags: [],
|
|
6535
6542
|
rationales: [],
|
|
6536
6543
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6537
6544
|
};
|
|
@@ -6571,6 +6578,7 @@ async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
|
6571
6578
|
entities: [],
|
|
6572
6579
|
claims: [],
|
|
6573
6580
|
questions: [],
|
|
6581
|
+
tags: [],
|
|
6574
6582
|
rationales: [],
|
|
6575
6583
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6576
6584
|
};
|
|
@@ -6596,6 +6604,7 @@ async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
|
6596
6604
|
entities: [],
|
|
6597
6605
|
claims: [],
|
|
6598
6606
|
questions: [],
|
|
6607
|
+
tags: [],
|
|
6599
6608
|
rationales: [],
|
|
6600
6609
|
producedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6601
6610
|
};
|
|
@@ -6967,7 +6976,14 @@ var deepLintResponseSchema = z5.object({
|
|
|
6967
6976
|
findings: z5.array(
|
|
6968
6977
|
z5.object({
|
|
6969
6978
|
severity: z5.string().optional().default("info"),
|
|
6970
|
-
code: z5.enum([
|
|
6979
|
+
code: z5.enum([
|
|
6980
|
+
"coverage_gap",
|
|
6981
|
+
"contradiction_candidate",
|
|
6982
|
+
"contradiction",
|
|
6983
|
+
"missing_citation",
|
|
6984
|
+
"candidate_page",
|
|
6985
|
+
"follow_up_question"
|
|
6986
|
+
]),
|
|
6971
6987
|
message: z5.string().min(1),
|
|
6972
6988
|
relatedSourceIds: z5.array(z5.string()).default([]),
|
|
6973
6989
|
relatedPageIds: z5.array(z5.string()).default([]),
|
|
@@ -7100,6 +7116,7 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
|
|
|
7100
7116
|
system: "You are an auditor for a local-first LLM knowledge vault. Return advisory findings only. Do not propose direct file edits.",
|
|
7101
7117
|
prompt: [
|
|
7102
7118
|
"Review this SwarmVault state and return high-signal advisory findings.",
|
|
7119
|
+
"Look for claims that contradict each other across different sources. When you find a genuine contradiction, use code 'contradiction' and include both source IDs in relatedSourceIds.",
|
|
7103
7120
|
"",
|
|
7104
7121
|
"Schema:",
|
|
7105
7122
|
schema.content,
|
|
@@ -8962,6 +8979,13 @@ function buildGraphReportArtifact(input) {
|
|
|
8962
8979
|
title: page.title,
|
|
8963
8980
|
sourceType: page.sourceType,
|
|
8964
8981
|
updatedAt: page.updatedAt
|
|
8982
|
+
})),
|
|
8983
|
+
contradictions: (input.contradictions ?? []).map((c) => ({
|
|
8984
|
+
sourceIdA: c.sourceIdA,
|
|
8985
|
+
sourceIdB: c.sourceIdB,
|
|
8986
|
+
claimA: c.claimA.text,
|
|
8987
|
+
claimB: c.claimB.text,
|
|
8988
|
+
confidenceDelta: Math.abs(c.claimA.confidence - c.claimB.confidence)
|
|
8965
8989
|
}))
|
|
8966
8990
|
};
|
|
8967
8991
|
}
|
|
@@ -9081,6 +9105,12 @@ function buildGraphReportPage(input) {
|
|
|
9081
9105
|
return `- ${sourceLabel} ${connection.relation} ${targetLabel} (${connection.evidenceClass}, ${connection.confidence.toFixed(2)}). Why: ${connection.why}. ${connection.explanation} Path: ${connection.pathSummary}.`;
|
|
9082
9106
|
}) : ["- No cross-community links detected."],
|
|
9083
9107
|
"",
|
|
9108
|
+
"## Contradictions",
|
|
9109
|
+
"",
|
|
9110
|
+
...input.report.contradictions.length ? input.report.contradictions.map(
|
|
9111
|
+
(c) => `- **${c.claimA}** vs **${c.claimB}** (sources: \`${c.sourceIdA}\`, \`${c.sourceIdB}\`, confidence delta: ${c.confidenceDelta.toFixed(2)})`
|
|
9112
|
+
) : ["- No contradictions detected."],
|
|
9113
|
+
"",
|
|
9084
9114
|
"## Group Patterns",
|
|
9085
9115
|
"",
|
|
9086
9116
|
...input.report.groupPatterns.length ? input.report.groupPatterns.map((hyperedge) => {
|
|
@@ -10983,21 +11013,64 @@ function buildGoPackageSymbolLookups(analyses, manifestsById) {
|
|
|
10983
11013
|
])
|
|
10984
11014
|
);
|
|
10985
11015
|
}
|
|
11016
|
+
function claimTokens(text) {
|
|
11017
|
+
return new Set(
|
|
11018
|
+
text.toLowerCase().match(/[a-z][a-z0-9-]{2,}/g)?.filter((t) => !(/* @__PURE__ */ new Set(["the", "and", "for", "that", "this", "with", "are", "was", "from", "has", "not", "all", "but"])).has(t)) ?? []
|
|
11019
|
+
);
|
|
11020
|
+
}
|
|
11021
|
+
function claimJaccardSimilarity(a, b) {
|
|
11022
|
+
if (a.size === 0 && b.size === 0) return 0;
|
|
11023
|
+
let intersection = 0;
|
|
11024
|
+
for (const token of a) {
|
|
11025
|
+
if (b.has(token)) intersection++;
|
|
11026
|
+
}
|
|
11027
|
+
return intersection / (a.size + b.size - intersection);
|
|
11028
|
+
}
|
|
11029
|
+
function detectContradictions(analyses) {
|
|
11030
|
+
const contradictions = [];
|
|
11031
|
+
const claimsWithTokens = analyses.flatMap(
|
|
11032
|
+
(analysis) => analysis.claims.filter((c) => c.polarity === "positive" || c.polarity === "negative").map((c) => ({ sourceId: analysis.sourceId, claim: c, tokens: claimTokens(c.text) }))
|
|
11033
|
+
);
|
|
11034
|
+
for (let i = 0; i < claimsWithTokens.length; i++) {
|
|
11035
|
+
for (let j = i + 1; j < claimsWithTokens.length; j++) {
|
|
11036
|
+
const a = claimsWithTokens[i];
|
|
11037
|
+
const b = claimsWithTokens[j];
|
|
11038
|
+
if (a.sourceId === b.sourceId) continue;
|
|
11039
|
+
if (a.claim.polarity === b.claim.polarity) continue;
|
|
11040
|
+
const similarity = claimJaccardSimilarity(a.tokens, b.tokens);
|
|
11041
|
+
if (similarity >= 0.3) {
|
|
11042
|
+
contradictions.push({
|
|
11043
|
+
sourceIdA: a.sourceId,
|
|
11044
|
+
sourceIdB: b.sourceId,
|
|
11045
|
+
claimA: { text: a.claim.text, confidence: a.claim.confidence },
|
|
11046
|
+
claimB: { text: b.claim.text, confidence: b.claim.confidence },
|
|
11047
|
+
similarity
|
|
11048
|
+
});
|
|
11049
|
+
}
|
|
11050
|
+
}
|
|
11051
|
+
}
|
|
11052
|
+
return contradictions;
|
|
11053
|
+
}
|
|
10986
11054
|
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
10987
11055
|
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
10988
11056
|
const goPackageSymbolLookups = buildGoPackageSymbolLookups(analyses, manifestsById);
|
|
10989
|
-
const
|
|
10990
|
-
|
|
10991
|
-
|
|
10992
|
-
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
|
|
11000
|
-
|
|
11057
|
+
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
11058
|
+
const sourceNodes = manifests.map((manifest) => {
|
|
11059
|
+
const analysis = analysesBySourceId.get(manifest.sourceId);
|
|
11060
|
+
return {
|
|
11061
|
+
id: `source:${manifest.sourceId}`,
|
|
11062
|
+
type: "source",
|
|
11063
|
+
label: manifest.title,
|
|
11064
|
+
pageId: `source:${manifest.sourceId}`,
|
|
11065
|
+
freshness: "fresh",
|
|
11066
|
+
confidence: 1,
|
|
11067
|
+
sourceIds: [manifest.sourceId],
|
|
11068
|
+
projectIds: scopedProjectIdsFromSources([manifest.sourceId], sourceProjects),
|
|
11069
|
+
sourceClass: manifest.sourceClass,
|
|
11070
|
+
language: manifest.language,
|
|
11071
|
+
tags: analysis?.tags ?? []
|
|
11072
|
+
};
|
|
11073
|
+
});
|
|
11001
11074
|
const conceptMap = /* @__PURE__ */ new Map();
|
|
11002
11075
|
const entityMap = /* @__PURE__ */ new Map();
|
|
11003
11076
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
@@ -11012,7 +11085,6 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
11012
11085
|
edgesById.add(edge.id);
|
|
11013
11086
|
edges.push(edge);
|
|
11014
11087
|
};
|
|
11015
|
-
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
11016
11088
|
for (const analysis of analyses) {
|
|
11017
11089
|
for (const concept of analysis.concepts) {
|
|
11018
11090
|
const existing = conceptMap.get(concept.id);
|
|
@@ -11362,7 +11434,7 @@ function recentResearchSourcePages(graph, previousCompiledAt) {
|
|
|
11362
11434
|
sourceType: page.sourceType
|
|
11363
11435
|
}));
|
|
11364
11436
|
}
|
|
11365
|
-
async function buildGraphOrientationPages(graph, paths, schemaHash, previousCompiledAt) {
|
|
11437
|
+
async function buildGraphOrientationPages(graph, paths, schemaHash, previousCompiledAt, contradictions = []) {
|
|
11366
11438
|
const benchmark = await readJsonFile(paths.benchmarkPath);
|
|
11367
11439
|
const communityRecords = [];
|
|
11368
11440
|
for (const community of graph.communities ?? []) {
|
|
@@ -11392,7 +11464,8 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
|
|
|
11392
11464
|
benchmark,
|
|
11393
11465
|
benchmarkStale: benchmark ? benchmark.graphHash !== graphHash(graph) : false,
|
|
11394
11466
|
recentResearchSources: recentResearchSourcePages(graph, previousCompiledAt),
|
|
11395
|
-
graphHash: graphHash(graph)
|
|
11467
|
+
graphHash: graphHash(graph),
|
|
11468
|
+
contradictions
|
|
11396
11469
|
});
|
|
11397
11470
|
const reportAbsolutePath = path21.join(paths.wikiDir, "graph", "report.md");
|
|
11398
11471
|
const reportRecord = await buildManagedGraphPage(
|
|
@@ -11656,7 +11729,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
11656
11729
|
modulePreview ?? void 0,
|
|
11657
11730
|
{
|
|
11658
11731
|
projectIds: sourceProjectIds,
|
|
11659
|
-
extraTags: sourceCategoryTags,
|
|
11732
|
+
extraTags: [...sourceCategoryTags, ...analysis.tags ?? []],
|
|
11660
11733
|
sourceClass: manifest.sourceClass
|
|
11661
11734
|
}
|
|
11662
11735
|
)
|
|
@@ -11702,7 +11775,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
11702
11775
|
localModules,
|
|
11703
11776
|
relatedOutputs: relatedOutputsForPage(modulePreview, input.outputPages),
|
|
11704
11777
|
projectIds: sourceProjectIds,
|
|
11705
|
-
extraTags: sourceCategoryTags
|
|
11778
|
+
extraTags: [...sourceCategoryTags, ...analysis.tags ?? []]
|
|
11706
11779
|
})
|
|
11707
11780
|
)
|
|
11708
11781
|
);
|
|
@@ -11782,6 +11855,22 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
11782
11855
|
const compiledPages = records.map((record) => record.page);
|
|
11783
11856
|
const basePages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
11784
11857
|
const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex);
|
|
11858
|
+
const contradictions = detectContradictions(input.analyses);
|
|
11859
|
+
for (const contradiction of contradictions) {
|
|
11860
|
+
const edgeId = `contradiction:${contradiction.sourceIdA}->${contradiction.sourceIdB}`;
|
|
11861
|
+
if (!structuralGraph.edges.some((e) => e.id === edgeId)) {
|
|
11862
|
+
structuralGraph.edges.push({
|
|
11863
|
+
id: edgeId,
|
|
11864
|
+
source: `source:${contradiction.sourceIdA}`,
|
|
11865
|
+
target: `source:${contradiction.sourceIdB}`,
|
|
11866
|
+
relation: "contradicts",
|
|
11867
|
+
status: "conflicted",
|
|
11868
|
+
evidenceClass: "ambiguous",
|
|
11869
|
+
confidence: Math.abs(contradiction.claimA.confidence - contradiction.claimB.confidence),
|
|
11870
|
+
provenance: [contradiction.sourceIdA, contradiction.sourceIdB]
|
|
11871
|
+
});
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11785
11874
|
const embeddingEdges = await embeddingSimilarityEdges(rootDir, structuralGraph).catch(() => []);
|
|
11786
11875
|
const baseGraph = embeddingEdges.length > 0 ? (() => {
|
|
11787
11876
|
const edges = uniqueBy([...structuralGraph.edges, ...embeddingEdges], (edge) => edge.id).sort(
|
|
@@ -11795,7 +11884,13 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
11795
11884
|
communities: metrics.communities
|
|
11796
11885
|
};
|
|
11797
11886
|
})() : structuralGraph;
|
|
11798
|
-
const graphOrientation = await buildGraphOrientationPages(
|
|
11887
|
+
const graphOrientation = await buildGraphOrientationPages(
|
|
11888
|
+
baseGraph,
|
|
11889
|
+
paths,
|
|
11890
|
+
globalSchemaHash,
|
|
11891
|
+
input.previousState?.generatedAt,
|
|
11892
|
+
contradictions
|
|
11893
|
+
);
|
|
11799
11894
|
records.push(...graphOrientation.records);
|
|
11800
11895
|
const allPages = [...basePages, ...graphOrientation.records.map((record) => record.page)];
|
|
11801
11896
|
const graph = {
|
|
@@ -12466,6 +12561,58 @@ function updateCandidateHistory(compileState, page, deleted = false) {
|
|
|
12466
12561
|
function sortGraphPages(pages) {
|
|
12467
12562
|
return [...pages].sort((left, right) => left.path.localeCompare(right.path) || left.title.localeCompare(right.title));
|
|
12468
12563
|
}
|
|
12564
|
+
function computeUnifiedDiff(current, staged, label) {
|
|
12565
|
+
const currentLines = current.split("\n");
|
|
12566
|
+
const stagedLines = staged.split("\n");
|
|
12567
|
+
const output = [`--- a/${label}`, `+++ b/${label}`];
|
|
12568
|
+
const n = currentLines.length;
|
|
12569
|
+
const m = stagedLines.length;
|
|
12570
|
+
const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0));
|
|
12571
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
12572
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
12573
|
+
dp[i2][j2] = currentLines[i2] === stagedLines[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
12574
|
+
}
|
|
12575
|
+
}
|
|
12576
|
+
let i = 0;
|
|
12577
|
+
let j = 0;
|
|
12578
|
+
while (i < n || j < m) {
|
|
12579
|
+
if (i < n && j < m && currentLines[i] === stagedLines[j]) {
|
|
12580
|
+
output.push(` ${currentLines[i]}`);
|
|
12581
|
+
i++;
|
|
12582
|
+
j++;
|
|
12583
|
+
} else if (j < m && (i >= n || dp[i][j + 1] >= dp[i + 1][j])) {
|
|
12584
|
+
output.push(`+${stagedLines[j]}`);
|
|
12585
|
+
j++;
|
|
12586
|
+
} else {
|
|
12587
|
+
output.push(`-${currentLines[i]}`);
|
|
12588
|
+
i++;
|
|
12589
|
+
}
|
|
12590
|
+
}
|
|
12591
|
+
return output.join("\n");
|
|
12592
|
+
}
|
|
12593
|
+
function computeChangeSummary(current, staged, changeType) {
|
|
12594
|
+
if (changeType === "create") return "New page";
|
|
12595
|
+
if (changeType === "delete") return "Removed page";
|
|
12596
|
+
if (changeType === "promote") return "Promoted from candidate";
|
|
12597
|
+
if (!current || !staged) return "Updated page";
|
|
12598
|
+
const currentParsed = matter9(current);
|
|
12599
|
+
const stagedParsed = matter9(staged);
|
|
12600
|
+
const changes = [];
|
|
12601
|
+
const currentTags = currentParsed.data.tags ?? [];
|
|
12602
|
+
const stagedTags = stagedParsed.data.tags ?? [];
|
|
12603
|
+
const addedTags = stagedTags.filter((t) => !currentTags.includes(t));
|
|
12604
|
+
const removedTags = currentTags.filter((t) => !stagedTags.includes(t));
|
|
12605
|
+
if (addedTags.length) changes.push(`added ${addedTags.length} tag(s)`);
|
|
12606
|
+
if (removedTags.length) changes.push(`removed ${removedTags.length} tag(s)`);
|
|
12607
|
+
if (currentParsed.data.title !== stagedParsed.data.title) changes.push("updated title");
|
|
12608
|
+
const currentLines = currentParsed.content.trim().split("\n").length;
|
|
12609
|
+
const stagedLines = stagedParsed.content.trim().split("\n").length;
|
|
12610
|
+
const lineDelta = stagedLines - currentLines;
|
|
12611
|
+
if (lineDelta > 0) changes.push(`added ${lineDelta} line(s)`);
|
|
12612
|
+
else if (lineDelta < 0) changes.push(`removed ${Math.abs(lineDelta)} line(s)`);
|
|
12613
|
+
else if (currentParsed.content !== stagedParsed.content) changes.push("modified content");
|
|
12614
|
+
return changes.length ? changes.join(", ") : "no visible changes";
|
|
12615
|
+
}
|
|
12469
12616
|
async function listApprovals(rootDir) {
|
|
12470
12617
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12471
12618
|
const manifests = await Promise.all(
|
|
@@ -12479,7 +12626,7 @@ async function listApprovals(rootDir) {
|
|
|
12479
12626
|
);
|
|
12480
12627
|
return manifests.filter((manifest) => Boolean(manifest)).map(approvalSummary).sort((left, right) => right.createdAt.localeCompare(left.createdAt));
|
|
12481
12628
|
}
|
|
12482
|
-
async function readApproval(rootDir, approvalId) {
|
|
12629
|
+
async function readApproval(rootDir, approvalId, options) {
|
|
12483
12630
|
const { paths } = await loadVaultConfig(rootDir);
|
|
12484
12631
|
const manifest = await readApprovalManifest(paths, approvalId);
|
|
12485
12632
|
const details = await Promise.all(
|
|
@@ -12487,11 +12634,16 @@ async function readApproval(rootDir, approvalId) {
|
|
|
12487
12634
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
12488
12635
|
const currentContent = currentPath ? await fs17.readFile(path21.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
12489
12636
|
const stagedContent = entry.nextPath ? await fs17.readFile(path21.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
12490
|
-
|
|
12637
|
+
const detail = {
|
|
12491
12638
|
...entry,
|
|
12492
12639
|
currentContent,
|
|
12493
12640
|
stagedContent
|
|
12494
12641
|
};
|
|
12642
|
+
detail.changeSummary = computeChangeSummary(detail.currentContent, detail.stagedContent, detail.changeType);
|
|
12643
|
+
if (options?.diff && detail.currentContent && detail.stagedContent) {
|
|
12644
|
+
detail.diff = computeUnifiedDiff(detail.currentContent, detail.stagedContent, detail.nextPath ?? detail.pageId);
|
|
12645
|
+
}
|
|
12646
|
+
return detail;
|
|
12495
12647
|
})
|
|
12496
12648
|
);
|
|
12497
12649
|
return {
|
|
@@ -13687,11 +13839,43 @@ async function lintVault(rootDir, options = {}) {
|
|
|
13687
13839
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13688
13840
|
success: true,
|
|
13689
13841
|
lintFindingCount: findings2.length,
|
|
13690
|
-
lines: [
|
|
13842
|
+
lines: [
|
|
13843
|
+
`findings=${findings2.length}`,
|
|
13844
|
+
`deep=${Boolean(options.deep)}`,
|
|
13845
|
+
`web=${Boolean(options.web)}`,
|
|
13846
|
+
`conflicts=${Boolean(options.conflicts)}`
|
|
13847
|
+
]
|
|
13691
13848
|
});
|
|
13692
13849
|
return findings2;
|
|
13693
13850
|
}
|
|
13851
|
+
const contradictionFindings = options.conflicts ? graph.edges.filter((edge) => edge.relation === "contradicts").map((edge) => {
|
|
13852
|
+
const sourceIdA = edge.provenance[0] ?? edge.source.replace(/^source:/, "");
|
|
13853
|
+
const sourceIdB = edge.provenance[1] ?? edge.target.replace(/^source:/, "");
|
|
13854
|
+
return {
|
|
13855
|
+
severity: "warning",
|
|
13856
|
+
code: "contradiction",
|
|
13857
|
+
message: `Contradicting claims detected between source "${sourceIdA}" and source "${sourceIdB}".`,
|
|
13858
|
+
relatedSourceIds: [sourceIdA, sourceIdB]
|
|
13859
|
+
};
|
|
13860
|
+
}) : [];
|
|
13861
|
+
if (options.conflicts && !options.deep) {
|
|
13862
|
+
await recordSession(rootDir, {
|
|
13863
|
+
operation: "lint",
|
|
13864
|
+
title: `Linted ${graph.pages.length} page(s)`,
|
|
13865
|
+
startedAt,
|
|
13866
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13867
|
+
success: true,
|
|
13868
|
+
relatedPageIds: graph.pages.map((page) => page.id),
|
|
13869
|
+
relatedSourceIds: uniqueStrings3(graph.pages.flatMap((page) => page.sourceIds)),
|
|
13870
|
+
lintFindingCount: contradictionFindings.length,
|
|
13871
|
+
lines: [`findings=${contradictionFindings.length}`, `deep=false`, `web=false`, `conflicts=true`]
|
|
13872
|
+
});
|
|
13873
|
+
return contradictionFindings;
|
|
13874
|
+
}
|
|
13694
13875
|
const findings = await structuralLintFindings(rootDir, paths, graph, schemas, manifests, sourceProjects);
|
|
13876
|
+
if (options.conflicts) {
|
|
13877
|
+
findings.push(...contradictionFindings);
|
|
13878
|
+
}
|
|
13695
13879
|
if (options.deep) {
|
|
13696
13880
|
findings.push(...await runDeepLint(rootDir, findings, { web: options.web }));
|
|
13697
13881
|
}
|
|
@@ -13706,7 +13890,12 @@ async function lintVault(rootDir, options = {}) {
|
|
|
13706
13890
|
relatedPageIds: graph.pages.map((page) => page.id),
|
|
13707
13891
|
relatedSourceIds: uniqueStrings3(graph.pages.flatMap((page) => page.sourceIds)),
|
|
13708
13892
|
lintFindingCount: findings.length,
|
|
13709
|
-
lines: [
|
|
13893
|
+
lines: [
|
|
13894
|
+
`findings=${findings.length}`,
|
|
13895
|
+
`deep=${Boolean(options.deep)}`,
|
|
13896
|
+
`web=${Boolean(options.web)}`,
|
|
13897
|
+
`conflicts=${Boolean(options.conflicts)}`
|
|
13898
|
+
]
|
|
13710
13899
|
});
|
|
13711
13900
|
return findings;
|
|
13712
13901
|
}
|
|
@@ -13724,7 +13913,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
13724
13913
|
}
|
|
13725
13914
|
|
|
13726
13915
|
// src/mcp.ts
|
|
13727
|
-
var SERVER_VERSION = "0.1.
|
|
13916
|
+
var SERVER_VERSION = "0.1.32";
|
|
13728
13917
|
async function createMcpServer(rootDir) {
|
|
13729
13918
|
const server = new McpServer({
|
|
13730
13919
|
name: "swarmvault",
|
|
@@ -14554,6 +14743,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14554
14743
|
let consecutiveFailures = 0;
|
|
14555
14744
|
let currentDebounceMs = baseDebounceMs;
|
|
14556
14745
|
const reasons = /* @__PURE__ */ new Set();
|
|
14746
|
+
let activeCycle = null;
|
|
14557
14747
|
const watcher = chokidar.watch(watchTargets, {
|
|
14558
14748
|
ignoreInitial: true,
|
|
14559
14749
|
usePolling: true,
|
|
@@ -14598,7 +14788,12 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14598
14788
|
clearTimeout(timer);
|
|
14599
14789
|
}
|
|
14600
14790
|
timer = setTimeout(() => {
|
|
14601
|
-
|
|
14791
|
+
const cycle = runCycle();
|
|
14792
|
+
activeCycle = cycle.finally(() => {
|
|
14793
|
+
if (activeCycle === cycle) {
|
|
14794
|
+
activeCycle = null;
|
|
14795
|
+
}
|
|
14796
|
+
});
|
|
14602
14797
|
}, currentDebounceMs);
|
|
14603
14798
|
};
|
|
14604
14799
|
const runCycle = async () => {
|
|
@@ -14766,6 +14961,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14766
14961
|
clearTimeout(timer);
|
|
14767
14962
|
}
|
|
14768
14963
|
await watcher.close();
|
|
14964
|
+
await activeCycle;
|
|
14769
14965
|
}
|
|
14770
14966
|
};
|
|
14771
14967
|
}
|