skillwiki 0.10.21 → 0.10.23
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-6ZDKTNLA.js +294 -0
- package/dist/{chunk-65Q5UGND.js → chunk-BASWDOQB.js} +2 -2
- package/dist/{chunk-Y6KRDGI2.js → chunk-C2DKFJFA.js} +6 -1
- package/dist/chunk-EQPU2BPM.js +468 -0
- package/dist/{chunk-GNS2ZV5P.js → chunk-PQG26AGJ.js} +1 -1
- package/dist/{chunk-I5JD3BQZ.js → chunk-QBZYEEBD.js} +13 -296
- package/dist/chunk-QNTBNNEL.js +616 -0
- package/dist/{chunk-UNPZDCWN.js → chunk-SYKSL3JQ.js} +860 -700
- package/dist/cli.js +2334 -273
- package/dist/{index-projection-HAXDEM2F.js → index-projection-FRWTZB5Y.js} +3 -2
- package/dist/{managed-write-preflight-CTXX2MHQ.js → managed-write-preflight-4LPVMP42.js} +3 -3
- package/dist/skillwiki-mcp.js +6 -4
- package/dist/sources-L2SQV63E.js +10 -0
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/README.md +13 -2
- package/skills/agents/wiki-add-task.md +2 -1
- package/skills/agents/wiki-archive.md +8 -9
- package/skills/agents/wiki-audit.md +2 -1
- package/skills/agents/wiki-lint.md +2 -1
- package/skills/agents/wiki-query.md +3 -1
- package/skills/agents/wiki-reingest.md +9 -8
- package/skills/package.json +1 -1
- package/skills/proj-decide/SKILL.md +35 -5
- package/skills/skills/proj-decide/SKILL.md +35 -5
- package/skills/skills/using-skillwiki/SKILL.md +19 -13
- package/skills/skills/wiki-add-task/SKILL.md +4 -3
- package/skills/skills/wiki-archive/SKILL.md +23 -18
- package/skills/skills/wiki-audit/SKILL.md +3 -1
- package/skills/skills/wiki-init/SKILL.md +12 -1
- package/skills/skills/wiki-lint/SKILL.md +2 -1
- package/skills/skills/wiki-query/SKILL.md +7 -0
- package/skills/skills/wiki-reingest/SKILL.md +12 -8
- package/skills/skills/wiki-remove/SKILL.md +22 -5
- package/skills/skills/wiki-sync/SKILL.md +39 -54
- package/skills/using-skillwiki/SKILL.md +19 -13
- package/skills/wiki-add-task/SKILL.md +4 -3
- package/skills/wiki-archive/SKILL.md +23 -18
- package/skills/wiki-audit/SKILL.md +3 -1
- package/skills/wiki-init/SKILL.md +12 -1
- package/skills/wiki-lint/SKILL.md +2 -1
- package/skills/wiki-query/SKILL.md +7 -0
- package/skills/wiki-reingest/SKILL.md +12 -8
- package/skills/wiki-remove/SKILL.md +22 -5
- package/skills/wiki-sync/SKILL.md +39 -54
- package/templates/SCHEMA.md +8 -5
- package/templates/web-clipper/llm-wiki-clippings.json +56 -0
- package/templates/web-clipper/readme.txt +16 -0
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
extractFrontmatter,
|
|
4
|
+
scanSensitiveContent,
|
|
5
|
+
scanVault
|
|
6
|
+
} from "./chunk-6ZDKTNLA.js";
|
|
2
7
|
import {
|
|
3
8
|
MetaSchema,
|
|
4
9
|
TypedKnowledgeSchema,
|
|
5
10
|
detectSchema,
|
|
6
11
|
err,
|
|
7
|
-
getErrorMessage,
|
|
8
12
|
ok
|
|
9
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-C2DKFJFA.js";
|
|
10
14
|
|
|
11
15
|
// src/utils/index-projection.ts
|
|
12
|
-
import { readFile as
|
|
13
|
-
import { join as
|
|
16
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
17
|
+
import { join as join2 } from "path";
|
|
14
18
|
|
|
15
19
|
// src/utils/atomic-write.ts
|
|
16
20
|
import { randomBytes } from "crypto";
|
|
@@ -59,186 +63,11 @@ async function atomicWriteText(path, text) {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
// src/utils/index-universe.ts
|
|
62
|
-
import { readFile as
|
|
66
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
63
67
|
|
|
64
68
|
// src/utils/typed-page.ts
|
|
65
69
|
import { lstatSync, realpathSync } from "fs";
|
|
66
70
|
import { dirname as dirname2, posix, relative, resolve, sep } from "path";
|
|
67
|
-
|
|
68
|
-
// src/parsers/frontmatter.ts
|
|
69
|
-
import yaml from "js-yaml";
|
|
70
|
-
var FM_OPEN = /^---\r?\n/;
|
|
71
|
-
function splitFrontmatter(text) {
|
|
72
|
-
if (!FM_OPEN.test(text)) return ok({ rawFrontmatter: "", body: text, bodyStart: 0 });
|
|
73
|
-
const afterOpen = text.replace(FM_OPEN, "");
|
|
74
|
-
const closeIdx = afterOpen.search(/\r?\n---\r?\n/);
|
|
75
|
-
if (closeIdx === -1) return err("MISSING_CLOSING_DELIMITER");
|
|
76
|
-
const rawFrontmatter = afterOpen.slice(0, closeIdx);
|
|
77
|
-
const closeMatch = afterOpen.slice(closeIdx).match(/\r?\n---\r?\n/);
|
|
78
|
-
const bodyStart = text.length - (afterOpen.length - closeIdx - closeMatch[0].length);
|
|
79
|
-
const body = text.slice(bodyStart);
|
|
80
|
-
return ok({ rawFrontmatter, body, bodyStart });
|
|
81
|
-
}
|
|
82
|
-
function extractFrontmatter(text) {
|
|
83
|
-
const split = splitFrontmatter(text);
|
|
84
|
-
if (!split.ok) return split;
|
|
85
|
-
if (!split.data.rawFrontmatter) return ok({});
|
|
86
|
-
try {
|
|
87
|
-
const parsed = yaml.load(split.data.rawFrontmatter, { schema: yaml.JSON_SCHEMA });
|
|
88
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return ok({});
|
|
89
|
-
return ok(parsed);
|
|
90
|
-
} catch (e) {
|
|
91
|
-
return err("INVALID_FRONTMATTER", { message: getErrorMessage(e) });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// src/utils/sensitive-content.ts
|
|
96
|
-
import { createHash } from "crypto";
|
|
97
|
-
var REDACTED_RE = /\[REDACTED:[^\]]+\]/i;
|
|
98
|
-
var SYNTHETIC_RE = /^(?:<[^>]+>|\$\{[^}]+\}|REPLACE_WITH_[A-Z0-9_]+|YOUR_[A-Z0-9_]+|EXAMPLE_[A-Z0-9_]+)$/i;
|
|
99
|
-
var MATCHERS = [
|
|
100
|
-
{
|
|
101
|
-
kind: "private_key",
|
|
102
|
-
re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
kind: "authorization_header",
|
|
106
|
-
re: /\bAuthorization["']?\s*:\s*["']?(Bearer\s+[A-Za-z0-9._~+/-]{20,})["']?/gi,
|
|
107
|
-
valueGroup: 1
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
kind: "cookie",
|
|
111
|
-
re: /\b(?:Cookie|Set-Cookie)\s*:\s*([^\n]{20,})/gi,
|
|
112
|
-
valueGroup: 1
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
kind: "jwt",
|
|
116
|
-
re: /\b([A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{12,}\.[A-Za-z0-9_-]{12,})\b/g,
|
|
117
|
-
valueGroup: 1
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
kind: "provider_key",
|
|
121
|
-
re: /\b(sk-[A-Za-z0-9_-]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|gh[pousr]_[A-Za-z0-9_=-]{20,})\b/g,
|
|
122
|
-
valueGroup: 1
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
kind: "access_key",
|
|
126
|
-
re: /\b((?:AKIA|ASIA)[A-Z0-9]{16})\b/g,
|
|
127
|
-
valueGroup: 1
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
kind: "access_key",
|
|
131
|
-
re: /\b(?:access[-_ ]?key|credential)["']?\s*[:=]\s*["']?([A-Za-z0-9._~+/-]{20,})["']?/gi,
|
|
132
|
-
valueGroup: 1
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
kind: "api_key",
|
|
136
|
-
re: /\b(?:api[-_ ]?key)["']?\s*[:=]\s*["']?([A-Za-z0-9._~+/-]{20,})["']?/gi,
|
|
137
|
-
valueGroup: 1
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
kind: "password",
|
|
141
|
-
re: /\b(?:pass(?:word|wd)?)["']?\s*[:=]\s*["']?([^\s`"']{8,})["']?/gi,
|
|
142
|
-
valueGroup: 1
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
kind: "secret",
|
|
146
|
-
re: /\b(?:secret|client[-_ ]?secret)["']?\s*[:=]\s*["']?([A-Za-z0-9._~+/-]{16,})["']?/gi,
|
|
147
|
-
valueGroup: 1
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
kind: "token",
|
|
151
|
-
re: /\b(?:token|session)["']?\s*[:=]\s*["']?([A-Za-z0-9._~+/-]{16,})["']?/gi,
|
|
152
|
-
valueGroup: 1
|
|
153
|
-
}
|
|
154
|
-
];
|
|
155
|
-
function fingerprint(value) {
|
|
156
|
-
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
157
|
-
}
|
|
158
|
-
function lineFor(text, offset) {
|
|
159
|
-
return text.slice(0, offset).split(/\r?\n/).length;
|
|
160
|
-
}
|
|
161
|
-
function redactMarker(kind, value) {
|
|
162
|
-
return `[REDACTED:${kind}:${fingerprint(value)}]`;
|
|
163
|
-
}
|
|
164
|
-
function isSyntheticPlaceholder(value) {
|
|
165
|
-
return REDACTED_RE.test(value) || SYNTHETIC_RE.test(value.trim());
|
|
166
|
-
}
|
|
167
|
-
function isNonSecretTokenCapture(value) {
|
|
168
|
-
if (value.startsWith("//")) return true;
|
|
169
|
-
if (/^[a-z]{2,}(?:-[a-z]{2,})+$/.test(value)) return true;
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
function collectMatches(text) {
|
|
173
|
-
const matches = [];
|
|
174
|
-
for (const matcher of MATCHERS) {
|
|
175
|
-
matcher.re.lastIndex = 0;
|
|
176
|
-
for (const m of text.matchAll(matcher.re)) {
|
|
177
|
-
const whole = m[0];
|
|
178
|
-
const start = m.index ?? 0;
|
|
179
|
-
if (REDACTED_RE.test(whole)) continue;
|
|
180
|
-
const value = matcher.valueGroup ? m[matcher.valueGroup] : whole;
|
|
181
|
-
if (isSyntheticPlaceholder(value)) continue;
|
|
182
|
-
if (matcher.kind === "token" && isNonSecretTokenCapture(value)) continue;
|
|
183
|
-
const valueOffset = whole.lastIndexOf(value);
|
|
184
|
-
const valueStart = start + Math.max(0, valueOffset);
|
|
185
|
-
matches.push({
|
|
186
|
-
start,
|
|
187
|
-
end: start + whole.length,
|
|
188
|
-
valueStart,
|
|
189
|
-
valueEnd: valueStart + value.length,
|
|
190
|
-
kind: matcher.kind
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return matches.sort((a, b) => {
|
|
195
|
-
if (a.valueStart !== b.valueStart) return a.valueStart - b.valueStart;
|
|
196
|
-
return b.valueEnd - b.valueStart - (a.valueEnd - a.valueStart);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
function collapseOverlaps(matches) {
|
|
200
|
-
const kept = [];
|
|
201
|
-
for (const match of matches) {
|
|
202
|
-
const overlaps = kept.some((k) => match.valueStart < k.valueEnd && match.valueEnd > k.valueStart);
|
|
203
|
-
if (!overlaps) kept.push(match);
|
|
204
|
-
}
|
|
205
|
-
return kept;
|
|
206
|
-
}
|
|
207
|
-
function scanSensitiveContent(text, opts = {}) {
|
|
208
|
-
return collapseOverlaps(collectMatches(text)).map((match) => {
|
|
209
|
-
const value = text.slice(match.valueStart, match.valueEnd);
|
|
210
|
-
const marker = redactMarker(match.kind, value);
|
|
211
|
-
const rawPreview = text.slice(Math.max(0, match.start - 24), Math.min(text.length, match.end + 24));
|
|
212
|
-
const preview = rawPreview.replace(value, marker);
|
|
213
|
-
return {
|
|
214
|
-
file: opts.file,
|
|
215
|
-
line: lineFor(text, match.valueStart),
|
|
216
|
-
kind: match.kind,
|
|
217
|
-
preview,
|
|
218
|
-
fingerprint: fingerprint(value)
|
|
219
|
-
};
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
function redactSensitiveContent(text, opts = {}) {
|
|
223
|
-
const matches = collapseOverlaps(collectMatches(text));
|
|
224
|
-
if (matches.length === 0) return { text, changed: false, findings: [] };
|
|
225
|
-
let out = "";
|
|
226
|
-
let cursor = 0;
|
|
227
|
-
for (const match of matches) {
|
|
228
|
-
const value = text.slice(match.valueStart, match.valueEnd);
|
|
229
|
-
out += text.slice(cursor, match.valueStart);
|
|
230
|
-
out += redactMarker(match.kind, value);
|
|
231
|
-
cursor = match.valueEnd;
|
|
232
|
-
}
|
|
233
|
-
out += text.slice(cursor);
|
|
234
|
-
return {
|
|
235
|
-
text: out,
|
|
236
|
-
changed: out !== text,
|
|
237
|
-
findings: scanSensitiveContent(text, opts)
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/utils/typed-page.ts
|
|
242
71
|
var TYPE_DIRECTORY = {
|
|
243
72
|
entity: "entities",
|
|
244
73
|
concept: "concepts",
|
|
@@ -346,108 +175,6 @@ function prepareTypedPage(content, target) {
|
|
|
346
175
|
return invalidFrontmatter(target, []);
|
|
347
176
|
}
|
|
348
177
|
|
|
349
|
-
// src/utils/vault.ts
|
|
350
|
-
import { existsSync, readFileSync } from "fs";
|
|
351
|
-
import { readFile as readFile2, readdir, stat } from "fs/promises";
|
|
352
|
-
import { join as join2, relative as relative2, sep as sep2 } from "path";
|
|
353
|
-
var TYPED_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
354
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
355
|
-
var DEFAULT_IO_CONCURRENCY = 1;
|
|
356
|
-
function vaultIoConcurrency() {
|
|
357
|
-
const raw = Number.parseInt(process.env.SKILLWIKI_VAULT_IO_CONCURRENCY ?? "", 10);
|
|
358
|
-
return Number.isFinite(raw) && raw > 0 ? Math.min(raw, 64) : DEFAULT_IO_CONCURRENCY;
|
|
359
|
-
}
|
|
360
|
-
function decodeProcMountPath(value) {
|
|
361
|
-
return value.replace(/\\040/g, " ");
|
|
362
|
-
}
|
|
363
|
-
function isRcloneFuseVaultFromMounts(root, mounts) {
|
|
364
|
-
return mounts.split(/\r?\n/).some((line) => {
|
|
365
|
-
const parts = line.split(" ");
|
|
366
|
-
if (parts.length < 3) return false;
|
|
367
|
-
const mountPoint = decodeProcMountPath(parts[1]);
|
|
368
|
-
const fsType = parts[2];
|
|
369
|
-
return fsType === "fuse.rclone" && (root === mountPoint || root.startsWith(`${mountPoint}/`));
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
function resolveReadOnlyVaultRootWithMounts(root, mounts) {
|
|
373
|
-
if (/^(1|true|yes)$/i.test(process.env.SKILLWIKI_DISABLE_VAULT_READ_MIRROR ?? "")) {
|
|
374
|
-
return { root, mirrored: false };
|
|
375
|
-
}
|
|
376
|
-
const explicitMirror = process.env.SKILLWIKI_VAULT_READ_MIRROR;
|
|
377
|
-
if (explicitMirror && existsSync(join2(explicitMirror, "SCHEMA.md"))) {
|
|
378
|
-
return { root: explicitMirror, mirrored: explicitMirror !== root };
|
|
379
|
-
}
|
|
380
|
-
const siblingMirror = `${root}-git`;
|
|
381
|
-
if (isRcloneFuseVaultFromMounts(root, mounts) && existsSync(join2(siblingMirror, "SCHEMA.md"))) {
|
|
382
|
-
return { root: siblingMirror, mirrored: true };
|
|
383
|
-
}
|
|
384
|
-
return { root, mirrored: false };
|
|
385
|
-
}
|
|
386
|
-
function resolveReadOnlyVaultRoot(root) {
|
|
387
|
-
let mounts = "";
|
|
388
|
-
try {
|
|
389
|
-
mounts = readFileSync("/proc/mounts", "utf8");
|
|
390
|
-
} catch {
|
|
391
|
-
}
|
|
392
|
-
return resolveReadOnlyVaultRootWithMounts(root, mounts);
|
|
393
|
-
}
|
|
394
|
-
async function mapWithConcurrency(items, limit, mapper) {
|
|
395
|
-
const out = new Array(items.length);
|
|
396
|
-
let next = 0;
|
|
397
|
-
const workers = Array.from({ length: Math.min(Math.max(1, limit), items.length) }, async () => {
|
|
398
|
-
for (; ; ) {
|
|
399
|
-
const index = next++;
|
|
400
|
-
if (index >= items.length) return;
|
|
401
|
-
out[index] = await mapper(items[index], index);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
await Promise.all(workers);
|
|
405
|
-
return out;
|
|
406
|
-
}
|
|
407
|
-
async function scanVault(root) {
|
|
408
|
-
try {
|
|
409
|
-
await stat(join2(root, "SCHEMA.md"));
|
|
410
|
-
} catch {
|
|
411
|
-
return err("VAULT_PATH_INVALID", { root, reason: "SCHEMA.md missing" });
|
|
412
|
-
}
|
|
413
|
-
const all = await walk(root);
|
|
414
|
-
const rels = all.map((p) => ({ absPath: p, relPath: relative2(root, p).split(sep2).join("/") }));
|
|
415
|
-
return ok({
|
|
416
|
-
root,
|
|
417
|
-
allMarkdown: rels,
|
|
418
|
-
typedKnowledge: rels.filter((p) => TYPED_DIRS.some((d) => p.relPath.startsWith(d + "/"))),
|
|
419
|
-
raw: rels.filter((p) => p.relPath.startsWith("raw/")),
|
|
420
|
-
workItems: rels.filter((p) => /^projects\/[^/]+\/work\/[^/]+\/(spec|plan|log)\.md$/.test(p.relPath)),
|
|
421
|
-
compound: rels.filter((p) => /^projects\/[^/]+\/compound\//.test(p.relPath))
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
async function walk(dir) {
|
|
425
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
426
|
-
const out = [];
|
|
427
|
-
const subdirs = [];
|
|
428
|
-
for (const e of entries) {
|
|
429
|
-
const p = join2(dir, e.name);
|
|
430
|
-
if (e.isDirectory()) {
|
|
431
|
-
if (SKIP_DIRS.has(e.name)) continue;
|
|
432
|
-
subdirs.push(p);
|
|
433
|
-
} else if (e.isFile() && e.name.endsWith(".md")) out.push(p);
|
|
434
|
-
}
|
|
435
|
-
const nested = await mapWithConcurrency(subdirs, Math.min(8, vaultIoConcurrency()), walk);
|
|
436
|
-
for (const files of nested) out.push(...files);
|
|
437
|
-
return out;
|
|
438
|
-
}
|
|
439
|
-
async function readPage(p) {
|
|
440
|
-
return readFile2(p.absPath, "utf8");
|
|
441
|
-
}
|
|
442
|
-
async function readPageCached(p, cache) {
|
|
443
|
-
if (!cache) return readPage(p);
|
|
444
|
-
const existing = cache.get(p.absPath);
|
|
445
|
-
if (existing) return existing;
|
|
446
|
-
const pending = readPage(p);
|
|
447
|
-
cache.set(p.absPath, pending);
|
|
448
|
-
return pending;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
178
|
// src/utils/index-universe.ts
|
|
452
179
|
var ROOT_INDEX_SECTION_ORDER = [
|
|
453
180
|
"Entities",
|
|
@@ -480,7 +207,7 @@ async function buildRootIndexUniverse(input) {
|
|
|
480
207
|
for (const page of typedPages) {
|
|
481
208
|
let text;
|
|
482
209
|
try {
|
|
483
|
-
text = await
|
|
210
|
+
text = await readFile2(page.absPath, "utf8");
|
|
484
211
|
} catch {
|
|
485
212
|
continue;
|
|
486
213
|
}
|
|
@@ -526,7 +253,7 @@ async function buildRootIndexUniverse(input) {
|
|
|
526
253
|
for (const readme of projectReadmes) {
|
|
527
254
|
let text;
|
|
528
255
|
try {
|
|
529
|
-
text = await
|
|
256
|
+
text = await readFile2(readme.absPath, "utf8");
|
|
530
257
|
} catch {
|
|
531
258
|
continue;
|
|
532
259
|
}
|
|
@@ -600,7 +327,7 @@ async function renderRootIndex(input) {
|
|
|
600
327
|
let currentText = input.currentText;
|
|
601
328
|
if (currentText === void 0) {
|
|
602
329
|
try {
|
|
603
|
-
currentText = await
|
|
330
|
+
currentText = await readFile3(join2(vault, "index.md"), "utf8");
|
|
604
331
|
} catch {
|
|
605
332
|
currentText = "";
|
|
606
333
|
}
|
|
@@ -668,23 +395,13 @@ async function renderRootIndex(input) {
|
|
|
668
395
|
});
|
|
669
396
|
}
|
|
670
397
|
async function writeRootIndexProjection(vault, projection) {
|
|
671
|
-
return atomicWriteText(
|
|
398
|
+
return atomicWriteText(join2(vault, "index.md"), projection.text);
|
|
672
399
|
}
|
|
673
400
|
|
|
674
401
|
export {
|
|
675
|
-
splitFrontmatter,
|
|
676
|
-
extractFrontmatter,
|
|
677
|
-
scanSensitiveContent,
|
|
678
|
-
redactSensitiveContent,
|
|
679
402
|
atomicWriteText,
|
|
680
403
|
assertTargetInsideVault,
|
|
681
404
|
prepareTypedPage,
|
|
682
|
-
vaultIoConcurrency,
|
|
683
|
-
resolveReadOnlyVaultRoot,
|
|
684
|
-
mapWithConcurrency,
|
|
685
|
-
scanVault,
|
|
686
|
-
readPage,
|
|
687
|
-
readPageCached,
|
|
688
405
|
buildRootIndexUniverse,
|
|
689
406
|
UNMANAGED_START,
|
|
690
407
|
UNMANAGED_END,
|