sdtk-wiki-kit 0.1.0 → 0.1.2
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 +186 -13
- package/assets/atlas/build_atlas.py +164 -79
- package/package.json +1 -1
- package/src/commands/help.js +38 -3
- package/src/commands/lint.js +2 -1
- package/src/commands/operations.js +345 -0
- package/src/commands/search.js +89 -0
- package/src/commands/wiki.js +83 -9
- package/src/index.js +35 -1
- package/src/lib/wiki-compile.js +694 -6
- package/src/lib/wiki-extract.js +637 -0
- package/src/lib/wiki-flags.js +8 -0
- package/src/lib/wiki-lint.js +179 -2
- package/src/lib/wiki-search.js +175 -0
package/src/lib/wiki-compile.js
CHANGED
|
@@ -12,6 +12,11 @@ const {
|
|
|
12
12
|
} = require("./wiki-paths");
|
|
13
13
|
|
|
14
14
|
const REPORT_PREFIX = "compile-dry-run-preview";
|
|
15
|
+
const APPLY_PLAN_PREFIX = "compile-apply-plan";
|
|
16
|
+
const APPLY_PLAN_RECORD_TYPE = "sdtk_wiki_compile_apply_plan";
|
|
17
|
+
const APPLY_PLAN_SCHEMA_VERSION = 1;
|
|
18
|
+
const PERSONAL_BRAIN_RELATIVE = path.join(".sdtk", "wiki", "personal-brain");
|
|
19
|
+
const APPLY_MODE = "create_only_or_same_content_noop";
|
|
15
20
|
const ALLOWED_OPERATION_TYPES = new Set([
|
|
16
21
|
"append_section",
|
|
17
22
|
"create_page",
|
|
@@ -27,6 +32,14 @@ function asArray(value) {
|
|
|
27
32
|
return Array.isArray(value) ? value : [];
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
function toPosix(value) {
|
|
36
|
+
return String(value || "").replace(/\\/g, "/");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function stableHash(value) {
|
|
40
|
+
return require("crypto").createHash("sha256").update(String(value)).digest("hex");
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
function isRemoteUrl(value) {
|
|
31
44
|
return /^(?:https?|ftp):\/\//i.test(String(value || ""));
|
|
32
45
|
}
|
|
@@ -59,6 +72,430 @@ function ensureExistingWorkspace(projectPath) {
|
|
|
59
72
|
return workspacePath;
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
function firstSourceRef(record) {
|
|
76
|
+
const refs = asArray(record.source_refs || record.sourceRefs);
|
|
77
|
+
return refs.length > 0 ? String(refs[0]) : "semantic_extraction_report";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function sourceHashFor(sourceById, sourceId, fallback = "") {
|
|
81
|
+
const source = sourceById.get(sourceId);
|
|
82
|
+
return source && source.source_hash ? String(source.source_hash) : fallback;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function refsFor(record) {
|
|
86
|
+
return [
|
|
87
|
+
...asArray(record.source_refs || record.sourceRefs),
|
|
88
|
+
...asArray(record.provenance_refs || record.provenanceRefs),
|
|
89
|
+
record.source_logical_path,
|
|
90
|
+
record.source_relative_path,
|
|
91
|
+
].filter(Boolean).map(String);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function sourceRefsFor(record) {
|
|
95
|
+
const refs = asArray(record.source_refs || record.sourceRefs);
|
|
96
|
+
if (record.source_id) refs.unshift(record.source_id);
|
|
97
|
+
return [...new Set(refs.filter(Boolean).map(String))];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function provenanceRefsFor(record) {
|
|
101
|
+
return [...new Set(asArray(record.provenance_refs || record.provenanceRefs).filter(Boolean).map(String))];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function mdList(values) {
|
|
105
|
+
const items = asArray(values).filter(Boolean).map(String);
|
|
106
|
+
return items.length > 0 ? items.map((item) => `- ${item}`).join("\n") : "- None recorded.";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function recordTitle(record, fallback) {
|
|
110
|
+
return String(record.title || record.name || record.topic || record.entity_id || record.concept_id || record.source_id || fallback || "Generated page");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function renderSourcePage(source) {
|
|
114
|
+
return [
|
|
115
|
+
`# ${recordTitle(source, "Source")}`,
|
|
116
|
+
"",
|
|
117
|
+
"## Summary",
|
|
118
|
+
"",
|
|
119
|
+
`Local source page generated from ${source.source_logical_path || source.source_relative_path || source.source_id}.`,
|
|
120
|
+
"",
|
|
121
|
+
"## Source Metadata",
|
|
122
|
+
"",
|
|
123
|
+
`- source_id: ${source.source_id}`,
|
|
124
|
+
`- source_relative_path: ${source.source_relative_path || "(missing)"}`,
|
|
125
|
+
`- source_logical_path: ${source.source_logical_path || "(missing)"}`,
|
|
126
|
+
`- source_url: ${source.source_url || "(missing)"}`,
|
|
127
|
+
`- source_hash: ${source.source_hash || "(missing)"}`,
|
|
128
|
+
`- encoding_quality: ${source.encoding_quality || "(unknown)"}`,
|
|
129
|
+
"",
|
|
130
|
+
"## Source Quality",
|
|
131
|
+
"",
|
|
132
|
+
mdList(source.source_quality && source.source_quality.quality_flags),
|
|
133
|
+
"",
|
|
134
|
+
"## Provenance",
|
|
135
|
+
"",
|
|
136
|
+
mdList(provenanceRefsFor(source)),
|
|
137
|
+
"",
|
|
138
|
+
].join("\n");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function renderToolEntityPage(entity) {
|
|
142
|
+
return [
|
|
143
|
+
`# ${recordTitle(entity, "Tool entity")}`,
|
|
144
|
+
"",
|
|
145
|
+
"## Summary",
|
|
146
|
+
"",
|
|
147
|
+
entity.summary || `Generated tool entity page for ${entity.name || entity.entity_id}.`,
|
|
148
|
+
"",
|
|
149
|
+
"## Repository",
|
|
150
|
+
"",
|
|
151
|
+
`- owner: ${entity.repo_owner || "(missing)"}`,
|
|
152
|
+
`- repo: ${entity.repo_name || "(missing)"}`,
|
|
153
|
+
`- url: ${entity.github_url || "(missing)"}`,
|
|
154
|
+
`- category: ${entity.category || "(unknown)"}`,
|
|
155
|
+
"",
|
|
156
|
+
"## Evidence",
|
|
157
|
+
"",
|
|
158
|
+
mdList(sourceRefsFor(entity)),
|
|
159
|
+
"",
|
|
160
|
+
"## Provenance",
|
|
161
|
+
"",
|
|
162
|
+
mdList(provenanceRefsFor(entity)),
|
|
163
|
+
"",
|
|
164
|
+
].join("\n");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function renderConceptPage(concept) {
|
|
168
|
+
return [
|
|
169
|
+
`# ${recordTitle(concept, "Concept")}`,
|
|
170
|
+
"",
|
|
171
|
+
"## Definition",
|
|
172
|
+
"",
|
|
173
|
+
concept.definition || `Generated concept page for ${concept.name || concept.concept_id}.`,
|
|
174
|
+
"",
|
|
175
|
+
"## Aliases",
|
|
176
|
+
"",
|
|
177
|
+
mdList(concept.aliases),
|
|
178
|
+
"",
|
|
179
|
+
"## Related Entities",
|
|
180
|
+
"",
|
|
181
|
+
mdList(concept.related_entities),
|
|
182
|
+
"",
|
|
183
|
+
"## Sources",
|
|
184
|
+
"",
|
|
185
|
+
mdList(sourceRefsFor(concept)),
|
|
186
|
+
"",
|
|
187
|
+
].join("\n");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function renderComparisonPage(comparison) {
|
|
191
|
+
return [
|
|
192
|
+
`# ${recordTitle(comparison, "Comparison")}`,
|
|
193
|
+
"",
|
|
194
|
+
"## Compared Entities",
|
|
195
|
+
"",
|
|
196
|
+
mdList(comparison.compared_entities),
|
|
197
|
+
"",
|
|
198
|
+
"## Criteria",
|
|
199
|
+
"",
|
|
200
|
+
mdList(comparison.criteria),
|
|
201
|
+
"",
|
|
202
|
+
"## Sources",
|
|
203
|
+
"",
|
|
204
|
+
mdList(sourceRefsFor(comparison)),
|
|
205
|
+
"",
|
|
206
|
+
].join("\n");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function renderSynthesisPage(synthesis) {
|
|
210
|
+
return [
|
|
211
|
+
`# ${recordTitle(synthesis, "Synthesis")}`,
|
|
212
|
+
"",
|
|
213
|
+
"## Summary",
|
|
214
|
+
"",
|
|
215
|
+
synthesis.summary || `Generated synthesis page for ${synthesis.topic || synthesis.synthesis_id}.`,
|
|
216
|
+
"",
|
|
217
|
+
"## Recommendations",
|
|
218
|
+
"",
|
|
219
|
+
mdList(synthesis.recommendations),
|
|
220
|
+
"",
|
|
221
|
+
"## Sources",
|
|
222
|
+
"",
|
|
223
|
+
mdList(sourceRefsFor(synthesis)),
|
|
224
|
+
"",
|
|
225
|
+
].join("\n");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function renderRootPage(name, payload) {
|
|
229
|
+
const extractionCounts = payload && payload.source_counts ? payload.source_counts : {};
|
|
230
|
+
const titles = {
|
|
231
|
+
index: "Personal Brain Index",
|
|
232
|
+
overview: "Personal Brain Overview",
|
|
233
|
+
ontology: "Personal Brain Ontology",
|
|
234
|
+
taxonomy: "Personal Brain Taxonomy",
|
|
235
|
+
graph: "Personal Brain Graph",
|
|
236
|
+
log: "Personal Brain Generation Log",
|
|
237
|
+
};
|
|
238
|
+
return [
|
|
239
|
+
`# ${titles[name] || "Personal Brain"}`,
|
|
240
|
+
"",
|
|
241
|
+
"## Generation Context",
|
|
242
|
+
"",
|
|
243
|
+
`- record_type: ${payload.record_type || "(unknown)"}`,
|
|
244
|
+
`- generated_at: ${payload.generated_at || "(unknown)"}`,
|
|
245
|
+
`- sources_scanned: ${extractionCounts.scanned ?? "(unknown)"}`,
|
|
246
|
+
`- sources_indexed: ${extractionCounts.indexed ?? "(unknown)"}`,
|
|
247
|
+
`- tool_candidates: ${asArray(payload.tool_entities).length}`,
|
|
248
|
+
`- concept_candidates: ${asArray(payload.concepts).length}`,
|
|
249
|
+
"",
|
|
250
|
+
"## Structure",
|
|
251
|
+
"",
|
|
252
|
+
"- sources/",
|
|
253
|
+
"- entities/tools/",
|
|
254
|
+
"- concepts/",
|
|
255
|
+
"- comparisons/",
|
|
256
|
+
"- syntheses/",
|
|
257
|
+
"- maintenance/",
|
|
258
|
+
"",
|
|
259
|
+
].join("\n");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function renderMaintenancePage(payload) {
|
|
263
|
+
return [
|
|
264
|
+
"# Extraction Quality Review",
|
|
265
|
+
"",
|
|
266
|
+
"## Source Quality Findings",
|
|
267
|
+
"",
|
|
268
|
+
mdList(asArray(payload.source_quality_findings).map((finding) => `${finding.finding_id || finding.source_id}: ${asArray(finding.quality_flags).join(", ") || "review required"}`)),
|
|
269
|
+
"",
|
|
270
|
+
"## Unsupported Items",
|
|
271
|
+
"",
|
|
272
|
+
mdList(asArray(payload.unsupported_items).map((item) => `${item.item_id || "unsupported"}: ${item.reason || item.raw_observation_summary || "review required"}`)),
|
|
273
|
+
"",
|
|
274
|
+
].join("\n");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function pushCreatePageOperation(operations, raw) {
|
|
278
|
+
if (!raw.target_page_path) return;
|
|
279
|
+
operations.push({
|
|
280
|
+
operation_id: raw.operation_id,
|
|
281
|
+
operation_type: "create_page",
|
|
282
|
+
source_id: raw.source_id || "semantic_extraction_report",
|
|
283
|
+
source_hash: raw.source_hash || "",
|
|
284
|
+
target_page_path: raw.target_page_path,
|
|
285
|
+
content: raw.content || "",
|
|
286
|
+
proposed_content_summary: raw.proposed_content_summary,
|
|
287
|
+
source_refs: raw.source_refs || [],
|
|
288
|
+
provenance_refs: raw.provenance_refs || [],
|
|
289
|
+
evidence_refs: raw.evidence_refs || [],
|
|
290
|
+
confidence: raw.confidence || "medium",
|
|
291
|
+
review_status: raw.review_status || "needs_review",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function operationsFromSemanticExtraction(payload) {
|
|
296
|
+
const sourceById = new Map(asArray(payload.sources).map((source) => [String(source.source_id), source]));
|
|
297
|
+
const operations = [];
|
|
298
|
+
const reportHash = stableHash(JSON.stringify({
|
|
299
|
+
generated_at: payload.generated_at,
|
|
300
|
+
source_counts: payload.source_counts,
|
|
301
|
+
})).slice(0, 12);
|
|
302
|
+
const rootSourceId = `semantic_extraction_${reportHash}`;
|
|
303
|
+
const rootSourceHash = reportHash;
|
|
304
|
+
|
|
305
|
+
for (const source of asArray(payload.sources)) {
|
|
306
|
+
pushCreatePageOperation(operations, {
|
|
307
|
+
operation_id: `pb-source-${String(operations.length + 1).padStart(3, "0")}`,
|
|
308
|
+
source_id: source.source_id,
|
|
309
|
+
source_hash: source.source_hash,
|
|
310
|
+
target_page_path: source.target_page_path,
|
|
311
|
+
content: renderSourcePage(source),
|
|
312
|
+
proposed_content_summary: `Create personal-brain source page for ${source.source_logical_path || source.source_relative_path || source.source_id}.`,
|
|
313
|
+
source_refs: sourceRefsFor(source),
|
|
314
|
+
provenance_refs: provenanceRefsFor(source),
|
|
315
|
+
evidence_refs: refsFor(source),
|
|
316
|
+
confidence: source.source_quality && source.source_quality.low_confidence_extraction ? "low" : "medium",
|
|
317
|
+
review_status: source.source_quality && source.source_quality.low_confidence_extraction ? "needs_review" : "ready_for_review",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (const entity of asArray(payload.tool_entities)) {
|
|
322
|
+
const sourceId = firstSourceRef(entity);
|
|
323
|
+
pushCreatePageOperation(operations, {
|
|
324
|
+
operation_id: `pb-tool-${String(operations.length + 1).padStart(3, "0")}`,
|
|
325
|
+
source_id: sourceId,
|
|
326
|
+
source_hash: sourceHashFor(sourceById, sourceId),
|
|
327
|
+
target_page_path: entity.target_page_path,
|
|
328
|
+
content: renderToolEntityPage(entity),
|
|
329
|
+
proposed_content_summary: `Create tool entity page for ${entity.name || entity.entity_id}.`,
|
|
330
|
+
source_refs: sourceRefsFor(entity),
|
|
331
|
+
provenance_refs: provenanceRefsFor(entity),
|
|
332
|
+
evidence_refs: refsFor(entity),
|
|
333
|
+
confidence: entity.confidence_tier || entity.confidence || "medium",
|
|
334
|
+
review_status: "ready_for_review",
|
|
335
|
+
});
|
|
336
|
+
operations.push({
|
|
337
|
+
operation_id: `pb-tool-source-ref-${String(operations.length + 1).padStart(3, "0")}`,
|
|
338
|
+
operation_type: "add_source_ref",
|
|
339
|
+
source_id: sourceId,
|
|
340
|
+
source_hash: sourceHashFor(sourceById, sourceId),
|
|
341
|
+
target_page_path: entity.target_page_path,
|
|
342
|
+
content_sections: [
|
|
343
|
+
{
|
|
344
|
+
title: "Source References",
|
|
345
|
+
body: mdList(sourceRefsFor(entity)),
|
|
346
|
+
},
|
|
347
|
+
],
|
|
348
|
+
proposed_content_summary: `Add source references for tool entity ${entity.name || entity.entity_id}.`,
|
|
349
|
+
source_refs: sourceRefsFor(entity),
|
|
350
|
+
provenance_refs: provenanceRefsFor(entity),
|
|
351
|
+
evidence_refs: refsFor(entity),
|
|
352
|
+
confidence: entity.confidence_tier || entity.confidence || "medium",
|
|
353
|
+
review_status: "ready_for_review",
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
for (const concept of asArray(payload.concepts)) {
|
|
358
|
+
const sourceId = firstSourceRef(concept);
|
|
359
|
+
pushCreatePageOperation(operations, {
|
|
360
|
+
operation_id: `pb-concept-${String(operations.length + 1).padStart(3, "0")}`,
|
|
361
|
+
source_id: sourceId,
|
|
362
|
+
source_hash: sourceHashFor(sourceById, sourceId),
|
|
363
|
+
target_page_path: concept.target_page_path,
|
|
364
|
+
content: renderConceptPage(concept),
|
|
365
|
+
proposed_content_summary: `Create concept page for ${concept.name || concept.concept_id}.`,
|
|
366
|
+
source_refs: sourceRefsFor(concept),
|
|
367
|
+
provenance_refs: provenanceRefsFor(concept),
|
|
368
|
+
evidence_refs: refsFor(concept),
|
|
369
|
+
confidence: concept.confidence_tier || concept.confidence || "medium",
|
|
370
|
+
review_status: "ready_for_review",
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
for (const comparison of asArray(payload.comparisons)) {
|
|
375
|
+
const sourceId = firstSourceRef(comparison);
|
|
376
|
+
pushCreatePageOperation(operations, {
|
|
377
|
+
operation_id: `pb-comparison-${String(operations.length + 1).padStart(3, "0")}`,
|
|
378
|
+
source_id: sourceId,
|
|
379
|
+
source_hash: sourceHashFor(sourceById, sourceId),
|
|
380
|
+
target_page_path: comparison.target_page_path,
|
|
381
|
+
content: renderComparisonPage(comparison),
|
|
382
|
+
proposed_content_summary: `Create comparison page for ${comparison.topic || comparison.comparison_id}.`,
|
|
383
|
+
source_refs: sourceRefsFor(comparison),
|
|
384
|
+
provenance_refs: provenanceRefsFor(comparison),
|
|
385
|
+
evidence_refs: refsFor(comparison),
|
|
386
|
+
confidence: comparison.confidence_tier || comparison.confidence || "medium",
|
|
387
|
+
review_status: "needs_review",
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
for (const synthesis of asArray(payload.syntheses)) {
|
|
392
|
+
const sourceId = firstSourceRef(synthesis);
|
|
393
|
+
pushCreatePageOperation(operations, {
|
|
394
|
+
operation_id: `pb-synthesis-${String(operations.length + 1).padStart(3, "0")}`,
|
|
395
|
+
source_id: sourceId,
|
|
396
|
+
source_hash: sourceHashFor(sourceById, sourceId),
|
|
397
|
+
target_page_path: synthesis.target_page_path,
|
|
398
|
+
content: renderSynthesisPage(synthesis),
|
|
399
|
+
proposed_content_summary: `Create synthesis page for ${synthesis.topic || synthesis.synthesis_id}.`,
|
|
400
|
+
source_refs: sourceRefsFor(synthesis),
|
|
401
|
+
provenance_refs: provenanceRefsFor(synthesis),
|
|
402
|
+
evidence_refs: refsFor(synthesis),
|
|
403
|
+
confidence: synthesis.confidence_tier || synthesis.confidence || "medium",
|
|
404
|
+
review_status: "needs_review",
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const rootPages = [
|
|
409
|
+
["index", ".sdtk/wiki/personal-brain/index.md", "Create personal-brain index from semantic extraction output."],
|
|
410
|
+
["overview", ".sdtk/wiki/personal-brain/overview.md", "Create personal-brain overview summarizing extracted local sources."],
|
|
411
|
+
["ontology", ".sdtk/wiki/personal-brain/ontology.md", "Create ontology page describing source, tool_entity, concept, claim, relation, comparison, and synthesis records."],
|
|
412
|
+
["taxonomy", ".sdtk/wiki/personal-brain/taxonomy.md", "Create taxonomy page for extracted categories and concepts."],
|
|
413
|
+
["graph", ".sdtk/wiki/personal-brain/graph.md", "Create semantic graph summary page from extracted relations."],
|
|
414
|
+
["log", ".sdtk/wiki/personal-brain/log.md", "Create generation log page for extraction and compile preview evidence."],
|
|
415
|
+
];
|
|
416
|
+
for (const [name, target, summary] of rootPages) {
|
|
417
|
+
pushCreatePageOperation(operations, {
|
|
418
|
+
operation_id: `pb-root-${name}`,
|
|
419
|
+
source_id: rootSourceId,
|
|
420
|
+
source_hash: rootSourceHash,
|
|
421
|
+
target_page_path: target,
|
|
422
|
+
content: renderRootPage(name, payload),
|
|
423
|
+
proposed_content_summary: summary,
|
|
424
|
+
evidence_refs: [`record_type:${payload.record_type}`, `generated_at:${payload.generated_at || "(missing)"}`],
|
|
425
|
+
confidence: "medium",
|
|
426
|
+
review_status: "needs_review",
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
for (const relation of asArray(payload.relations)) {
|
|
431
|
+
operations.push({
|
|
432
|
+
operation_id: `pb-relation-${String(operations.length + 1).padStart(3, "0")}`,
|
|
433
|
+
operation_type: "add_relation",
|
|
434
|
+
source_id: relation.source_id || rootSourceId,
|
|
435
|
+
source_hash: rootSourceHash,
|
|
436
|
+
target_page_path: ".sdtk/wiki/personal-brain/graph.md",
|
|
437
|
+
content_sections: [
|
|
438
|
+
{
|
|
439
|
+
title: `Relation: ${relation.relation_type || relation.relation_id}`,
|
|
440
|
+
body: [
|
|
441
|
+
`- source_id: ${relation.source_id || "(missing)"}`,
|
|
442
|
+
`- target_id: ${relation.target_id || "(missing)"}`,
|
|
443
|
+
`- relation_type: ${relation.relation_type || "(missing)"}`,
|
|
444
|
+
`- evidence: ${relation.evidence || "(missing)"}`,
|
|
445
|
+
`- source_refs: ${sourceRefsFor(relation).join(", ") || "(missing)"}`,
|
|
446
|
+
`- provenance_refs: ${provenanceRefsFor(relation).join(", ") || "(missing)"}`,
|
|
447
|
+
].join("\n"),
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
proposed_content_summary: `Preview semantic relation ${relation.relation_type || relation.relation_id}.`,
|
|
451
|
+
source_refs: sourceRefsFor(relation),
|
|
452
|
+
provenance_refs: provenanceRefsFor(relation),
|
|
453
|
+
evidence_refs: refsFor(relation),
|
|
454
|
+
confidence: relation.confidence_tier || relation.confidence || "medium",
|
|
455
|
+
review_status: "ready_for_review",
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const qualityFindingCount = asArray(payload.source_quality_findings).length;
|
|
460
|
+
const unsupportedCount = asArray(payload.unsupported_items).length;
|
|
461
|
+
if (qualityFindingCount > 0 || unsupportedCount > 0) {
|
|
462
|
+
pushCreatePageOperation(operations, {
|
|
463
|
+
operation_id: "pb-maintenance-extraction-quality-review",
|
|
464
|
+
source_id: rootSourceId,
|
|
465
|
+
source_hash: rootSourceHash,
|
|
466
|
+
target_page_path: ".sdtk/wiki/personal-brain/maintenance/extraction-quality-review.md",
|
|
467
|
+
content: renderMaintenancePage(payload),
|
|
468
|
+
proposed_content_summary: `Create maintenance review page for ${qualityFindingCount} source-quality finding(s) and ${unsupportedCount} unsupported extraction item(s).`,
|
|
469
|
+
evidence_refs: [`source_quality_findings:${qualityFindingCount}`, `unsupported_items:${unsupportedCount}`],
|
|
470
|
+
confidence: unsupportedCount > 0 ? "low" : "medium",
|
|
471
|
+
review_status: "needs_review",
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return operations;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function emptySourceQualitySummary() {
|
|
479
|
+
return {
|
|
480
|
+
findings_count: 0,
|
|
481
|
+
unsupported_count: 0,
|
|
482
|
+
low_confidence_count: 0,
|
|
483
|
+
warning_count: 0,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function sourceQualitySummaryFromExtraction(payload) {
|
|
488
|
+
const findings = asArray(payload.source_quality_findings);
|
|
489
|
+
const unsupported = asArray(payload.unsupported_items);
|
|
490
|
+
const lowConfidence = findings.filter((finding) => ["low", "unsupported"].includes(String(finding.confidence_tier || finding.confidence || "").toLowerCase())).length;
|
|
491
|
+
return {
|
|
492
|
+
findings_count: findings.length,
|
|
493
|
+
unsupported_count: unsupported.length,
|
|
494
|
+
low_confidence_count: lowConfidence,
|
|
495
|
+
warning_count: findings.length + unsupported.length,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
62
499
|
function parseJsonPlan(planPath) {
|
|
63
500
|
let payload;
|
|
64
501
|
try {
|
|
@@ -66,10 +503,25 @@ function parseJsonPlan(planPath) {
|
|
|
66
503
|
} catch (error) {
|
|
67
504
|
throw new ValidationError(`Invalid JSON compile plan: ${error.message}. No project files were changed.`);
|
|
68
505
|
}
|
|
506
|
+
if (payload && payload.record_type === APPLY_PLAN_RECORD_TYPE) {
|
|
507
|
+
return {
|
|
508
|
+
operations: asArray(payload.operations),
|
|
509
|
+
sourceQualitySummary: payload.source_quality_summary || emptySourceQualitySummary(),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if (payload && payload.record_type === "sdtk_wiki_semantic_extraction") {
|
|
513
|
+
return {
|
|
514
|
+
operations: operationsFromSemanticExtraction(payload),
|
|
515
|
+
sourceQualitySummary: sourceQualitySummaryFromExtraction(payload),
|
|
516
|
+
};
|
|
517
|
+
}
|
|
69
518
|
if (!payload || !Array.isArray(payload.operations)) {
|
|
70
519
|
throw new ValidationError("Invalid compile plan: JSON plan must contain an operations array. No project files were changed.");
|
|
71
520
|
}
|
|
72
|
-
return
|
|
521
|
+
return {
|
|
522
|
+
operations: payload.operations,
|
|
523
|
+
sourceQualitySummary: payload.source_quality_summary || emptySourceQualitySummary(),
|
|
524
|
+
};
|
|
73
525
|
}
|
|
74
526
|
|
|
75
527
|
function parseMarkdownPlan(planPath) {
|
|
@@ -104,7 +556,10 @@ function parseMarkdownPlan(planPath) {
|
|
|
104
556
|
"Invalid markdown compile plan: expected structured operation blocks with operation_type. No project files were changed."
|
|
105
557
|
);
|
|
106
558
|
}
|
|
107
|
-
return
|
|
559
|
+
return {
|
|
560
|
+
operations,
|
|
561
|
+
sourceQualitySummary: emptySourceQualitySummary(),
|
|
562
|
+
};
|
|
108
563
|
}
|
|
109
564
|
|
|
110
565
|
function parsePlanOperations(planPath) {
|
|
@@ -141,9 +596,52 @@ function expectedMutationFor(operation) {
|
|
|
141
596
|
}
|
|
142
597
|
}
|
|
143
598
|
|
|
144
|
-
function
|
|
599
|
+
function renderContentSections(sections) {
|
|
600
|
+
return asArray(sections)
|
|
601
|
+
.map((section) => {
|
|
602
|
+
if (typeof section === "string") return section;
|
|
603
|
+
const title = section && section.title ? String(section.title) : "Generated Section";
|
|
604
|
+
const body = section && section.body ? String(section.body) : "";
|
|
605
|
+
return `## ${title}\n\n${body}`.trimEnd();
|
|
606
|
+
})
|
|
607
|
+
.filter(Boolean)
|
|
608
|
+
.join("\n\n");
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function fallbackOperationContent(operation) {
|
|
612
|
+
const title = operation.target_page_id || path.basename(operation.target_page_path || "generated-page.md", path.extname(operation.target_page_path || ""));
|
|
613
|
+
return [
|
|
614
|
+
`# ${title}`,
|
|
615
|
+
"",
|
|
616
|
+
"## Generated Operation",
|
|
617
|
+
"",
|
|
618
|
+
operation.proposed_content_summary || "Generated personal-brain content.",
|
|
619
|
+
"",
|
|
620
|
+
"## Evidence",
|
|
621
|
+
"",
|
|
622
|
+
mdList(operation.evidence_refs),
|
|
623
|
+
"",
|
|
624
|
+
].join("\n");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function normalizeTargetPath(rawTargetPath, projectPath) {
|
|
628
|
+
const personalBrainRoot = path.join(getWikiWorkspacePath(projectPath), "personal-brain");
|
|
629
|
+
const targetValue = String(rawTargetPath || "");
|
|
630
|
+
const resolvedTarget = targetValue ? path.resolve(projectPath, targetValue) : "";
|
|
631
|
+
const withinProject = Boolean(resolvedTarget) && isPathInsideOrEqual(resolvedTarget, projectPath);
|
|
632
|
+
const withinPersonalBrain = Boolean(resolvedTarget) && isPathInsideOrEqual(resolvedTarget, personalBrainRoot);
|
|
633
|
+
return {
|
|
634
|
+
resolvedTarget,
|
|
635
|
+
targetPathNormalized: withinProject ? toPosix(path.relative(projectPath, resolvedTarget)) : toPosix(resolvedTarget),
|
|
636
|
+
withinProject,
|
|
637
|
+
withinPersonalBrain,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function normalizeOperation(rawOperation, index, projectPath) {
|
|
145
642
|
const operationType = String(rawOperation.operation_type || rawOperation.operationType || "").trim();
|
|
146
643
|
const isSupported = ALLOWED_OPERATION_TYPES.has(operationType);
|
|
644
|
+
const target = normalizeTargetPath(rawOperation.target_page_path || rawOperation.targetPagePath || "", projectPath);
|
|
147
645
|
const operation = {
|
|
148
646
|
operation_id: String(rawOperation.operation_id || rawOperation.operationId || `operation-${String(index + 1).padStart(3, "0")}`),
|
|
149
647
|
source_id: String(rawOperation.source_id || rawOperation.sourceId || "unknown_source"),
|
|
@@ -151,14 +649,28 @@ function normalizeOperation(rawOperation, index) {
|
|
|
151
649
|
revision: String(rawOperation.revision || ""),
|
|
152
650
|
target_page_id: String(rawOperation.target_page_id || rawOperation.targetPageId || ""),
|
|
153
651
|
target_page_path: String(rawOperation.target_page_path || rawOperation.targetPagePath || ""),
|
|
652
|
+
target_path_normalized: target.targetPathNormalized,
|
|
653
|
+
target_path_within_project: target.withinProject,
|
|
654
|
+
target_path_within_personal_brain: target.withinPersonalBrain,
|
|
154
655
|
operation_type: operationType || "unsupported_operation",
|
|
155
656
|
requested_operation_type: operationType || "(missing)",
|
|
156
657
|
proposed_content_summary: String(rawOperation.proposed_content_summary || rawOperation.proposedContentSummary || ""),
|
|
658
|
+
content: String(rawOperation.content || ""),
|
|
659
|
+
content_sections: asArray(rawOperation.content_sections || rawOperation.contentSections),
|
|
660
|
+
source_refs: splitRefs(rawOperation.source_refs || rawOperation.sourceRefs || rawOperation.source_id || rawOperation.sourceId),
|
|
661
|
+
provenance_refs: splitRefs(rawOperation.provenance_refs || rawOperation.provenanceRefs),
|
|
157
662
|
evidence_refs: splitRefs(rawOperation.evidence_refs || rawOperation.evidenceRefs),
|
|
158
663
|
confidence: String(rawOperation.confidence || "unknown"),
|
|
159
664
|
review_status: String(rawOperation.review_status || rawOperation.reviewStatus || "needs_review"),
|
|
160
665
|
validation_status: isSupported ? "supported" : "unsupported_operation",
|
|
666
|
+
apply_mode: rawOperation.apply_mode || rawOperation.applyMode || APPLY_MODE,
|
|
161
667
|
};
|
|
668
|
+
if (!operation.content && operation.content_sections.length > 0) {
|
|
669
|
+
operation.content = renderContentSections(operation.content_sections);
|
|
670
|
+
}
|
|
671
|
+
if (!operation.content) {
|
|
672
|
+
operation.content = fallbackOperationContent(operation);
|
|
673
|
+
}
|
|
162
674
|
operation.expected_mutation_if_applied = expectedMutationFor(operation);
|
|
163
675
|
if (!isSupported) {
|
|
164
676
|
operation.operation_type = "unsupported_operation";
|
|
@@ -175,7 +687,7 @@ function summarizeOperations(operations) {
|
|
|
175
687
|
return counts;
|
|
176
688
|
}
|
|
177
689
|
|
|
178
|
-
function renderReport({ projectPath, workspacePath, planPath, generatedAt, operations }) {
|
|
690
|
+
function renderReport({ projectPath, workspacePath, planPath, applyPlanPath, generatedAt, operations }) {
|
|
179
691
|
const summary = summarizeOperations(operations);
|
|
180
692
|
const sortedTypes = Array.from(new Set([...Array.from(ALLOWED_OPERATION_TYPES), "unsupported_operation"]));
|
|
181
693
|
const unsupported = operations.filter((operation) => operation.validation_status === "unsupported_operation");
|
|
@@ -186,6 +698,7 @@ function renderReport({ projectPath, workspacePath, planPath, generatedAt, opera
|
|
|
186
698
|
`Project path: ${projectPath}`,
|
|
187
699
|
`Workspace path: ${workspacePath}`,
|
|
188
700
|
`Input plan path: ${planPath}`,
|
|
701
|
+
`Apply JSON sidecar path: ${applyPlanPath}`,
|
|
189
702
|
`Operations: ${operations.length}`,
|
|
190
703
|
"",
|
|
191
704
|
"No wiki pages, raw sources, provenance, or atlas compatibility files were modified.",
|
|
@@ -238,6 +751,161 @@ function renderReport({ projectPath, workspacePath, planPath, generatedAt, opera
|
|
|
238
751
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
239
752
|
}
|
|
240
753
|
|
|
754
|
+
function renderApplyPlan({ projectPath, planPath, reportPath, generatedAt, operations, sourceQualitySummary }) {
|
|
755
|
+
return {
|
|
756
|
+
schema_version: APPLY_PLAN_SCHEMA_VERSION,
|
|
757
|
+
record_type: APPLY_PLAN_RECORD_TYPE,
|
|
758
|
+
generated_at: generatedAt,
|
|
759
|
+
project_path: projectPath,
|
|
760
|
+
source_plan_path: planPath,
|
|
761
|
+
dry_run_report_path: reportPath,
|
|
762
|
+
operation_counts: summarizeOperations(operations),
|
|
763
|
+
source_quality_summary: sourceQualitySummary || emptySourceQualitySummary(),
|
|
764
|
+
operations: operations.map((operation) => ({
|
|
765
|
+
operation_id: operation.operation_id,
|
|
766
|
+
operation_type: operation.operation_type,
|
|
767
|
+
target_page_path: operation.target_page_path,
|
|
768
|
+
target_path_normalized: operation.target_path_normalized,
|
|
769
|
+
target_path_within_project: operation.target_path_within_project,
|
|
770
|
+
target_path_within_personal_brain: operation.target_path_within_personal_brain,
|
|
771
|
+
content: operation.content,
|
|
772
|
+
content_sections: operation.content_sections,
|
|
773
|
+
source_refs: operation.source_refs,
|
|
774
|
+
provenance_refs: operation.provenance_refs,
|
|
775
|
+
evidence_refs: operation.evidence_refs,
|
|
776
|
+
confidence: operation.confidence,
|
|
777
|
+
review_status: operation.review_status,
|
|
778
|
+
validation_status: operation.validation_status,
|
|
779
|
+
expected_mutation_if_applied: operation.expected_mutation_if_applied,
|
|
780
|
+
apply_mode: APPLY_MODE,
|
|
781
|
+
})),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function ensureTrailingNewline(value) {
|
|
786
|
+
const text = String(value || "").replace(/\r\n/g, "\n").trimEnd();
|
|
787
|
+
return `${text}\n`;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function loadApplyPlan(planPath) {
|
|
791
|
+
if (path.extname(planPath).toLowerCase() !== ".json") {
|
|
792
|
+
throw new ValidationError("Apply requires a compile apply JSON sidecar. Markdown plans are rejected for apply. No project files were changed.");
|
|
793
|
+
}
|
|
794
|
+
let payload;
|
|
795
|
+
try {
|
|
796
|
+
payload = JSON.parse(fs.readFileSync(planPath, "utf-8"));
|
|
797
|
+
} catch (error) {
|
|
798
|
+
throw new ValidationError(`Invalid compile apply JSON sidecar: ${error.message}. No project files were changed.`);
|
|
799
|
+
}
|
|
800
|
+
if (payload && payload.record_type === "sdtk_wiki_semantic_extraction") {
|
|
801
|
+
throw new ValidationError("Apply rejects semantic extraction JSON. Run compile --dry-run first and apply the generated compile apply JSON sidecar. No project files were changed.");
|
|
802
|
+
}
|
|
803
|
+
if (!payload || payload.record_type !== APPLY_PLAN_RECORD_TYPE) {
|
|
804
|
+
throw new ValidationError(`Apply requires record_type ${APPLY_PLAN_RECORD_TYPE}. No project files were changed.`);
|
|
805
|
+
}
|
|
806
|
+
if (!Array.isArray(payload.operations)) {
|
|
807
|
+
throw new ValidationError("Compile apply JSON sidecar must contain operations[]. No project files were changed.");
|
|
808
|
+
}
|
|
809
|
+
return payload;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
function validateApplyOperations(operations) {
|
|
813
|
+
const blocked = operations.filter((operation) => (
|
|
814
|
+
operation.validation_status !== "supported" ||
|
|
815
|
+
operation.operation_type === "unsupported_operation" ||
|
|
816
|
+
!ALLOWED_OPERATION_TYPES.has(operation.operation_type) ||
|
|
817
|
+
operation.apply_mode !== APPLY_MODE ||
|
|
818
|
+
!operation.target_path_within_project ||
|
|
819
|
+
!operation.target_path_within_personal_brain
|
|
820
|
+
));
|
|
821
|
+
if (blocked.length > 0) {
|
|
822
|
+
throw new ValidationError(
|
|
823
|
+
`Compile apply plan contains ${blocked.length} blocked or unsafe operation(s). No project files were changed.`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function aggregateApplyTargets(operations, projectPath) {
|
|
829
|
+
const targets = new Map();
|
|
830
|
+
for (const operation of operations) {
|
|
831
|
+
const target = normalizeTargetPath(operation.target_page_path, projectPath);
|
|
832
|
+
if (!target.withinProject || !target.withinPersonalBrain) {
|
|
833
|
+
throw new ValidationError(`Unsafe personal-brain target path: ${operation.target_page_path}. No project files were changed.`);
|
|
834
|
+
}
|
|
835
|
+
const existing = targets.get(target.resolvedTarget) || {
|
|
836
|
+
path: target.resolvedTarget,
|
|
837
|
+
normalized: target.targetPathNormalized,
|
|
838
|
+
sections: [],
|
|
839
|
+
operationIds: [],
|
|
840
|
+
};
|
|
841
|
+
existing.operationIds.push(operation.operation_id);
|
|
842
|
+
existing.sections.push(ensureTrailingNewline(operation.content));
|
|
843
|
+
targets.set(target.resolvedTarget, existing);
|
|
844
|
+
}
|
|
845
|
+
for (const target of targets.values()) {
|
|
846
|
+
target.content = ensureTrailingNewline(target.sections.join("\n"));
|
|
847
|
+
}
|
|
848
|
+
return [...targets.values()].sort((a, b) => a.normalized.localeCompare(b.normalized));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function runWikiCompileApply({ projectPath, planArg }) {
|
|
852
|
+
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
853
|
+
if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
|
|
854
|
+
throw new ValidationError(`--project-path is not a valid directory: ${resolvedProjectPath}`);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
try {
|
|
858
|
+
ensureExistingWorkspace(resolvedProjectPath);
|
|
859
|
+
const planPath = resolvePlanPath(planArg, resolvedProjectPath);
|
|
860
|
+
const payload = loadApplyPlan(planPath);
|
|
861
|
+
const operations = payload.operations.map((operation, index) => normalizeOperation(operation, index, resolvedProjectPath));
|
|
862
|
+
validateApplyOperations(operations);
|
|
863
|
+
const targets = aggregateApplyTargets(operations, resolvedProjectPath);
|
|
864
|
+
const conflicts = [];
|
|
865
|
+
const unchanged = [];
|
|
866
|
+
const toCreate = [];
|
|
867
|
+
|
|
868
|
+
for (const target of targets) {
|
|
869
|
+
assertWikiWorkspaceWritePath(target.path, resolvedProjectPath);
|
|
870
|
+
if (!isPathInsideOrEqual(target.path, path.join(getWikiWorkspacePath(resolvedProjectPath), "personal-brain"))) {
|
|
871
|
+
throw new ValidationError(`Refusing to write outside .sdtk/wiki/personal-brain: ${target.normalized}. No project files were changed.`);
|
|
872
|
+
}
|
|
873
|
+
if (!fs.existsSync(target.path)) {
|
|
874
|
+
toCreate.push(target);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
const current = fs.readFileSync(target.path, "utf-8").replace(/\r\n/g, "\n");
|
|
878
|
+
if (current === target.content) {
|
|
879
|
+
unchanged.push(target.normalized);
|
|
880
|
+
} else {
|
|
881
|
+
conflicts.push(target.normalized);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (conflicts.length > 0) {
|
|
886
|
+
throw new ValidationError(`Compile apply would overwrite ${conflicts.length} existing different file(s): ${conflicts.join(", ")}. No project files were changed.`);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const created = [];
|
|
890
|
+
for (const target of toCreate) {
|
|
891
|
+
fs.mkdirSync(path.dirname(target.path), { recursive: true });
|
|
892
|
+
fs.writeFileSync(target.path, target.content, "utf-8");
|
|
893
|
+
created.push(target.normalized);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const sourceQualitySummary = payload.source_quality_summary || emptySourceQualitySummary();
|
|
897
|
+
return {
|
|
898
|
+
planPath,
|
|
899
|
+
created,
|
|
900
|
+
unchanged,
|
|
901
|
+
sourceQualityWarningCount: Number(sourceQualitySummary.warning_count || sourceQualitySummary.findings_count || 0),
|
|
902
|
+
};
|
|
903
|
+
} catch (error) {
|
|
904
|
+
if (error instanceof CliError) throw error;
|
|
905
|
+
throw new CliError(`Failed to apply SDTK-WIKI compile plan: ${error.message}`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
241
909
|
function runWikiCompileDryRun({ projectPath, planArg }) {
|
|
242
910
|
const resolvedProjectPath = resolveProjectPath(projectPath || process.cwd());
|
|
243
911
|
if (!fs.existsSync(resolvedProjectPath) || !fs.statSync(resolvedProjectPath).isDirectory()) {
|
|
@@ -247,13 +915,17 @@ function runWikiCompileDryRun({ projectPath, planArg }) {
|
|
|
247
915
|
try {
|
|
248
916
|
const workspacePath = ensureExistingWorkspace(resolvedProjectPath);
|
|
249
917
|
const planPath = resolvePlanPath(planArg, resolvedProjectPath);
|
|
250
|
-
const
|
|
251
|
-
const
|
|
918
|
+
const parsed = parsePlanOperations(planPath);
|
|
919
|
+
const rawOperations = parsed.operations;
|
|
920
|
+
const sourceQualitySummary = parsed.sourceQualitySummary || emptySourceQualitySummary();
|
|
921
|
+
const operations = rawOperations.map((operation, index) => normalizeOperation(operation, index, resolvedProjectPath));
|
|
252
922
|
const reportsPath = getWikiReportsPath(resolvedProjectPath);
|
|
253
923
|
assertWikiWorkspaceWritePath(reportsPath, resolvedProjectPath);
|
|
254
924
|
const generatedAt = new Date().toISOString();
|
|
255
925
|
const reportPath = path.join(reportsPath, `${REPORT_PREFIX}-${todayStamp(new Date(generatedAt))}.md`);
|
|
926
|
+
const applyPlanPath = path.join(reportsPath, `${APPLY_PLAN_PREFIX}-${todayStamp(new Date(generatedAt))}.json`);
|
|
256
927
|
assertWikiWorkspaceWritePath(reportPath, resolvedProjectPath);
|
|
928
|
+
assertWikiWorkspaceWritePath(applyPlanPath, resolvedProjectPath);
|
|
257
929
|
fs.mkdirSync(reportsPath, { recursive: true });
|
|
258
930
|
fs.writeFileSync(
|
|
259
931
|
reportPath,
|
|
@@ -261,14 +933,28 @@ function runWikiCompileDryRun({ projectPath, planArg }) {
|
|
|
261
933
|
projectPath: resolvedProjectPath,
|
|
262
934
|
workspacePath,
|
|
263
935
|
planPath,
|
|
936
|
+
applyPlanPath,
|
|
264
937
|
generatedAt,
|
|
265
938
|
operations,
|
|
266
939
|
}),
|
|
267
940
|
"utf-8"
|
|
268
941
|
);
|
|
942
|
+
fs.writeFileSync(
|
|
943
|
+
applyPlanPath,
|
|
944
|
+
JSON.stringify(renderApplyPlan({
|
|
945
|
+
projectPath: resolvedProjectPath,
|
|
946
|
+
planPath,
|
|
947
|
+
reportPath,
|
|
948
|
+
generatedAt,
|
|
949
|
+
operations,
|
|
950
|
+
sourceQualitySummary,
|
|
951
|
+
}), null, 2) + "\n",
|
|
952
|
+
"utf-8"
|
|
953
|
+
);
|
|
269
954
|
|
|
270
955
|
return {
|
|
271
956
|
reportPath,
|
|
957
|
+
applyPlanPath,
|
|
272
958
|
operations,
|
|
273
959
|
summary: summarizeOperations(operations),
|
|
274
960
|
unsupportedCount: operations.filter((operation) => operation.validation_status === "unsupported_operation").length,
|
|
@@ -280,8 +966,10 @@ function runWikiCompileDryRun({ projectPath, planArg }) {
|
|
|
280
966
|
}
|
|
281
967
|
|
|
282
968
|
module.exports = {
|
|
969
|
+
APPLY_PLAN_RECORD_TYPE,
|
|
283
970
|
ALLOWED_OPERATION_TYPES,
|
|
284
971
|
REPORT_PREFIX,
|
|
285
972
|
renderReport,
|
|
973
|
+
runWikiCompileApply,
|
|
286
974
|
runWikiCompileDryRun,
|
|
287
975
|
};
|