skillwiki 0.10.7 → 0.10.9
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-4KGCTQM3.js → chunk-3ZVZ2YEE.js} +1 -1
- package/dist/{chunk-45SMQAWC.js → chunk-AVCGIWSL.js} +456 -249
- package/dist/{chunk-R6BKJWVC.js → chunk-TYN2IHBY.js} +2 -0
- package/dist/cli.js +750 -347
- package/dist/{managed-write-preflight-57DQZI2N.js → managed-write-preflight-7TWSEZJZ.js} +2 -2
- package/dist/skillwiki-mcp.js +2 -2
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
satelliteGateFromFleetLoad,
|
|
33
33
|
snapshotterAliasForLocalHost,
|
|
34
34
|
writeDotenv
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-TYN2IHBY.js";
|
|
36
36
|
import {
|
|
37
37
|
CompoundSchema,
|
|
38
38
|
ExitCode,
|
|
@@ -47,8 +47,9 @@ import {
|
|
|
47
47
|
} from "./chunk-C5OLZRRM.js";
|
|
48
48
|
|
|
49
49
|
// src/commands/log-append.ts
|
|
50
|
-
import { readFile, stat } from "fs/promises";
|
|
51
|
-
import {
|
|
50
|
+
import { readFile as readFile2, stat } from "fs/promises";
|
|
51
|
+
import { hostname } from "os";
|
|
52
|
+
import { join as join4 } from "path";
|
|
52
53
|
|
|
53
54
|
// src/utils/last-op.ts
|
|
54
55
|
import { readFileSync, writeFileSync, mkdirSync, unlinkSync, existsSync } from "fs";
|
|
@@ -159,36 +160,187 @@ function releaseLogLock(handle) {
|
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
// src/utils/log-events.ts
|
|
164
|
+
import { mkdir, open, readFile, readdir } from "fs/promises";
|
|
165
|
+
import { join as join3 } from "path";
|
|
166
|
+
function isPlainObject(value) {
|
|
167
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
168
|
+
}
|
|
169
|
+
function canonicalize(value) {
|
|
170
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
171
|
+
if (!isPlainObject(value)) return value;
|
|
172
|
+
const out = {};
|
|
173
|
+
for (const key of Object.keys(value).sort()) {
|
|
174
|
+
out[key] = canonicalize(value[key]);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
function eventPathFor(event) {
|
|
179
|
+
const day = event.occurred_at.slice(0, 10);
|
|
180
|
+
return `meta/log-events/${day}/${event.operation_id}.json`;
|
|
181
|
+
}
|
|
182
|
+
function validateLogEvent(event) {
|
|
183
|
+
if (event.schema !== "skillwiki-log-event/v1") {
|
|
184
|
+
return err("SCHEME_REJECTED", { message: "invalid event schema" });
|
|
185
|
+
}
|
|
186
|
+
if (!/^[0-9a-f]{64}$/.test(event.operation_id)) {
|
|
187
|
+
return err("SCHEME_REJECTED", { message: "operation_id must be 64 hex chars" });
|
|
188
|
+
}
|
|
189
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(event.occurred_at)) {
|
|
190
|
+
return err("SCHEME_REJECTED", { message: "occurred_at must be UTC ISO with milliseconds" });
|
|
191
|
+
}
|
|
192
|
+
for (const field of ["host_id", "actor", "kind", "target", "note"]) {
|
|
193
|
+
const v = event[field];
|
|
194
|
+
if (typeof v !== "string" || v.trim().length === 0 || v.length > 500) {
|
|
195
|
+
return err("SCHEME_REJECTED", { message: `invalid ${field}` });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!isPlainObject(event.metadata)) {
|
|
199
|
+
return err("SCHEME_REJECTED", { message: "metadata must be a plain object" });
|
|
200
|
+
}
|
|
201
|
+
const sensitive = scanSensitiveContent(JSON.stringify(event));
|
|
202
|
+
if (sensitive.length > 0) {
|
|
203
|
+
return err("SENSITIVE_CONTENT_DETECTED", { findings: sensitive });
|
|
204
|
+
}
|
|
205
|
+
return ok({
|
|
206
|
+
schema: "skillwiki-log-event/v1",
|
|
207
|
+
operation_id: event.operation_id,
|
|
208
|
+
occurred_at: event.occurred_at,
|
|
209
|
+
host_id: event.host_id,
|
|
210
|
+
actor: event.actor,
|
|
211
|
+
kind: event.kind,
|
|
212
|
+
target: event.target,
|
|
213
|
+
note: event.note,
|
|
214
|
+
metadata: canonicalize(event.metadata)
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function canonicalEventJson(event) {
|
|
218
|
+
const ordered = {
|
|
219
|
+
schema: event.schema,
|
|
220
|
+
operation_id: event.operation_id,
|
|
221
|
+
occurred_at: event.occurred_at,
|
|
222
|
+
host_id: event.host_id,
|
|
223
|
+
actor: event.actor,
|
|
224
|
+
kind: event.kind,
|
|
225
|
+
target: event.target,
|
|
226
|
+
note: event.note,
|
|
227
|
+
metadata: event.metadata
|
|
228
|
+
};
|
|
229
|
+
return `${JSON.stringify(ordered, null, 2)}
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
232
|
+
async function writeLogEvent(vault, event) {
|
|
233
|
+
const validated = validateLogEvent(event);
|
|
234
|
+
if (!validated.ok) return validated;
|
|
235
|
+
const rel = eventPathFor(validated.data);
|
|
236
|
+
const abs = join3(vault, rel);
|
|
237
|
+
await mkdir(join3(vault, "meta", "log-events", validated.data.occurred_at.slice(0, 10)), {
|
|
238
|
+
recursive: true
|
|
239
|
+
});
|
|
240
|
+
const body = canonicalEventJson(validated.data);
|
|
241
|
+
try {
|
|
242
|
+
const handle = await open(abs, "wx");
|
|
243
|
+
try {
|
|
244
|
+
await handle.writeFile(body, "utf8");
|
|
245
|
+
} finally {
|
|
246
|
+
await handle.close();
|
|
247
|
+
}
|
|
248
|
+
return ok({ path: rel, created: true });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error.code === "EEXIST") {
|
|
251
|
+
let existing;
|
|
252
|
+
try {
|
|
253
|
+
existing = await readFile(abs, "utf8");
|
|
254
|
+
} catch (readErr) {
|
|
255
|
+
return err("WRITE_FAILED", { path: rel, message: String(readErr) });
|
|
256
|
+
}
|
|
257
|
+
if (existing === body) return ok({ path: rel, created: false });
|
|
258
|
+
return err("EVENT_IDENTITY_COLLISION", { path: rel });
|
|
259
|
+
}
|
|
260
|
+
return err("WRITE_FAILED", { path: rel, message: String(error) });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async function readLogEvents(vault) {
|
|
264
|
+
const root = join3(vault, "meta", "log-events");
|
|
265
|
+
let days;
|
|
266
|
+
try {
|
|
267
|
+
days = (await readdir(root, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
268
|
+
} catch {
|
|
269
|
+
return ok([]);
|
|
270
|
+
}
|
|
271
|
+
const events = [];
|
|
272
|
+
for (const day of days) {
|
|
273
|
+
const files = (await readdir(join3(root, day))).filter((f) => f.endsWith(".json")).sort();
|
|
274
|
+
for (const file of files) {
|
|
275
|
+
const text = await readFile(join3(root, day, file), "utf8");
|
|
276
|
+
let parsed;
|
|
277
|
+
try {
|
|
278
|
+
parsed = JSON.parse(text);
|
|
279
|
+
} catch {
|
|
280
|
+
return err("SCHEME_REJECTED", { path: `meta/log-events/${day}/${file}`, message: "invalid JSON" });
|
|
281
|
+
}
|
|
282
|
+
const validated = validateLogEvent(parsed);
|
|
283
|
+
if (!validated.ok) return validated;
|
|
284
|
+
if (eventPathFor(validated.data) !== `meta/log-events/${day}/${file}`) {
|
|
285
|
+
return err("SCHEME_REJECTED", {
|
|
286
|
+
path: `meta/log-events/${day}/${file}`,
|
|
287
|
+
message: "path/identity mismatch"
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
events.push(validated.data);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
events.sort((a, b) => {
|
|
294
|
+
if (a.occurred_at !== b.occurred_at) return a.occurred_at < b.occurred_at ? -1 : 1;
|
|
295
|
+
return a.operation_id < b.operation_id ? -1 : a.operation_id > b.operation_id ? 1 : 0;
|
|
296
|
+
});
|
|
297
|
+
return ok(events);
|
|
298
|
+
}
|
|
299
|
+
|
|
162
300
|
// src/commands/log-append.ts
|
|
163
301
|
var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
|
|
164
|
-
function
|
|
302
|
+
function countLogEntries(logText) {
|
|
303
|
+
let count = 0;
|
|
304
|
+
for (const _ of logText.matchAll(ENTRY_RE)) count += 1;
|
|
305
|
+
return count;
|
|
306
|
+
}
|
|
307
|
+
function operationMarkers(operationId) {
|
|
165
308
|
if (!/^[0-9a-f]{64}$/.test(operationId)) {
|
|
166
309
|
return err("USAGE", { message: "operationId must be a SHA-256 hex string" });
|
|
167
310
|
}
|
|
168
|
-
return ok(
|
|
311
|
+
return ok([
|
|
312
|
+
`<!-- skillwiki-log-op:${operationId} -->`,
|
|
313
|
+
`<!-- skillwiki-page-publish:${operationId} -->`
|
|
314
|
+
]);
|
|
169
315
|
}
|
|
170
|
-
|
|
316
|
+
function preferredMarker(operationId, eventKind) {
|
|
317
|
+
if (!eventKind || eventKind === "page-publish") {
|
|
318
|
+
return `<!-- skillwiki-page-publish:${operationId} -->`;
|
|
319
|
+
}
|
|
320
|
+
return `<!-- skillwiki-log-op:${operationId} -->`;
|
|
321
|
+
}
|
|
322
|
+
async function appendWhileLocked(logPath, content, markers, writeMarker) {
|
|
171
323
|
let logText;
|
|
172
324
|
try {
|
|
173
|
-
logText = await
|
|
325
|
+
logText = await readFile2(logPath, "utf8");
|
|
174
326
|
} catch {
|
|
175
327
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
176
328
|
}
|
|
177
|
-
const entriesBefore =
|
|
178
|
-
if (marker
|
|
329
|
+
const entriesBefore = countLogEntries(logText);
|
|
330
|
+
if (markers?.some((marker) => logText.includes(marker))) {
|
|
179
331
|
return {
|
|
180
332
|
exitCode: ExitCode.OK,
|
|
181
333
|
result: ok({
|
|
182
334
|
entries_before: entriesBefore,
|
|
183
335
|
entries_after: entriesBefore,
|
|
184
336
|
appended: false,
|
|
185
|
-
humanHint: `
|
|
337
|
+
humanHint: `operation already appended (${entriesBefore} entries)`
|
|
186
338
|
})
|
|
187
339
|
};
|
|
188
340
|
}
|
|
189
341
|
const body = logText.replace(/\s+$/, "");
|
|
190
|
-
const appendedContent =
|
|
191
|
-
${
|
|
342
|
+
const appendedContent = writeMarker ? `${content}
|
|
343
|
+
${writeMarker}` : content;
|
|
192
344
|
const written = await atomicWriteText(logPath, `${body}
|
|
193
345
|
|
|
194
346
|
${appendedContent}
|
|
@@ -209,7 +361,7 @@ ${appendedContent}
|
|
|
209
361
|
}
|
|
210
362
|
async function runLogAppend(input) {
|
|
211
363
|
try {
|
|
212
|
-
await stat(
|
|
364
|
+
await stat(join4(input.vault, "SCHEMA.md"));
|
|
213
365
|
} catch {
|
|
214
366
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
215
367
|
}
|
|
@@ -224,11 +376,55 @@ async function runLogAppend(input) {
|
|
|
224
376
|
result: err("SENSITIVE_CONTENT_DETECTED", { file: "log.md", findings: sensitive })
|
|
225
377
|
};
|
|
226
378
|
}
|
|
227
|
-
let
|
|
379
|
+
let markers;
|
|
380
|
+
let writeMarker;
|
|
228
381
|
if (input.operationId !== void 0) {
|
|
229
|
-
const operation =
|
|
382
|
+
const operation = operationMarkers(input.operationId);
|
|
230
383
|
if (!operation.ok) return { exitCode: ExitCode.USAGE, result: operation };
|
|
231
|
-
|
|
384
|
+
markers = operation.data;
|
|
385
|
+
writeMarker = preferredMarker(input.operationId, input.eventKind);
|
|
386
|
+
}
|
|
387
|
+
let eventCreated;
|
|
388
|
+
let eventPath;
|
|
389
|
+
if (input.writeEvent === true && input.operationId) {
|
|
390
|
+
const day = input.eventMetadata?.day || content.match(/\[(\d{4}-\d{2}-\d{2})\]/)?.[1] || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
391
|
+
const event = await writeLogEvent(input.vault, {
|
|
392
|
+
schema: "skillwiki-log-event/v1",
|
|
393
|
+
operation_id: input.operationId,
|
|
394
|
+
occurred_at: `${day}T00:00:00.000Z`,
|
|
395
|
+
host_id: hostname() || "localhost",
|
|
396
|
+
actor: "skillwiki-cli",
|
|
397
|
+
kind: input.eventKind || "log-append",
|
|
398
|
+
target: input.eventTarget || "log.md",
|
|
399
|
+
note: input.eventNote || content.split("\n")[0].slice(0, 500),
|
|
400
|
+
metadata: {
|
|
401
|
+
...input.eventMetadata || {}
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
if (!event.ok) {
|
|
405
|
+
if (event.error === "EVENT_IDENTITY_COLLISION") {
|
|
406
|
+
eventCreated = false;
|
|
407
|
+
eventPath = eventPathFor({
|
|
408
|
+
schema: "skillwiki-log-event/v1",
|
|
409
|
+
operation_id: input.operationId,
|
|
410
|
+
occurred_at: `${day}T00:00:00.000Z`,
|
|
411
|
+
host_id: "localhost",
|
|
412
|
+
actor: "skillwiki-cli",
|
|
413
|
+
kind: input.eventKind || "log-append",
|
|
414
|
+
target: input.eventTarget || "log.md",
|
|
415
|
+
note: "collision",
|
|
416
|
+
metadata: {}
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
return {
|
|
420
|
+
exitCode: event.error === "SENSITIVE_CONTENT_DETECTED" ? ExitCode.SENSITIVE_CONTENT_DETECTED : ExitCode.WRITE_FAILED,
|
|
421
|
+
result: event
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
eventCreated = event.data.created;
|
|
426
|
+
eventPath = event.data.path;
|
|
427
|
+
}
|
|
232
428
|
}
|
|
233
429
|
const acquired = await acquireLogLock(input.vault, input.strictLock ? { reclaimStale: false } : {});
|
|
234
430
|
if (!acquired.ok) {
|
|
@@ -238,11 +434,11 @@ async function runLogAppend(input) {
|
|
|
238
434
|
return { exitCode: ExitCode.LOG_APPEND_LOCK_HELD, result: err("LOG_APPEND_LOCK_HELD", { vault: input.vault }) };
|
|
239
435
|
}
|
|
240
436
|
const lockHandle = acquired.data;
|
|
241
|
-
const logPath =
|
|
437
|
+
const logPath = join4(input.vault, "log.md");
|
|
242
438
|
let outcome;
|
|
243
439
|
let released;
|
|
244
440
|
try {
|
|
245
|
-
outcome = await appendWhileLocked(logPath, content,
|
|
441
|
+
outcome = await appendWhileLocked(logPath, content, markers, writeMarker);
|
|
246
442
|
} catch (error) {
|
|
247
443
|
outcome = {
|
|
248
444
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -257,10 +453,16 @@ async function runLogAppend(input) {
|
|
|
257
453
|
result: err("WRITE_FAILED", { stage: "log-unlock" })
|
|
258
454
|
};
|
|
259
455
|
}
|
|
260
|
-
if (outcome
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
456
|
+
if (outcome.result.ok) {
|
|
457
|
+
const alreadyApplied = !outcome.result.data.appended && eventCreated === false;
|
|
458
|
+
outcome = {
|
|
459
|
+
exitCode: outcome.exitCode,
|
|
460
|
+
result: ok({
|
|
461
|
+
...outcome.result.data,
|
|
462
|
+
...eventCreated !== void 0 ? { event_created: eventCreated } : {},
|
|
463
|
+
...eventPath ? { event_path: eventPath } : {},
|
|
464
|
+
humanHint: alreadyApplied ? `operation already applied (${outcome.result.data.entries_before} entries)` : outcome.result.data.humanHint
|
|
465
|
+
})
|
|
264
466
|
};
|
|
265
467
|
}
|
|
266
468
|
if (outcome.result.ok && outcome.result.data.appended && input.recordLastOp !== false) {
|
|
@@ -283,7 +485,7 @@ async function runLogAppend(input) {
|
|
|
283
485
|
|
|
284
486
|
// src/utils/sync-lock.ts
|
|
285
487
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
286
|
-
import { join as
|
|
488
|
+
import { join as join5 } from "path";
|
|
287
489
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
288
490
|
function getEnvSessionId() {
|
|
289
491
|
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
@@ -306,7 +508,7 @@ function getCliSessionId(cwd) {
|
|
|
306
508
|
return `cli-${getCwdHash(cwd)}`;
|
|
307
509
|
}
|
|
308
510
|
function lockPath(vault) {
|
|
309
|
-
return
|
|
511
|
+
return join5(vault, ".skillwiki", "sync.lock");
|
|
310
512
|
}
|
|
311
513
|
function readLock(vault) {
|
|
312
514
|
const path = lockPath(vault);
|
|
@@ -325,7 +527,7 @@ function isStale(lock, now) {
|
|
|
325
527
|
}
|
|
326
528
|
function acquireLock(vault, opts = {}) {
|
|
327
529
|
const path = lockPath(vault);
|
|
328
|
-
const dir =
|
|
530
|
+
const dir = join5(vault, ".skillwiki");
|
|
329
531
|
if (!existsSync3(dir)) {
|
|
330
532
|
mkdirSync3(dir, { recursive: true });
|
|
331
533
|
}
|
|
@@ -410,7 +612,7 @@ function acquireOwnedSyncLock(vault, opts) {
|
|
|
410
612
|
};
|
|
411
613
|
const path = lockPath(vault);
|
|
412
614
|
try {
|
|
413
|
-
mkdirSync3(
|
|
615
|
+
mkdirSync3(join5(vault, ".skillwiki"), { recursive: true });
|
|
414
616
|
} catch (error) {
|
|
415
617
|
return err("WRITE_FAILED", { path, message: String(error) });
|
|
416
618
|
}
|
|
@@ -442,12 +644,12 @@ function releaseOwnedSyncLock(handle) {
|
|
|
442
644
|
|
|
443
645
|
// src/commands/validate.ts
|
|
444
646
|
import { createHash as createHash2 } from "crypto";
|
|
445
|
-
import { readFile as
|
|
647
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
446
648
|
import { resolve, relative, sep } from "path";
|
|
447
649
|
|
|
448
650
|
// src/utils/index-entry.ts
|
|
449
|
-
import { readFile as
|
|
450
|
-
import { join as
|
|
651
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
652
|
+
import { join as join6 } from "path";
|
|
451
653
|
var TYPE_SECTION = {
|
|
452
654
|
entity: "Entities",
|
|
453
655
|
concept: "Concepts",
|
|
@@ -491,10 +693,10 @@ function renderIndexUpsert(text, input) {
|
|
|
491
693
|
});
|
|
492
694
|
}
|
|
493
695
|
async function upsertIndexEntry(input) {
|
|
494
|
-
const path =
|
|
696
|
+
const path = join6(input.vault, "index.md");
|
|
495
697
|
let before = "";
|
|
496
698
|
try {
|
|
497
|
-
before = await
|
|
699
|
+
before = await readFile3(path, "utf8");
|
|
498
700
|
} catch {
|
|
499
701
|
before = "";
|
|
500
702
|
}
|
|
@@ -529,7 +731,7 @@ var SCHEMAS = {
|
|
|
529
731
|
async function runValidate(input) {
|
|
530
732
|
let text;
|
|
531
733
|
try {
|
|
532
|
-
text = await
|
|
734
|
+
text = await readFile4(input.file, "utf8");
|
|
533
735
|
} catch {
|
|
534
736
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.file }) };
|
|
535
737
|
}
|
|
@@ -669,7 +871,7 @@ ${errors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}` })
|
|
|
669
871
|
}
|
|
670
872
|
|
|
671
873
|
// src/commands/graph.ts
|
|
672
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
874
|
+
import { writeFile, mkdir as mkdir2 } from "fs/promises";
|
|
673
875
|
import { dirname } from "path";
|
|
674
876
|
|
|
675
877
|
// src/parsers/wikilinks.ts
|
|
@@ -824,7 +1026,7 @@ async function runGraphBuild(input) {
|
|
|
824
1026
|
const adamicAdar = computeAdamicAdar(adjacency);
|
|
825
1027
|
const edge_count = Object.values(adjacency).reduce((acc, arr) => acc + arr.length, 0);
|
|
826
1028
|
try {
|
|
827
|
-
await
|
|
1029
|
+
await mkdir2(dirname(input.out), { recursive: true });
|
|
828
1030
|
await writeFile(input.out, JSON.stringify({ adjacency, adamicAdar }, null, 2));
|
|
829
1031
|
} catch (e) {
|
|
830
1032
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
|
|
@@ -868,7 +1070,7 @@ function computeAdamicAdar(adj) {
|
|
|
868
1070
|
}
|
|
869
1071
|
|
|
870
1072
|
// src/utils/wiki-path.ts
|
|
871
|
-
import { join as
|
|
1073
|
+
import { join as join7 } from "path";
|
|
872
1074
|
async function resolveInitTimePath(input) {
|
|
873
1075
|
const chain = [];
|
|
874
1076
|
if (input.flag !== void 0 && input.flag.length > 0) {
|
|
@@ -881,27 +1083,27 @@ async function resolveInitTimePath(input) {
|
|
|
881
1083
|
return { path: input.envValue, source: "env", ...input.explain ? { chain } : {} };
|
|
882
1084
|
}
|
|
883
1085
|
if (input.explain) chain.push({ source: "env", matched: false });
|
|
884
|
-
const sw = await parseDotenvFile(
|
|
1086
|
+
const sw = await parseDotenvFile(join7(input.home, ".skillwiki", ".env"));
|
|
885
1087
|
if (sw.WIKI_PATH !== void 0) {
|
|
886
1088
|
if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: true, value: sw.WIKI_PATH });
|
|
887
1089
|
return { path: sw.WIKI_PATH, source: "skillwiki-dotenv", ...input.explain ? { chain } : {} };
|
|
888
1090
|
}
|
|
889
1091
|
if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: false });
|
|
890
|
-
const hermes = await parseDotenvFile(
|
|
1092
|
+
const hermes = await parseDotenvFile(join7(input.home, ".hermes", ".env"));
|
|
891
1093
|
if (hermes.WIKI_PATH !== void 0) {
|
|
892
1094
|
if (input.explain) chain.push({ source: "hermes-dotenv", matched: true, value: hermes.WIKI_PATH });
|
|
893
1095
|
return { path: hermes.WIKI_PATH, source: "hermes-dotenv", ...input.explain ? { chain } : {} };
|
|
894
1096
|
}
|
|
895
1097
|
if (input.explain) chain.push({ source: "hermes-dotenv", matched: false });
|
|
896
1098
|
if (input.cwd) {
|
|
897
|
-
const projCfg = await parseDotenvFile(
|
|
1099
|
+
const projCfg = await parseDotenvFile(join7(input.cwd, ".skillwiki", ".env"));
|
|
898
1100
|
if (projCfg.WIKI_PATH !== void 0) {
|
|
899
1101
|
if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
|
|
900
1102
|
return { path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} };
|
|
901
1103
|
}
|
|
902
1104
|
}
|
|
903
1105
|
if (input.explain) chain.push({ source: "project-dotenv", matched: false });
|
|
904
|
-
const fallback =
|
|
1106
|
+
const fallback = join7(input.home, "wiki");
|
|
905
1107
|
if (input.explain) chain.push({ source: "default", matched: true, value: fallback });
|
|
906
1108
|
return { path: fallback, source: "default", ...input.explain ? { chain } : {} };
|
|
907
1109
|
}
|
|
@@ -912,7 +1114,7 @@ async function resolveRuntimePath(input) {
|
|
|
912
1114
|
return ok({ path: input.flag, source: "flag", ...input.explain ? { chain } : {} });
|
|
913
1115
|
}
|
|
914
1116
|
if (input.explain) chain.push({ source: "flag", matched: false });
|
|
915
|
-
const swGlobal = await parseDotenvFile(
|
|
1117
|
+
const swGlobal = await parseDotenvFile(join7(input.home, ".skillwiki", ".env"));
|
|
916
1118
|
const wikiName = input.wiki;
|
|
917
1119
|
if (wikiName !== void 0 && wikiName.length > 0) {
|
|
918
1120
|
if (wikiName.toLowerCase() === "default") {
|
|
@@ -956,7 +1158,7 @@ async function resolveRuntimePath(input) {
|
|
|
956
1158
|
}
|
|
957
1159
|
if (input.explain) chain.push({ source: "env", matched: false });
|
|
958
1160
|
if (input.cwd) {
|
|
959
|
-
const projCfg = await parseDotenvFile(
|
|
1161
|
+
const projCfg = await parseDotenvFile(join7(input.cwd, ".skillwiki", ".env"));
|
|
960
1162
|
if (projCfg.WIKI_PATH !== void 0) {
|
|
961
1163
|
if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
|
|
962
1164
|
return ok({ path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} });
|
|
@@ -1076,8 +1278,8 @@ function simulateRemoval(adj, removed) {
|
|
|
1076
1278
|
}
|
|
1077
1279
|
|
|
1078
1280
|
// src/commands/audit.ts
|
|
1079
|
-
import { readFile as
|
|
1080
|
-
import { dirname as dirname2, resolve as resolve2, join as
|
|
1281
|
+
import { readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
1282
|
+
import { dirname as dirname2, resolve as resolve2, join as join9 } from "path";
|
|
1081
1283
|
|
|
1082
1284
|
// src/parsers/citations.ts
|
|
1083
1285
|
var FENCE2 = /```[\s\S]*?```/g;
|
|
@@ -1189,7 +1391,7 @@ function hasWikilinkCitations(body) {
|
|
|
1189
1391
|
// src/utils/raw-source.ts
|
|
1190
1392
|
import { existsSync as existsSync4 } from "fs";
|
|
1191
1393
|
import { stat as stat2 } from "fs/promises";
|
|
1192
|
-
import { join as
|
|
1394
|
+
import { join as join8 } from "path";
|
|
1193
1395
|
function normalizeRawSourceTarget(entry) {
|
|
1194
1396
|
let target = entry.trim().replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
1195
1397
|
target = target.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
@@ -1199,11 +1401,11 @@ function normalizeRawSourceTarget(entry) {
|
|
|
1199
1401
|
function rawSourceTargetCandidates(vault, target) {
|
|
1200
1402
|
const normalized = normalizeRawSourceTarget(target);
|
|
1201
1403
|
if (!normalized) return [];
|
|
1202
|
-
const candidates = [
|
|
1203
|
-
if (!normalized.endsWith(".md")) candidates.push(
|
|
1404
|
+
const candidates = [join8(vault, normalized)];
|
|
1405
|
+
if (!normalized.endsWith(".md")) candidates.push(join8(vault, `${normalized}.md`));
|
|
1204
1406
|
if (normalized.startsWith("raw/")) {
|
|
1205
|
-
candidates.push(
|
|
1206
|
-
if (!normalized.endsWith(".md")) candidates.push(
|
|
1407
|
+
candidates.push(join8(vault, "_archive", normalized));
|
|
1408
|
+
if (!normalized.endsWith(".md")) candidates.push(join8(vault, "_archive", `${normalized}.md`));
|
|
1207
1409
|
}
|
|
1208
1410
|
return [...new Set(candidates)];
|
|
1209
1411
|
}
|
|
@@ -1225,7 +1427,7 @@ async function rawSourceTargetExists(vault, target) {
|
|
|
1225
1427
|
async function runAudit(input) {
|
|
1226
1428
|
let text;
|
|
1227
1429
|
try {
|
|
1228
|
-
text = await
|
|
1430
|
+
text = await readFile5(input.file, "utf8");
|
|
1229
1431
|
} catch {
|
|
1230
1432
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.file }) };
|
|
1231
1433
|
}
|
|
@@ -1278,7 +1480,7 @@ async function findVaultRoot(start) {
|
|
|
1278
1480
|
let cur = start;
|
|
1279
1481
|
for (let i = 0; i < 20; i++) {
|
|
1280
1482
|
try {
|
|
1281
|
-
await stat3(
|
|
1483
|
+
await stat3(join9(cur, "SCHEMA.md"));
|
|
1282
1484
|
return cur;
|
|
1283
1485
|
} catch {
|
|
1284
1486
|
}
|
|
@@ -1370,8 +1572,8 @@ ${broken.map((b) => ` ${b.page}:[[${b.slug}]] (line ${b.line})`).join("\n")}` }
|
|
|
1370
1572
|
}
|
|
1371
1573
|
|
|
1372
1574
|
// src/commands/tag-audit.ts
|
|
1373
|
-
import { readFile as
|
|
1374
|
-
import { join as
|
|
1575
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1576
|
+
import { join as join10 } from "path";
|
|
1375
1577
|
|
|
1376
1578
|
// src/parsers/taxonomy.ts
|
|
1377
1579
|
import yaml from "js-yaml";
|
|
@@ -1396,12 +1598,12 @@ function parseTaxonomyDocument(schemaText) {
|
|
|
1396
1598
|
const nextHeading = /^#{1,2}[ \t]+/m.exec(unboundedTail);
|
|
1397
1599
|
const sectionEnd = nextHeading?.index === void 0 ? schemaText.length : afterHeading + nextHeading.index;
|
|
1398
1600
|
const sectionText = schemaText.slice(afterHeading, sectionEnd);
|
|
1399
|
-
const
|
|
1400
|
-
if (!
|
|
1601
|
+
const open2 = /^```yaml[ \t]*\r?$/m.exec(sectionText);
|
|
1602
|
+
if (!open2 || open2.index === void 0) {
|
|
1401
1603
|
return err("NO_TAXONOMY_BLOCK", { message: "Fenced YAML taxonomy block not found" });
|
|
1402
1604
|
}
|
|
1403
|
-
const openStart = afterHeading +
|
|
1404
|
-
const yamlStart = openStart +
|
|
1605
|
+
const openStart = afterHeading + open2.index;
|
|
1606
|
+
const yamlStart = openStart + open2[0].length + 1;
|
|
1405
1607
|
const afterOpen = schemaText.slice(yamlStart, sectionEnd);
|
|
1406
1608
|
const close = /^```[ \t]*\r?$/m.exec(afterOpen);
|
|
1407
1609
|
if (!close || close.index === void 0) {
|
|
@@ -1524,7 +1726,7 @@ async function runTagAudit(input) {
|
|
|
1524
1726
|
const scanResult = input.scan ? ok(input.scan) : await scanVault(input.vault);
|
|
1525
1727
|
if (!scanResult.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
|
|
1526
1728
|
const scan = scanResult.data;
|
|
1527
|
-
const schemaText = await
|
|
1729
|
+
const schemaText = await readFile6(join10(input.vault, "SCHEMA.md"), "utf8");
|
|
1528
1730
|
const tax = extractTaxonomy(schemaText);
|
|
1529
1731
|
if (!tax.ok) return { exitCode: ExitCode.INVALID_FRONTMATTER, result: tax };
|
|
1530
1732
|
const allowed = new Set(tax.data);
|
|
@@ -1556,8 +1758,8 @@ async function runTagAudit(input) {
|
|
|
1556
1758
|
}
|
|
1557
1759
|
|
|
1558
1760
|
// src/commands/index-check.ts
|
|
1559
|
-
import { readFile as
|
|
1560
|
-
import { join as
|
|
1761
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
1762
|
+
import { join as join11 } from "path";
|
|
1561
1763
|
function normalizeIndexTarget(raw) {
|
|
1562
1764
|
return raw.replace(/\.md$/, "").replace(/^\.?\//, "");
|
|
1563
1765
|
}
|
|
@@ -1569,7 +1771,7 @@ async function runIndexCheck(input) {
|
|
|
1569
1771
|
}
|
|
1570
1772
|
let indexText = "";
|
|
1571
1773
|
try {
|
|
1572
|
-
indexText = await
|
|
1774
|
+
indexText = await readFile7(join11(input.vault, "index.md"), "utf8");
|
|
1573
1775
|
} catch {
|
|
1574
1776
|
}
|
|
1575
1777
|
const indexTargets = /* @__PURE__ */ new Set();
|
|
@@ -1623,20 +1825,20 @@ async function runIndexCheck(input) {
|
|
|
1623
1825
|
}
|
|
1624
1826
|
|
|
1625
1827
|
// src/commands/project-index.ts
|
|
1626
|
-
import { readdir, readFile as
|
|
1627
|
-
import { join as
|
|
1828
|
+
import { readdir as readdir2, readFile as readFile8, mkdir as mkdir3 } from "fs/promises";
|
|
1829
|
+
import { join as join12, dirname as dirname3, basename } from "path";
|
|
1628
1830
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
1629
1831
|
var PROJECT_LOCAL_DIRS = ["requirements", "work", "architecture", "history"];
|
|
1630
1832
|
async function scanMarkdownTree(rootAbs, rootRel) {
|
|
1631
1833
|
const found = [];
|
|
1632
1834
|
let entries;
|
|
1633
1835
|
try {
|
|
1634
|
-
entries = await
|
|
1836
|
+
entries = await readdir2(rootAbs, { withFileTypes: true });
|
|
1635
1837
|
} catch {
|
|
1636
1838
|
return found;
|
|
1637
1839
|
}
|
|
1638
1840
|
for (const entry of entries) {
|
|
1639
|
-
const abs =
|
|
1841
|
+
const abs = join12(rootAbs, entry.name);
|
|
1640
1842
|
const rel = `${rootRel}/${entry.name}`;
|
|
1641
1843
|
if (entry.isDirectory()) {
|
|
1642
1844
|
found.push(...await scanMarkdownTree(abs, rel));
|
|
@@ -1665,23 +1867,23 @@ function projectLocalType(slug, page, data) {
|
|
|
1665
1867
|
return typeof data.type === "string" ? data.type : "project";
|
|
1666
1868
|
}
|
|
1667
1869
|
async function renderProjectIndex(vault, slug, opts = {}) {
|
|
1668
|
-
const projectDir =
|
|
1870
|
+
const projectDir = join12(vault, "projects", slug);
|
|
1669
1871
|
try {
|
|
1670
|
-
await
|
|
1872
|
+
await readdir2(projectDir);
|
|
1671
1873
|
} catch {
|
|
1672
1874
|
return err("PROJECT_NOT_FOUND", { slug, path: projectDir });
|
|
1673
1875
|
}
|
|
1674
1876
|
const wikilinkPattern = `[[${slug}]]`;
|
|
1675
1877
|
const entries = [];
|
|
1676
|
-
const compoundDir =
|
|
1878
|
+
const compoundDir = join12(vault, "projects", slug, "compound");
|
|
1677
1879
|
try {
|
|
1678
|
-
const compoundFiles = await
|
|
1880
|
+
const compoundFiles = await readdir2(compoundDir, { withFileTypes: true });
|
|
1679
1881
|
for (const entry of compoundFiles) {
|
|
1680
1882
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
1681
|
-
const filePath =
|
|
1883
|
+
const filePath = join12(compoundDir, entry.name);
|
|
1682
1884
|
let text;
|
|
1683
1885
|
try {
|
|
1684
|
-
text = await
|
|
1886
|
+
text = await readFile8(filePath, "utf8");
|
|
1685
1887
|
} catch {
|
|
1686
1888
|
continue;
|
|
1687
1889
|
}
|
|
@@ -1698,16 +1900,16 @@ async function renderProjectIndex(vault, slug, opts = {}) {
|
|
|
1698
1900
|
for (const dir of LAYER2_DIRS) {
|
|
1699
1901
|
let files;
|
|
1700
1902
|
try {
|
|
1701
|
-
files = await
|
|
1903
|
+
files = await readdir2(join12(vault, dir), { withFileTypes: true });
|
|
1702
1904
|
} catch {
|
|
1703
1905
|
continue;
|
|
1704
1906
|
}
|
|
1705
1907
|
for (const entry of files) {
|
|
1706
1908
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
1707
|
-
const filePath =
|
|
1909
|
+
const filePath = join12(vault, dir, entry.name);
|
|
1708
1910
|
let text;
|
|
1709
1911
|
try {
|
|
1710
|
-
text = await
|
|
1912
|
+
text = await readFile8(filePath, "utf8");
|
|
1711
1913
|
} catch {
|
|
1712
1914
|
continue;
|
|
1713
1915
|
}
|
|
@@ -1723,14 +1925,14 @@ async function renderProjectIndex(vault, slug, opts = {}) {
|
|
|
1723
1925
|
}
|
|
1724
1926
|
}
|
|
1725
1927
|
for (const dir of PROJECT_LOCAL_DIRS) {
|
|
1726
|
-
const rootAbs =
|
|
1928
|
+
const rootAbs = join12(projectDir, dir);
|
|
1727
1929
|
const rootRel = `projects/${slug}/${dir}`;
|
|
1728
1930
|
const pages = await scanMarkdownTree(rootAbs, rootRel);
|
|
1729
1931
|
for (const page of pages) {
|
|
1730
|
-
const filePath =
|
|
1932
|
+
const filePath = join12(vault, page);
|
|
1731
1933
|
let text;
|
|
1732
1934
|
try {
|
|
1733
|
-
text = await
|
|
1935
|
+
text = await readFile8(filePath, "utf8");
|
|
1734
1936
|
} catch {
|
|
1735
1937
|
continue;
|
|
1736
1938
|
}
|
|
@@ -1803,7 +2005,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
1803
2005
|
}
|
|
1804
2006
|
async function runProjectIndex(input) {
|
|
1805
2007
|
const slug = input.slug;
|
|
1806
|
-
const projectDir =
|
|
2008
|
+
const projectDir = join12(input.vault, "projects", slug);
|
|
1807
2009
|
const rendered = await renderProjectIndex(input.vault, slug);
|
|
1808
2010
|
if (!rendered.ok) {
|
|
1809
2011
|
return {
|
|
@@ -1811,12 +2013,12 @@ async function runProjectIndex(input) {
|
|
|
1811
2013
|
result: rendered
|
|
1812
2014
|
};
|
|
1813
2015
|
}
|
|
1814
|
-
const indexPath =
|
|
2016
|
+
const indexPath = join12(projectDir, "knowledge.md");
|
|
1815
2017
|
const entries = rendered.data.entries;
|
|
1816
2018
|
let existing = false;
|
|
1817
2019
|
let stale = false;
|
|
1818
2020
|
try {
|
|
1819
|
-
const existingText = await
|
|
2021
|
+
const existingText = await readFile8(indexPath, "utf8");
|
|
1820
2022
|
existing = true;
|
|
1821
2023
|
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
1822
2024
|
const existingPages = new Set(existingEntries.map((l) => {
|
|
@@ -1829,7 +2031,7 @@ async function runProjectIndex(input) {
|
|
|
1829
2031
|
}
|
|
1830
2032
|
if (input.apply) {
|
|
1831
2033
|
try {
|
|
1832
|
-
await
|
|
2034
|
+
await mkdir3(dirname3(indexPath), { recursive: true });
|
|
1833
2035
|
} catch (e) {
|
|
1834
2036
|
return {
|
|
1835
2037
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -1861,8 +2063,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
1861
2063
|
}
|
|
1862
2064
|
|
|
1863
2065
|
// src/commands/stale.ts
|
|
1864
|
-
import { readdir as
|
|
1865
|
-
import { join as
|
|
2066
|
+
import { readdir as readdir3, rename, mkdir as mkdir4, readFile as readFile9 } from "fs/promises";
|
|
2067
|
+
import { join as join13 } from "path";
|
|
1866
2068
|
|
|
1867
2069
|
// src/parsers/expiry-annotations.ts
|
|
1868
2070
|
var HEADING_RE = /^#{1,6}\s+(.+)$/;
|
|
@@ -1911,10 +2113,10 @@ async function runStale(input) {
|
|
|
1911
2113
|
const archived = [];
|
|
1912
2114
|
const workDirs = /* @__PURE__ */ new Map();
|
|
1913
2115
|
const workDirsBySlug = /* @__PURE__ */ new Map();
|
|
1914
|
-
const projectsDir =
|
|
2116
|
+
const projectsDir = join13(input.vault, "projects");
|
|
1915
2117
|
let projectSlugs = [];
|
|
1916
2118
|
try {
|
|
1917
|
-
projectSlugs = (await
|
|
2119
|
+
projectSlugs = (await readdir3(projectsDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
1918
2120
|
} catch {
|
|
1919
2121
|
}
|
|
1920
2122
|
if (input.project) {
|
|
@@ -1924,10 +2126,10 @@ async function runStale(input) {
|
|
|
1924
2126
|
projectSlugs = [input.project];
|
|
1925
2127
|
}
|
|
1926
2128
|
for (const slug of projectSlugs) {
|
|
1927
|
-
const workPath =
|
|
2129
|
+
const workPath = join13(projectsDir, slug, "work");
|
|
1928
2130
|
let entries;
|
|
1929
2131
|
try {
|
|
1930
|
-
entries = await
|
|
2132
|
+
entries = await readdir3(workPath, { withFileTypes: true });
|
|
1931
2133
|
} catch {
|
|
1932
2134
|
continue;
|
|
1933
2135
|
}
|
|
@@ -1935,11 +2137,11 @@ async function runStale(input) {
|
|
|
1935
2137
|
for (const e of entries) {
|
|
1936
2138
|
if (!e.isDirectory()) continue;
|
|
1937
2139
|
const relDir = `projects/${slug}/work/${e.name}`;
|
|
1938
|
-
const absDir =
|
|
2140
|
+
const absDir = join13(workPath, e.name);
|
|
1939
2141
|
let status = "";
|
|
1940
2142
|
let files;
|
|
1941
2143
|
try {
|
|
1942
|
-
files = await
|
|
2144
|
+
files = await readdir3(absDir);
|
|
1943
2145
|
} catch {
|
|
1944
2146
|
workDirs.set(relDir, "");
|
|
1945
2147
|
slugDirs.set(e.name, "");
|
|
@@ -1948,7 +2150,7 @@ async function runStale(input) {
|
|
|
1948
2150
|
for (const f of files) {
|
|
1949
2151
|
if (!f.endsWith(".md")) continue;
|
|
1950
2152
|
try {
|
|
1951
|
-
const fm = extractFrontmatter(await
|
|
2153
|
+
const fm = extractFrontmatter(await readFile9(join13(absDir, f), "utf8"));
|
|
1952
2154
|
if (fm.ok && typeof fm.data.status === "string") {
|
|
1953
2155
|
status = fm.data.status;
|
|
1954
2156
|
break;
|
|
@@ -2044,9 +2246,9 @@ async function runStale(input) {
|
|
|
2044
2246
|
}
|
|
2045
2247
|
}
|
|
2046
2248
|
await mapWithConcurrency([...workDirs.keys()], vaultIoConcurrency(), async (relDir) => {
|
|
2047
|
-
const specPath =
|
|
2249
|
+
const specPath = join13(input.vault, relDir, "spec.md");
|
|
2048
2250
|
try {
|
|
2049
|
-
const specContent = await
|
|
2251
|
+
const specContent = await readFile9(specPath, "utf8");
|
|
2050
2252
|
const specFm = extractFrontmatter(specContent);
|
|
2051
2253
|
if (specFm.ok && typeof specFm.data.source === "string") {
|
|
2052
2254
|
const sourcePath = specFm.data.source;
|
|
@@ -2075,7 +2277,7 @@ async function runStale(input) {
|
|
|
2075
2277
|
if (daysSince(dateStr) < input.days) continue;
|
|
2076
2278
|
let files;
|
|
2077
2279
|
try {
|
|
2078
|
-
files = await
|
|
2280
|
+
files = await readdir3(join13(input.vault, relDir));
|
|
2079
2281
|
} catch {
|
|
2080
2282
|
continue;
|
|
2081
2283
|
}
|
|
@@ -2145,8 +2347,8 @@ async function runStale(input) {
|
|
|
2145
2347
|
staleSections.push(...staleSectionResults.flat());
|
|
2146
2348
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2147
2349
|
if (input.archive) {
|
|
2148
|
-
const archiveDir =
|
|
2149
|
-
await
|
|
2350
|
+
const archiveDir = join13(input.vault, "_archive", today);
|
|
2351
|
+
await mkdir4(archiveDir, { recursive: true });
|
|
2150
2352
|
const citedRawPaths = /* @__PURE__ */ new Set();
|
|
2151
2353
|
for (const page of scan.typedKnowledge) {
|
|
2152
2354
|
const text = await readPageCached(page, input.pageTextCache).catch(() => "");
|
|
@@ -2161,9 +2363,9 @@ async function runStale(input) {
|
|
|
2161
2363
|
}
|
|
2162
2364
|
for (const t of staleTranscripts) {
|
|
2163
2365
|
if (citedRawPaths.has(t.path) || citedRawPaths.has(t.path.replace(/\.md$/, ""))) continue;
|
|
2164
|
-
const dest =
|
|
2366
|
+
const dest = join13(archiveDir, t.path.split("/").pop());
|
|
2165
2367
|
try {
|
|
2166
|
-
await rename(
|
|
2368
|
+
await rename(join13(input.vault, t.path), dest);
|
|
2167
2369
|
archived.push(t.path);
|
|
2168
2370
|
} catch {
|
|
2169
2371
|
}
|
|
@@ -2173,18 +2375,18 @@ async function runStale(input) {
|
|
|
2173
2375
|
if (parts.length >= 4 && parts[0] === "projects") {
|
|
2174
2376
|
const slug = parts[1];
|
|
2175
2377
|
const itemName = parts[3];
|
|
2176
|
-
const histDir =
|
|
2177
|
-
await
|
|
2178
|
-
const dest =
|
|
2378
|
+
const histDir = join13(input.vault, "projects", slug, "history", "archived-work");
|
|
2379
|
+
await mkdir4(histDir, { recursive: true });
|
|
2380
|
+
const dest = join13(histDir, itemName);
|
|
2179
2381
|
try {
|
|
2180
|
-
await rename(
|
|
2382
|
+
await rename(join13(input.vault, w.path), dest);
|
|
2181
2383
|
archived.push(w.path);
|
|
2182
2384
|
} catch {
|
|
2183
2385
|
}
|
|
2184
2386
|
} else {
|
|
2185
|
-
const dest =
|
|
2387
|
+
const dest = join13(archiveDir, w.path.replace(/\//g, "_"));
|
|
2186
2388
|
try {
|
|
2187
|
-
await rename(
|
|
2389
|
+
await rename(join13(input.vault, w.path), dest);
|
|
2188
2390
|
archived.push(w.path);
|
|
2189
2391
|
} catch {
|
|
2190
2392
|
}
|
|
@@ -2239,19 +2441,19 @@ async function runPagesize(input) {
|
|
|
2239
2441
|
}
|
|
2240
2442
|
|
|
2241
2443
|
// src/commands/log-rotate.ts
|
|
2242
|
-
import { readFile as
|
|
2243
|
-
import { join as
|
|
2444
|
+
import { readFile as readFile10, rename as rename2, writeFile as writeFile2, stat as stat4 } from "fs/promises";
|
|
2445
|
+
import { join as join14 } from "path";
|
|
2244
2446
|
var ENTRY_RE2 = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
|
|
2245
2447
|
async function runLogRotate(input) {
|
|
2246
2448
|
try {
|
|
2247
|
-
await stat4(
|
|
2449
|
+
await stat4(join14(input.vault, "SCHEMA.md"));
|
|
2248
2450
|
} catch {
|
|
2249
2451
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
2250
2452
|
}
|
|
2251
|
-
const logPath =
|
|
2453
|
+
const logPath = join14(input.vault, "log.md");
|
|
2252
2454
|
let logText;
|
|
2253
2455
|
try {
|
|
2254
|
-
logText = await
|
|
2456
|
+
logText = await readFile10(logPath, "utf8");
|
|
2255
2457
|
} catch {
|
|
2256
2458
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
2257
2459
|
}
|
|
@@ -2268,7 +2470,7 @@ async function runLogRotate(input) {
|
|
|
2268
2470
|
}
|
|
2269
2471
|
const newestYear = matches[matches.length - 1][1];
|
|
2270
2472
|
const rotatedName = `log-${newestYear}.md`;
|
|
2271
|
-
const rotatedPath =
|
|
2473
|
+
const rotatedPath = join14(input.vault, rotatedName);
|
|
2272
2474
|
try {
|
|
2273
2475
|
await rename2(logPath, rotatedPath);
|
|
2274
2476
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -2313,13 +2515,13 @@ async function runTopicMapCheck(input) {
|
|
|
2313
2515
|
}
|
|
2314
2516
|
|
|
2315
2517
|
// src/commands/index-link-format.ts
|
|
2316
|
-
import { readFile as
|
|
2317
|
-
import { join as
|
|
2518
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
2519
|
+
import { join as join15 } from "path";
|
|
2318
2520
|
var MD_LINK_RE = /\[[^\[\]]+\]\([^)]+\.md\)/;
|
|
2319
2521
|
async function runIndexLinkFormat(input) {
|
|
2320
2522
|
let text = "";
|
|
2321
2523
|
try {
|
|
2322
|
-
text = await
|
|
2524
|
+
text = await readFile11(join15(input.vault, "index.md"), "utf8");
|
|
2323
2525
|
} catch {
|
|
2324
2526
|
}
|
|
2325
2527
|
const markdown_links = [];
|
|
@@ -2334,7 +2536,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
|
|
|
2334
2536
|
// src/commands/dedup.ts
|
|
2335
2537
|
import { createHash as createHash3 } from "crypto";
|
|
2336
2538
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
|
|
2337
|
-
import { dirname as dirname4, join as
|
|
2539
|
+
import { dirname as dirname4, join as join16, resolve as resolve3 } from "path";
|
|
2338
2540
|
|
|
2339
2541
|
// src/utils/rclone.ts
|
|
2340
2542
|
import { execFile } from "child_process";
|
|
@@ -2453,7 +2655,7 @@ async function runDedup(input) {
|
|
|
2453
2655
|
}
|
|
2454
2656
|
}
|
|
2455
2657
|
for (const page of scan.allMarkdown.filter((p) => !p.relPath.startsWith("raw/"))) {
|
|
2456
|
-
const text = readFileSync4(
|
|
2658
|
+
const text = readFileSync4(join16(input.vault, page.relPath), "utf-8");
|
|
2457
2659
|
let updated = text;
|
|
2458
2660
|
let changed = false;
|
|
2459
2661
|
for (const [oldPath, newPath] of replacements) {
|
|
@@ -2471,12 +2673,12 @@ async function runDedup(input) {
|
|
|
2471
2673
|
}
|
|
2472
2674
|
}
|
|
2473
2675
|
if (changed) {
|
|
2474
|
-
writeFileSync4(
|
|
2676
|
+
writeFileSync4(join16(input.vault, page.relPath), updated);
|
|
2475
2677
|
rewired.push(page.relPath);
|
|
2476
2678
|
}
|
|
2477
2679
|
}
|
|
2478
2680
|
for (const oldPath of replacements.keys()) {
|
|
2479
|
-
const fullPath =
|
|
2681
|
+
const fullPath = join16(input.vault, oldPath);
|
|
2480
2682
|
try {
|
|
2481
2683
|
unlinkSync4(fullPath);
|
|
2482
2684
|
removed.push(oldPath);
|
|
@@ -2570,7 +2772,7 @@ function buildSafeEntries(vault, duplicates, unsafe) {
|
|
|
2570
2772
|
return entries;
|
|
2571
2773
|
}
|
|
2572
2774
|
function hashRawBody(vault, relPath) {
|
|
2573
|
-
const text = readFileSync4(
|
|
2775
|
+
const text = readFileSync4(join16(vault, relPath), "utf-8");
|
|
2574
2776
|
const split = splitFrontmatter(text);
|
|
2575
2777
|
const body = split.ok ? split.data.body : text;
|
|
2576
2778
|
return createHash3("sha256").update(body).digest("hex");
|
|
@@ -2593,7 +2795,7 @@ async function planAndMaybePruneRemote(input, entries) {
|
|
|
2593
2795
|
}
|
|
2594
2796
|
|
|
2595
2797
|
// src/utils/safe-write.ts
|
|
2596
|
-
import { readFile as
|
|
2798
|
+
import { readFile as readFile12, writeFile as writeFile3 } from "fs/promises";
|
|
2597
2799
|
var DEFAULT_MIN_BODY_RATIO = 0.5;
|
|
2598
2800
|
var DEFAULT_MIN_OLD_BODY_BYTES = 200;
|
|
2599
2801
|
function bodyBytes(text) {
|
|
@@ -2603,7 +2805,7 @@ function bodyBytes(text) {
|
|
|
2603
2805
|
}
|
|
2604
2806
|
async function readIfExists(absPath) {
|
|
2605
2807
|
try {
|
|
2606
|
-
return await
|
|
2808
|
+
return await readFile12(absPath, "utf8");
|
|
2607
2809
|
} catch (e) {
|
|
2608
2810
|
if (e.code === "ENOENT") return null;
|
|
2609
2811
|
throw e;
|
|
@@ -2727,9 +2929,9 @@ ${newBody}`;
|
|
|
2727
2929
|
|
|
2728
2930
|
// src/commands/lint.ts
|
|
2729
2931
|
import { existsSync as existsSync6 } from "fs";
|
|
2730
|
-
import { readFile as
|
|
2932
|
+
import { readFile as readFile14, readdir as readdir4 } from "fs/promises";
|
|
2731
2933
|
import { createHash as createHash5 } from "crypto";
|
|
2732
|
-
import { join as
|
|
2934
|
+
import { join as join18, relative as relative2, sep as sep2 } from "path";
|
|
2733
2935
|
|
|
2734
2936
|
// src/commands/sparse-community.ts
|
|
2735
2937
|
async function runSparseCommunity(input) {
|
|
@@ -2787,8 +2989,8 @@ async function runRawBodyDedup(vault, scan, pageTextCache) {
|
|
|
2787
2989
|
|
|
2788
2990
|
// src/commands/path-too-long.ts
|
|
2789
2991
|
import { existsSync as existsSync5 } from "fs";
|
|
2790
|
-
import { mkdir as
|
|
2791
|
-
import { dirname as dirname5, join as
|
|
2992
|
+
import { mkdir as mkdir5, readFile as readFile13, rename as rename3, unlink } from "fs/promises";
|
|
2993
|
+
import { dirname as dirname5, join as join17, posix, resolve as resolve4 } from "path";
|
|
2792
2994
|
var MAX_PATH_LENGTH = 240;
|
|
2793
2995
|
var WINDOWS_ABSOLUTE_PATH_LIMIT = 259;
|
|
2794
2996
|
async function runPathTooLong(input) {
|
|
@@ -2821,10 +3023,10 @@ async function fixPathTooLong(input) {
|
|
|
2821
3023
|
}
|
|
2822
3024
|
try {
|
|
2823
3025
|
if (target.mode === "dedupe") {
|
|
2824
|
-
await unlink(
|
|
3026
|
+
await unlink(join17(input.vault, violation.relPath));
|
|
2825
3027
|
} else {
|
|
2826
|
-
await
|
|
2827
|
-
await rename3(
|
|
3028
|
+
await mkdir5(dirname5(join17(input.vault, target.relPath)), { recursive: true });
|
|
3029
|
+
await rename3(join17(input.vault, violation.relPath), join17(input.vault, target.relPath));
|
|
2828
3030
|
}
|
|
2829
3031
|
fixed.push({ from: violation.relPath, to: target.relPath });
|
|
2830
3032
|
} catch {
|
|
@@ -2838,7 +3040,7 @@ async function fixPathTooLong(input) {
|
|
|
2838
3040
|
for (const page of afterScan.data.allMarkdown) {
|
|
2839
3041
|
if (!shouldRewriteReferences(page.relPath)) continue;
|
|
2840
3042
|
try {
|
|
2841
|
-
const original = await
|
|
3043
|
+
const original = await readFile13(page.absPath, "utf8");
|
|
2842
3044
|
let updated = original;
|
|
2843
3045
|
for (const fix of fixed) {
|
|
2844
3046
|
updated = replacePathReferences(updated, fix.from, fix.to);
|
|
@@ -2901,9 +3103,9 @@ function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
|
2901
3103
|
async function resolveFixTarget(vault, original, preferred, maxLength) {
|
|
2902
3104
|
for (const candidate of candidateRelPaths(preferred, maxLength)) {
|
|
2903
3105
|
if (candidate === original || candidate.length > maxLength) continue;
|
|
2904
|
-
const candidatePath =
|
|
3106
|
+
const candidatePath = join17(vault, candidate);
|
|
2905
3107
|
if (!existsSync5(candidatePath)) return { relPath: candidate, mode: "rename" };
|
|
2906
|
-
if (await hasSameContent(
|
|
3108
|
+
if (await hasSameContent(join17(vault, original), candidatePath)) {
|
|
2907
3109
|
return { relPath: candidate, mode: "dedupe" };
|
|
2908
3110
|
}
|
|
2909
3111
|
}
|
|
@@ -2927,7 +3129,7 @@ function candidateRelPaths(preferred, maxLength) {
|
|
|
2927
3129
|
}
|
|
2928
3130
|
async function hasSameContent(a, b) {
|
|
2929
3131
|
try {
|
|
2930
|
-
const [left, right] = await Promise.all([
|
|
3132
|
+
const [left, right] = await Promise.all([readFile13(a), readFile13(b)]);
|
|
2931
3133
|
return left.equals(right);
|
|
2932
3134
|
} catch {
|
|
2933
3135
|
return false;
|
|
@@ -2974,7 +3176,7 @@ function buildCliSurface() {
|
|
|
2974
3176
|
program.command("orphans").option("--wiki <name>");
|
|
2975
3177
|
program.command("audit");
|
|
2976
3178
|
program.command("install").option("--target <dir>").option("--dry-run").option("--skills-root <dir>").option("--symlink");
|
|
2977
|
-
program.command("path").option("--vault <dir>").option("--target <dir>").option("--wiki <name>").option("--init-time").option("--explain");
|
|
3179
|
+
program.command("path").option("--vault <dir>").option("--target <dir>").option("--wiki <name>").option("--init-time").option("--explain").option("--plain");
|
|
2978
3180
|
program.command("lang").option("--lang <code>").option("--explain");
|
|
2979
3181
|
program.command("init").option("--target <dir>").requiredOption("--domain <text>").option("--taxonomy <csv>").option("--lang <code>").option("--force").option("--no-env").option("--profile <name>");
|
|
2980
3182
|
program.command("links").option("--wiki <name>");
|
|
@@ -2986,7 +3188,9 @@ function buildCliSurface() {
|
|
|
2986
3188
|
program.command("claim").option("--project <slug>").option("--slug <slug>").option("--wiki <name>");
|
|
2987
3189
|
program.command("pagesize").option("--lines <n>").option("--wiki <name>");
|
|
2988
3190
|
program.command("log-rotate").option("--threshold <n>").option("--apply").option("--wiki <name>");
|
|
2989
|
-
program.command("log-append").requiredOption("--content <text>").option("--wiki <name>");
|
|
3191
|
+
program.command("log-append").requiredOption("--content <text>").option("--operation-id <id>").option("--write-event").option("--wiki <name>");
|
|
3192
|
+
program.command("work-complete").requiredOption("--work-item <path>").option("--operation-id <id>").option("--no-commit").option("--wiki <name>");
|
|
3193
|
+
program.command("work-validate").requiredOption("--work-item <path>").option("--require-complete").option("--wiki <name>");
|
|
2990
3194
|
program.command("lint").option("--days <n>").option("--lines <n>").option("--log-threshold <n>").option("--fix").option("--only <bucket>").option("--summary").option("--examples <n>").option("--wiki <name>");
|
|
2991
3195
|
program.command("config");
|
|
2992
3196
|
program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
|
|
@@ -3388,10 +3592,10 @@ function summarizeLintOutput(output, examplesLimit = 3) {
|
|
|
3388
3592
|
};
|
|
3389
3593
|
}
|
|
3390
3594
|
async function walkMarkdownFiles(absDir, vaultRoot) {
|
|
3391
|
-
const entries = await
|
|
3595
|
+
const entries = await readdir4(absDir, { withFileTypes: true });
|
|
3392
3596
|
const pages = [];
|
|
3393
3597
|
for (const entry of entries) {
|
|
3394
|
-
const absPath =
|
|
3598
|
+
const absPath = join18(absDir, entry.name);
|
|
3395
3599
|
if (entry.isDirectory()) {
|
|
3396
3600
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
3397
3601
|
pages.push(...await walkMarkdownFiles(absPath, vaultRoot));
|
|
@@ -3402,12 +3606,12 @@ async function walkMarkdownFiles(absDir, vaultRoot) {
|
|
|
3402
3606
|
return pages;
|
|
3403
3607
|
}
|
|
3404
3608
|
async function collectCliRefsPages(vault) {
|
|
3405
|
-
if (!existsSync6(
|
|
3609
|
+
if (!existsSync6(join18(vault, "SCHEMA.md"))) {
|
|
3406
3610
|
return err("VAULT_PATH_INVALID", { root: vault, reason: "SCHEMA.md missing" });
|
|
3407
3611
|
}
|
|
3408
3612
|
const pages = [];
|
|
3409
3613
|
for (const dir of CLI_REFS_TYPED_DIRS) {
|
|
3410
|
-
const absDir =
|
|
3614
|
+
const absDir = join18(vault, dir);
|
|
3411
3615
|
if (!existsSync6(absDir)) continue;
|
|
3412
3616
|
pages.push(...await walkMarkdownFiles(absDir, vault));
|
|
3413
3617
|
}
|
|
@@ -3529,7 +3733,7 @@ async function applyFileSourceUrlFix(input, scan, fileSourceUrlFlags, fileSource
|
|
|
3529
3733
|
for (const relPath of fileSourceUrlFrontmatterFlags) {
|
|
3530
3734
|
try {
|
|
3531
3735
|
const absPath = `${input.vault}/${relPath}`;
|
|
3532
|
-
const raw = await
|
|
3736
|
+
const raw = await readFile14(absPath, "utf8");
|
|
3533
3737
|
const parts = raw.split("---", 3);
|
|
3534
3738
|
if (parts.length < 3) {
|
|
3535
3739
|
unresolved.push(relPath);
|
|
@@ -3922,8 +4126,8 @@ async function runLint(input) {
|
|
|
3922
4126
|
const readKnowledgeContent = (slug) => {
|
|
3923
4127
|
const existing = knowledgeContentCache.get(slug);
|
|
3924
4128
|
if (existing) return existing;
|
|
3925
|
-
const knowledgePath =
|
|
3926
|
-
const pending = existsSync6(knowledgePath) ?
|
|
4129
|
+
const knowledgePath = join18(lintVault, "projects", slug, "knowledge.md");
|
|
4130
|
+
const pending = existsSync6(knowledgePath) ? readFile14(knowledgePath, "utf8").catch(() => null) : Promise.resolve(null);
|
|
3927
4131
|
knowledgeContentCache.set(slug, pending);
|
|
3928
4132
|
return pending;
|
|
3929
4133
|
};
|
|
@@ -4029,7 +4233,7 @@ async function runLint(input) {
|
|
|
4029
4233
|
for (const relPath of legacyPages) {
|
|
4030
4234
|
try {
|
|
4031
4235
|
const absPath = `${input.vault}/${relPath}`;
|
|
4032
|
-
const raw = await
|
|
4236
|
+
const raw = await readFile14(absPath, "utf8");
|
|
4033
4237
|
const split = splitFrontmatter(raw);
|
|
4034
4238
|
if (!split.ok) {
|
|
4035
4239
|
unresolved.push(relPath);
|
|
@@ -4128,7 +4332,7 @@ ${newBody}`;
|
|
|
4128
4332
|
for (const relPath of noOverview) {
|
|
4129
4333
|
try {
|
|
4130
4334
|
const absPath = `${input.vault}/${relPath}`;
|
|
4131
|
-
const raw = await
|
|
4335
|
+
const raw = await readFile14(absPath, "utf8");
|
|
4132
4336
|
const split = splitFrontmatter(raw);
|
|
4133
4337
|
if (!split.ok) {
|
|
4134
4338
|
unresolved.push(relPath);
|
|
@@ -4169,7 +4373,7 @@ ${trimmedBody}`;
|
|
|
4169
4373
|
for (const relPath of missingTldrFlags) {
|
|
4170
4374
|
try {
|
|
4171
4375
|
const absPath = `${input.vault}/${relPath}`;
|
|
4172
|
-
const raw = await
|
|
4376
|
+
const raw = await readFile14(absPath, "utf8");
|
|
4173
4377
|
const split = splitFrontmatter(raw);
|
|
4174
4378
|
if (!split.ok) {
|
|
4175
4379
|
unresolved.push(relPath);
|
|
@@ -4219,7 +4423,7 @@ ${lines.join("\n")}`;
|
|
|
4219
4423
|
for (const relPath of wikilinkCitationFlags) {
|
|
4220
4424
|
try {
|
|
4221
4425
|
const absPath = `${input.vault}/${relPath}`;
|
|
4222
|
-
const raw = await
|
|
4426
|
+
const raw = await readFile14(absPath, "utf8");
|
|
4223
4427
|
const split = splitFrontmatter(raw);
|
|
4224
4428
|
if (!split.ok) {
|
|
4225
4429
|
unresolved.push(relPath);
|
|
@@ -4560,14 +4764,14 @@ async function runSyncLintDelta(input) {
|
|
|
4560
4764
|
}
|
|
4561
4765
|
|
|
4562
4766
|
// src/commands/config.ts
|
|
4563
|
-
import { readFile as
|
|
4767
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
4564
4768
|
import { existsSync as existsSync7 } from "fs";
|
|
4565
|
-
import { join as
|
|
4769
|
+
import { join as join19 } from "path";
|
|
4566
4770
|
function validateKey(key) {
|
|
4567
4771
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
4568
4772
|
}
|
|
4569
4773
|
function configPath(home) {
|
|
4570
|
-
return
|
|
4774
|
+
return join19(home, ".skillwiki", ".env");
|
|
4571
4775
|
}
|
|
4572
4776
|
async function runConfigGet(input) {
|
|
4573
4777
|
if (!validateKey(input.key)) {
|
|
@@ -4585,7 +4789,7 @@ async function runConfigSet(input) {
|
|
|
4585
4789
|
try {
|
|
4586
4790
|
let originalContent;
|
|
4587
4791
|
try {
|
|
4588
|
-
originalContent = await
|
|
4792
|
+
originalContent = await readFile15(filePath, "utf8");
|
|
4589
4793
|
} catch {
|
|
4590
4794
|
}
|
|
4591
4795
|
const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
|
|
@@ -4622,19 +4826,19 @@ async function runConfigPath(input) {
|
|
|
4622
4826
|
|
|
4623
4827
|
// src/commands/doctor.ts
|
|
4624
4828
|
import { existsSync as existsSync13, lstatSync, readlinkSync, readdirSync as readdirSync3, statSync as statSync2, readFileSync as readFileSync10 } from "fs";
|
|
4625
|
-
import { join as
|
|
4829
|
+
import { join as join25, resolve as resolve5 } from "path";
|
|
4626
4830
|
import { execSync as execSync2 } from "child_process";
|
|
4627
4831
|
import { platform as platform2 } from "os";
|
|
4628
4832
|
|
|
4629
4833
|
// src/utils/plugin-registry.ts
|
|
4630
4834
|
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
4631
|
-
import { join as
|
|
4632
|
-
var REGISTRY_PATH =
|
|
4633
|
-
var CODEX_CONFIG_PATH =
|
|
4835
|
+
import { join as join20 } from "path";
|
|
4836
|
+
var REGISTRY_PATH = join20(".claude", "plugins", "installed_plugins.json");
|
|
4837
|
+
var CODEX_CONFIG_PATH = join20(".codex", "config.toml");
|
|
4634
4838
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
4635
4839
|
function readInstalledPlugins(home) {
|
|
4636
4840
|
try {
|
|
4637
|
-
const raw = readFileSync5(
|
|
4841
|
+
const raw = readFileSync5(join20(home, REGISTRY_PATH), "utf8");
|
|
4638
4842
|
return JSON.parse(raw);
|
|
4639
4843
|
} catch {
|
|
4640
4844
|
return null;
|
|
@@ -4670,7 +4874,7 @@ function findPluginInstallations(home, key = PLUGIN_KEY) {
|
|
|
4670
4874
|
function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
4671
4875
|
const config = readCodexPluginConfig(home, key, marketplace);
|
|
4672
4876
|
if (!config?.enabled) return null;
|
|
4673
|
-
const cacheRoot =
|
|
4877
|
+
const cacheRoot = join20(home, ".codex", "plugins", "cache", marketplace, pluginName);
|
|
4674
4878
|
if (!existsSync8(cacheRoot)) return null;
|
|
4675
4879
|
let versions;
|
|
4676
4880
|
try {
|
|
@@ -4686,7 +4890,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
|
4686
4890
|
key,
|
|
4687
4891
|
pluginName,
|
|
4688
4892
|
marketplace,
|
|
4689
|
-
installPath:
|
|
4893
|
+
installPath: join20(cacheRoot, version),
|
|
4690
4894
|
version,
|
|
4691
4895
|
sourceType: config.sourceType,
|
|
4692
4896
|
source: config.source
|
|
@@ -4703,7 +4907,7 @@ function parsePluginKey(key) {
|
|
|
4703
4907
|
function readCodexPluginConfig(home, key, marketplace) {
|
|
4704
4908
|
let raw;
|
|
4705
4909
|
try {
|
|
4706
|
-
raw = readFileSync5(
|
|
4910
|
+
raw = readFileSync5(join20(home, CODEX_CONFIG_PATH), "utf8");
|
|
4707
4911
|
} catch {
|
|
4708
4912
|
return null;
|
|
4709
4913
|
}
|
|
@@ -4746,7 +4950,7 @@ function parseTomlScalar(rawValue) {
|
|
|
4746
4950
|
|
|
4747
4951
|
// src/utils/conflict-markers.ts
|
|
4748
4952
|
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
4749
|
-
import { join as
|
|
4953
|
+
import { join as join21 } from "path";
|
|
4750
4954
|
function scanConflictMarkerBlocksInText(relPath, text) {
|
|
4751
4955
|
const findings = [];
|
|
4752
4956
|
const lines = text.split(/\r?\n/);
|
|
@@ -4797,7 +5001,7 @@ function walkMarkdownFiles2(root, dir, rel, out) {
|
|
|
4797
5001
|
for (const entry of entries) {
|
|
4798
5002
|
if (entry.isDirectory()) {
|
|
4799
5003
|
if (PRUNE_DIRS.has(entry.name)) continue;
|
|
4800
|
-
walkMarkdownFiles2(root,
|
|
5004
|
+
walkMarkdownFiles2(root, join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name, out);
|
|
4801
5005
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
4802
5006
|
out.push(rel ? `${rel}/${entry.name}` : entry.name);
|
|
4803
5007
|
}
|
|
@@ -4811,7 +5015,7 @@ function scanVaultConflictMarkers(vaultRoot) {
|
|
|
4811
5015
|
for (const rel of relPaths) {
|
|
4812
5016
|
let text;
|
|
4813
5017
|
try {
|
|
4814
|
-
text = readFileSync6(
|
|
5018
|
+
text = readFileSync6(join21(vaultRoot, rel), "utf8");
|
|
4815
5019
|
} catch {
|
|
4816
5020
|
continue;
|
|
4817
5021
|
}
|
|
@@ -4822,7 +5026,7 @@ function scanVaultConflictMarkers(vaultRoot) {
|
|
|
4822
5026
|
|
|
4823
5027
|
// src/utils/remote-health.ts
|
|
4824
5028
|
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
4825
|
-
import { join as
|
|
5029
|
+
import { join as join22 } from "path";
|
|
4826
5030
|
import { execFileSync } from "child_process";
|
|
4827
5031
|
var REMOTE_PROBE_TIMEOUT_MS = 3e3;
|
|
4828
5032
|
var defaultExec = (file, args, cwd) => execFileSync(file, args, {
|
|
@@ -4833,7 +5037,7 @@ var defaultExec = (file, args, cwd) => execFileSync(file, args, {
|
|
|
4833
5037
|
}).trim();
|
|
4834
5038
|
function readWikiS3RemoteConfigured(home) {
|
|
4835
5039
|
try {
|
|
4836
|
-
const content = readFileSync7(
|
|
5040
|
+
const content = readFileSync7(join22(home, ".skillwiki", ".env"), "utf8");
|
|
4837
5041
|
for (const line of content.split(/\r?\n/)) {
|
|
4838
5042
|
const trimmed = line.trim();
|
|
4839
5043
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -4858,7 +5062,7 @@ function resolveWikiS3Remote(input) {
|
|
|
4858
5062
|
return readWikiS3RemoteConfigured(input.home);
|
|
4859
5063
|
}
|
|
4860
5064
|
function probeGithubReachability(vaultPath, exec = defaultExec) {
|
|
4861
|
-
if (!existsSync10(
|
|
5065
|
+
if (!existsSync10(join22(vaultPath, ".git"))) return "unknown";
|
|
4862
5066
|
try {
|
|
4863
5067
|
exec("git", ["remote", "get-url", "origin"], vaultPath);
|
|
4864
5068
|
} catch {
|
|
@@ -4924,10 +5128,10 @@ function probeRemoteHealth(input) {
|
|
|
4924
5128
|
|
|
4925
5129
|
// src/utils/satellite-run-health.ts
|
|
4926
5130
|
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
4927
|
-
import { join as
|
|
5131
|
+
import { join as join23 } from "path";
|
|
4928
5132
|
var SATELLITE_STALE_MS = 26 * 60 * 60 * 1e3;
|
|
4929
5133
|
function satelliteLatestRunPath(vault) {
|
|
4930
|
-
return
|
|
5134
|
+
return join23(vault, ".skillwiki", "agent-memory-trends", "latest-run.json");
|
|
4931
5135
|
}
|
|
4932
5136
|
function isFailedRunStatus(status) {
|
|
4933
5137
|
return status === "fail" || status === "failure";
|
|
@@ -4980,8 +5184,8 @@ function evaluateSatelliteRunHealth(vault, now) {
|
|
|
4980
5184
|
// src/utils/s3-mount-health.ts
|
|
4981
5185
|
import { execSync } from "child_process";
|
|
4982
5186
|
import { platform } from "os";
|
|
4983
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, readFileSync as
|
|
4984
|
-
import { join as
|
|
5187
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5, readFileSync as readFile16 } from "fs";
|
|
5188
|
+
import { join as join24 } from "path";
|
|
4985
5189
|
var OS = platform();
|
|
4986
5190
|
function findRcloneMountPid() {
|
|
4987
5191
|
try {
|
|
@@ -5139,7 +5343,7 @@ function detectFuseMount(vaultPath) {
|
|
|
5139
5343
|
return null;
|
|
5140
5344
|
}
|
|
5141
5345
|
function writeTest(dir) {
|
|
5142
|
-
const testFile =
|
|
5346
|
+
const testFile = join24(dir, `.doctor-write-test-${process.pid}.tmp`);
|
|
5143
5347
|
const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
|
|
5144
5348
|
const start = Date.now();
|
|
5145
5349
|
try {
|
|
@@ -5150,7 +5354,7 @@ function writeTest(dir) {
|
|
|
5150
5354
|
const writeMs = Date.now() - start;
|
|
5151
5355
|
const readStart = Date.now();
|
|
5152
5356
|
try {
|
|
5153
|
-
const back =
|
|
5357
|
+
const back = readFile16(testFile, "utf8");
|
|
5154
5358
|
const readMs = Date.now() - readStart;
|
|
5155
5359
|
if (back !== payload) {
|
|
5156
5360
|
try {
|
|
@@ -5239,12 +5443,12 @@ function detectCliChannels(argv, home) {
|
|
|
5239
5443
|
}
|
|
5240
5444
|
const plugin = findPlugin(home);
|
|
5241
5445
|
if (plugin) {
|
|
5242
|
-
const pluginBin =
|
|
5446
|
+
const pluginBin = join25(plugin.installPath, "bin", "skillwiki");
|
|
5243
5447
|
if (existsSync13(pluginBin)) {
|
|
5244
5448
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
5245
5449
|
}
|
|
5246
5450
|
}
|
|
5247
|
-
const installBin =
|
|
5451
|
+
const installBin = join25(home, ".claude", "skills", "bin", "skillwiki");
|
|
5248
5452
|
if (existsSync13(installBin)) {
|
|
5249
5453
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
5250
5454
|
}
|
|
@@ -5334,9 +5538,9 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5334
5538
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
5335
5539
|
}
|
|
5336
5540
|
const missing = [];
|
|
5337
|
-
if (!existsSync13(
|
|
5541
|
+
if (!existsSync13(join25(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
5338
5542
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
5339
|
-
if (!existsSync13(
|
|
5543
|
+
if (!existsSync13(join25(resolvedPath, dir))) missing.push(dir + "/");
|
|
5340
5544
|
}
|
|
5341
5545
|
if (missing.length === 0) {
|
|
5342
5546
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -5344,7 +5548,7 @@ function checkVaultStructure(resolvedPath) {
|
|
|
5344
5548
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
5345
5549
|
}
|
|
5346
5550
|
function checkSkillsInstalled(home, cwd) {
|
|
5347
|
-
const srcDir = cwd ?
|
|
5551
|
+
const srcDir = cwd ? join25(cwd, "packages", "skills") : void 0;
|
|
5348
5552
|
if (srcDir && existsSync13(srcDir)) {
|
|
5349
5553
|
const found = findInstalledSkillMd(srcDir);
|
|
5350
5554
|
if (found.length > 0) {
|
|
@@ -5358,7 +5562,7 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
5358
5562
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
5359
5563
|
}
|
|
5360
5564
|
}
|
|
5361
|
-
const skillsDir =
|
|
5565
|
+
const skillsDir = join25(home, ".claude", "skills");
|
|
5362
5566
|
if (existsSync13(skillsDir)) {
|
|
5363
5567
|
const found = findInstalledSkillMd(skillsDir);
|
|
5364
5568
|
if (found.length > 0) {
|
|
@@ -5369,10 +5573,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
5369
5573
|
}
|
|
5370
5574
|
function checkDuplicateSkills(home) {
|
|
5371
5575
|
const plugin = findPlugin(home);
|
|
5372
|
-
const skillsDir =
|
|
5576
|
+
const skillsDir = join25(home, ".claude", "skills");
|
|
5373
5577
|
const agentSkillDirs = [
|
|
5374
|
-
{ label: "~/.codex/skills/", path:
|
|
5375
|
-
{ label: "~/.agents/skills/", path:
|
|
5578
|
+
{ label: "~/.codex/skills/", path: join25(home, ".codex", "skills") },
|
|
5579
|
+
{ label: "~/.agents/skills/", path: join25(home, ".agents", "skills") }
|
|
5376
5580
|
];
|
|
5377
5581
|
if (!plugin) {
|
|
5378
5582
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -5475,7 +5679,7 @@ async function checkProfiles(home) {
|
|
|
5475
5679
|
}
|
|
5476
5680
|
async function checkProjectLocalOverride(cwd) {
|
|
5477
5681
|
const dir = cwd ?? process.cwd();
|
|
5478
|
-
const envPath =
|
|
5682
|
+
const envPath = join25(dir, ".skillwiki", ".env");
|
|
5479
5683
|
if (existsSync13(envPath)) {
|
|
5480
5684
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
5481
5685
|
}
|
|
@@ -5485,7 +5689,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
5485
5689
|
if (resolvedPath === void 0) {
|
|
5486
5690
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5487
5691
|
}
|
|
5488
|
-
if (!existsSync13(
|
|
5692
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5489
5693
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
5490
5694
|
}
|
|
5491
5695
|
try {
|
|
@@ -5508,9 +5712,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
5508
5712
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5509
5713
|
}
|
|
5510
5714
|
const missing = [];
|
|
5511
|
-
if (!existsSync13(
|
|
5512
|
-
if (!existsSync13(
|
|
5513
|
-
if (!existsSync13(
|
|
5715
|
+
if (!existsSync13(join25(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
5716
|
+
if (!existsSync13(join25(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
5717
|
+
if (!existsSync13(join25(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
5514
5718
|
if (missing.length === 0) {
|
|
5515
5719
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
5516
5720
|
}
|
|
@@ -5520,7 +5724,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5520
5724
|
if (resolvedPath === void 0) {
|
|
5521
5725
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5522
5726
|
}
|
|
5523
|
-
const rawDir =
|
|
5727
|
+
const rawDir = join25(resolvedPath, "raw");
|
|
5524
5728
|
if (!existsSync13(rawDir)) {
|
|
5525
5729
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
5526
5730
|
}
|
|
@@ -5536,7 +5740,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
5536
5740
|
if (entry.name === ".DS_Store") {
|
|
5537
5741
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
5538
5742
|
} else if (entry.isDirectory()) {
|
|
5539
|
-
walk(
|
|
5743
|
+
walk(join25(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
5540
5744
|
}
|
|
5541
5745
|
}
|
|
5542
5746
|
})(rawDir, "");
|
|
@@ -5567,7 +5771,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
5567
5771
|
if (resolvedPath === void 0) {
|
|
5568
5772
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5569
5773
|
}
|
|
5570
|
-
if (!existsSync13(
|
|
5774
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5571
5775
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
5572
5776
|
}
|
|
5573
5777
|
let timestamp;
|
|
@@ -5616,7 +5820,7 @@ function checkVaultGitDirty(resolvedPath) {
|
|
|
5616
5820
|
if (resolvedPath === void 0) {
|
|
5617
5821
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
|
|
5618
5822
|
}
|
|
5619
|
-
if (!existsSync13(
|
|
5823
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5620
5824
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
|
|
5621
5825
|
}
|
|
5622
5826
|
try {
|
|
@@ -5685,7 +5889,7 @@ function remoteMainHash(resolvedPath) {
|
|
|
5685
5889
|
}
|
|
5686
5890
|
function checkStaleRemoteMain(resolvedPath) {
|
|
5687
5891
|
if (resolvedPath === void 0) return void 0;
|
|
5688
|
-
if (!existsSync13(
|
|
5892
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) return void 0;
|
|
5689
5893
|
const localOrigin = gitRefHash(resolvedPath, "origin/main");
|
|
5690
5894
|
if (!localOrigin) return void 0;
|
|
5691
5895
|
const remoteMain = remoteMainHash(resolvedPath);
|
|
@@ -5701,7 +5905,7 @@ function checkVaultLocalGit(resolvedPath) {
|
|
|
5701
5905
|
if (resolvedPath === void 0) {
|
|
5702
5906
|
return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5703
5907
|
}
|
|
5704
|
-
if (!existsSync13(
|
|
5908
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5705
5909
|
return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
|
|
5706
5910
|
}
|
|
5707
5911
|
try {
|
|
@@ -5720,7 +5924,7 @@ function checkVaultGithubRemote(resolvedPath, exec) {
|
|
|
5720
5924
|
if (resolvedPath === void 0) {
|
|
5721
5925
|
return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
|
|
5722
5926
|
}
|
|
5723
|
-
if (!existsSync13(
|
|
5927
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5724
5928
|
return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
|
|
5725
5929
|
}
|
|
5726
5930
|
const state = probeGithubReachability(resolvedPath, exec);
|
|
@@ -5764,7 +5968,7 @@ function checkVaultPromotionLag(resolvedPath) {
|
|
|
5764
5968
|
if (resolvedPath === void 0) {
|
|
5765
5969
|
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
|
|
5766
5970
|
}
|
|
5767
|
-
if (!existsSync13(
|
|
5971
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5768
5972
|
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
|
|
5769
5973
|
}
|
|
5770
5974
|
try {
|
|
@@ -5791,7 +5995,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
5791
5995
|
if (resolvedPath === void 0) {
|
|
5792
5996
|
return check("pass", id, label, "No vault path \u2014 check skipped");
|
|
5793
5997
|
}
|
|
5794
|
-
if (!existsSync13(
|
|
5998
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5795
5999
|
return check("pass", id, label, "No git repo \u2014 check skipped");
|
|
5796
6000
|
}
|
|
5797
6001
|
if (!hasOriginMain(resolvedPath)) {
|
|
@@ -5908,11 +6112,11 @@ async function checkFleetIdentity(input) {
|
|
|
5908
6112
|
}
|
|
5909
6113
|
function pullLogPaths(home) {
|
|
5910
6114
|
const paths = platform2() === "darwin" ? [
|
|
5911
|
-
|
|
5912
|
-
|
|
6115
|
+
join25(home, "Library", "Logs", "wiki-pull.log"),
|
|
6116
|
+
join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
|
|
5913
6117
|
] : [
|
|
5914
|
-
|
|
5915
|
-
|
|
6118
|
+
join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
|
|
6119
|
+
join25(home, "Library", "Logs", "wiki-pull.log")
|
|
5916
6120
|
];
|
|
5917
6121
|
return [...new Set(paths)];
|
|
5918
6122
|
}
|
|
@@ -5952,7 +6156,7 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
5952
6156
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
5953
6157
|
}
|
|
5954
6158
|
const mountPoint = fuse.mountPoint;
|
|
5955
|
-
const conceptsDir =
|
|
6159
|
+
const conceptsDir = join25(resolvedPath, "concepts");
|
|
5956
6160
|
if (!existsSync13(conceptsDir)) {
|
|
5957
6161
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
5958
6162
|
}
|
|
@@ -6135,7 +6339,7 @@ function checkWriteTest(resolvedPath) {
|
|
|
6135
6339
|
if (!fuse) {
|
|
6136
6340
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
6137
6341
|
}
|
|
6138
|
-
const conceptsDir =
|
|
6342
|
+
const conceptsDir = join25(resolvedPath, "concepts");
|
|
6139
6343
|
if (!existsSync13(conceptsDir)) {
|
|
6140
6344
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
6141
6345
|
}
|
|
@@ -6237,7 +6441,7 @@ function checkVaultSyncPullHelper(home, env) {
|
|
|
6237
6441
|
);
|
|
6238
6442
|
}
|
|
6239
6443
|
function checkVaultSyncReviewRequiredJournals(vaultPath) {
|
|
6240
|
-
if (!vaultPath || !existsSync13(
|
|
6444
|
+
if (!vaultPath || !existsSync13(join25(vaultPath, ".git"))) {
|
|
6241
6445
|
return check("pass", "vault_sync_review_required_journals", "Review-required journals", "No git vault \u2014 check skipped");
|
|
6242
6446
|
}
|
|
6243
6447
|
try {
|
|
@@ -6258,7 +6462,7 @@ function checkVaultSyncReviewRequiredJournals(vaultPath) {
|
|
|
6258
6462
|
}
|
|
6259
6463
|
function readVaultSyncConfig(home) {
|
|
6260
6464
|
try {
|
|
6261
|
-
const content = readFileSync10(
|
|
6465
|
+
const content = readFileSync10(join25(home, ".skillwiki", ".env"), "utf8");
|
|
6262
6466
|
let installed = false;
|
|
6263
6467
|
let role;
|
|
6264
6468
|
let serviceScope;
|
|
@@ -6326,14 +6530,14 @@ function vaultSyncChecks(input) {
|
|
|
6326
6530
|
];
|
|
6327
6531
|
}
|
|
6328
6532
|
const isMac = os === "darwin";
|
|
6329
|
-
const logDir = input.logDir ?? (isMac ?
|
|
6330
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
6331
|
-
const filterPath = input.filterPath ??
|
|
6332
|
-
const packagedSnapshotPath =
|
|
6533
|
+
const logDir = input.logDir ?? (isMac ? join25(home, "Library", "Logs") : join25(home, ".local", "state", "vault-sync", "log"));
|
|
6534
|
+
const shareDir = input.shareDir ?? (isMac ? join25(home, "Library", "Application Support", "vault-sync", "bin") : join25(home, ".local", "share", "vault-sync", "bin"));
|
|
6535
|
+
const filterPath = input.filterPath ?? join25(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
6536
|
+
const packagedSnapshotPath = join25(shareDir, "wiki-snapshot.sh");
|
|
6333
6537
|
const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
6334
6538
|
const snapshotPath = input.snapshotScriptPath ?? (existsSync13(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
|
|
6335
6539
|
function snapshotLastStatusCheck() {
|
|
6336
|
-
const snapshotLog =
|
|
6540
|
+
const snapshotLog = join25(logDir, "wiki-snapshot.log");
|
|
6337
6541
|
try {
|
|
6338
6542
|
const logContent = readFileSync10(snapshotLog, "utf8");
|
|
6339
6543
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
@@ -6382,7 +6586,7 @@ function vaultSyncChecks(input) {
|
|
|
6382
6586
|
if (input.vaultSyncRole === "snapshotter") {
|
|
6383
6587
|
const c12 = existsSync13(snapshotPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found snapshot script: ${snapshotPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Snapshot script not found at ${snapshotPath}`);
|
|
6384
6588
|
const serviceScope = input.vaultSyncServiceScope ?? "user";
|
|
6385
|
-
const userTimerPath =
|
|
6589
|
+
const userTimerPath = join25(home, ".config", "systemd", "user", "wiki-snapshot.timer");
|
|
6386
6590
|
const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
|
|
6387
6591
|
let c22;
|
|
6388
6592
|
if (serviceScope === "user" && existsSync13(userTimerPath)) {
|
|
@@ -6454,7 +6658,7 @@ function vaultSyncChecks(input) {
|
|
|
6454
6658
|
}
|
|
6455
6659
|
return [c12, c22, c32, cFetch2, c42, c52];
|
|
6456
6660
|
}
|
|
6457
|
-
const pushScriptPath =
|
|
6661
|
+
const pushScriptPath = join25(shareDir, "wiki-push.sh");
|
|
6458
6662
|
const c1 = existsSync13(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
|
|
6459
6663
|
let c2;
|
|
6460
6664
|
try {
|
|
@@ -6506,7 +6710,7 @@ function vaultSyncChecks(input) {
|
|
|
6506
6710
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
6507
6711
|
);
|
|
6508
6712
|
}
|
|
6509
|
-
const logFile =
|
|
6713
|
+
const logFile = join25(logDir, "wiki-push.log");
|
|
6510
6714
|
let c3;
|
|
6511
6715
|
try {
|
|
6512
6716
|
const logContent = readFileSync10(logFile, "utf8");
|
|
@@ -6577,7 +6781,7 @@ function vaultSyncChecks(input) {
|
|
|
6577
6781
|
`Log directory not found at ${logDir}`
|
|
6578
6782
|
);
|
|
6579
6783
|
}
|
|
6580
|
-
const fetchLogFile =
|
|
6784
|
+
const fetchLogFile = join25(logDir, "wiki-fetch.log");
|
|
6581
6785
|
let cFetch;
|
|
6582
6786
|
try {
|
|
6583
6787
|
const logContent = readFileSync10(fetchLogFile, "utf8");
|
|
@@ -6721,15 +6925,15 @@ function findSkillMd(dir) {
|
|
|
6721
6925
|
}
|
|
6722
6926
|
for (const entry of entries) {
|
|
6723
6927
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
6724
|
-
results.push(
|
|
6928
|
+
results.push(join25(dir, entry.name));
|
|
6725
6929
|
} else if (entry.isDirectory()) {
|
|
6726
|
-
results.push(...findSkillMd(
|
|
6930
|
+
results.push(...findSkillMd(join25(dir, entry.name)));
|
|
6727
6931
|
}
|
|
6728
6932
|
}
|
|
6729
6933
|
return results;
|
|
6730
6934
|
}
|
|
6731
6935
|
function findInstalledSkillMd(dir) {
|
|
6732
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
6936
|
+
const directSkills = findSkillNames(dir).map((name) => join25(dir, name, "SKILL.md"));
|
|
6733
6937
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
6734
6938
|
}
|
|
6735
6939
|
function findSkillNames(dir) {
|
|
@@ -6741,7 +6945,7 @@ function findSkillNames(dir) {
|
|
|
6741
6945
|
return results;
|
|
6742
6946
|
}
|
|
6743
6947
|
for (const entry of entries) {
|
|
6744
|
-
if (entry.isDirectory() && existsSync13(
|
|
6948
|
+
if (entry.isDirectory() && existsSync13(join25(dir, entry.name, "SKILL.md"))) {
|
|
6745
6949
|
results.push(entry.name);
|
|
6746
6950
|
}
|
|
6747
6951
|
}
|
|
@@ -6789,7 +6993,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
6789
6993
|
}
|
|
6790
6994
|
let logLines = 0;
|
|
6791
6995
|
try {
|
|
6792
|
-
logLines = readFileSync10(
|
|
6996
|
+
logLines = readFileSync10(join25(scanRoot, "log.md"), "utf8").split("\n").length;
|
|
6793
6997
|
} catch {
|
|
6794
6998
|
}
|
|
6795
6999
|
return [
|
|
@@ -6921,9 +7125,9 @@ function readCliPackageJson(baseUrl = import.meta.url) {
|
|
|
6921
7125
|
}
|
|
6922
7126
|
|
|
6923
7127
|
// src/commands/observe.ts
|
|
6924
|
-
import { mkdir as
|
|
7128
|
+
import { mkdir as mkdir6, writeFile as writeFile4 } from "fs/promises";
|
|
6925
7129
|
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
6926
|
-
import { join as
|
|
7130
|
+
import { join as join26 } from "path";
|
|
6927
7131
|
import { createHash as createHash6 } from "crypto";
|
|
6928
7132
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6929
7133
|
function slugify(text) {
|
|
@@ -6952,9 +7156,9 @@ async function runObserve(input) {
|
|
|
6952
7156
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6953
7157
|
};
|
|
6954
7158
|
}
|
|
6955
|
-
const transcriptsDir =
|
|
7159
|
+
const transcriptsDir = join26(input.vault, "raw", "transcripts");
|
|
6956
7160
|
try {
|
|
6957
|
-
await
|
|
7161
|
+
await mkdir6(transcriptsDir, { recursive: true });
|
|
6958
7162
|
} catch {
|
|
6959
7163
|
return {
|
|
6960
7164
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
@@ -6964,7 +7168,7 @@ async function runObserve(input) {
|
|
|
6964
7168
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6965
7169
|
const slug = slugify(input.text);
|
|
6966
7170
|
const fileName = `${today}-observation-${slug}.md`;
|
|
6967
|
-
const filePath =
|
|
7171
|
+
const filePath = join26(transcriptsDir, fileName);
|
|
6968
7172
|
const body = `
|
|
6969
7173
|
${input.text.trim()}
|
|
6970
7174
|
`;
|
|
@@ -7005,8 +7209,8 @@ ${input.text.trim()}
|
|
|
7005
7209
|
|
|
7006
7210
|
// src/commands/memory.ts
|
|
7007
7211
|
import { createHash as createHash7 } from "crypto";
|
|
7008
|
-
import { mkdir as
|
|
7009
|
-
import { basename as basename2, extname, join as
|
|
7212
|
+
import { mkdir as mkdir7, readFile as readFile17, readdir as readdir5, stat as stat5, writeFile as writeFile5 } from "fs/promises";
|
|
7213
|
+
import { basename as basename2, extname, join as join27, relative as relative3, sep as sep3 } from "path";
|
|
7010
7214
|
|
|
7011
7215
|
// src/utils/memory-authority.ts
|
|
7012
7216
|
var TIER_RANK = {
|
|
@@ -7125,8 +7329,8 @@ async function runMemoryIndex(input) {
|
|
|
7125
7329
|
}
|
|
7126
7330
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
7127
7331
|
const relCachePath = memoryCacheRelPath(input.project);
|
|
7128
|
-
const absCachePath =
|
|
7129
|
-
await
|
|
7332
|
+
const absCachePath = join27(input.vault, relCachePath);
|
|
7333
|
+
await mkdir7(join27(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
|
|
7130
7334
|
await writeFile5(absCachePath, `${JSON.stringify({
|
|
7131
7335
|
generated_at: generatedAt,
|
|
7132
7336
|
project: input.project,
|
|
@@ -7318,7 +7522,7 @@ async function buildMemoryIndexState(pages, project) {
|
|
|
7318
7522
|
}
|
|
7319
7523
|
async function checkMemoryIndex(vault, project, current) {
|
|
7320
7524
|
const relCachePath = memoryCacheRelPath(project);
|
|
7321
|
-
const cacheText = await readIfExists2(
|
|
7525
|
+
const cacheText = await readIfExists2(join27(vault, relCachePath));
|
|
7322
7526
|
if (!cacheText) {
|
|
7323
7527
|
return {
|
|
7324
7528
|
ok: true,
|
|
@@ -7711,7 +7915,7 @@ function renderMemoryIndexStatusHint(status) {
|
|
|
7711
7915
|
}
|
|
7712
7916
|
async function readIfExists2(path) {
|
|
7713
7917
|
try {
|
|
7714
|
-
return await
|
|
7918
|
+
return await readFile17(path, "utf8");
|
|
7715
7919
|
} catch {
|
|
7716
7920
|
return "";
|
|
7717
7921
|
}
|
|
@@ -7724,10 +7928,10 @@ async function collectImportFiles(source) {
|
|
|
7724
7928
|
return files.sort((a, b) => a.localeCompare(b));
|
|
7725
7929
|
}
|
|
7726
7930
|
async function walkImportFiles(dir, out) {
|
|
7727
|
-
const entries = await
|
|
7931
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
7728
7932
|
for (const entry of entries) {
|
|
7729
7933
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
7730
|
-
const path =
|
|
7934
|
+
const path = join27(dir, entry.name);
|
|
7731
7935
|
if (entry.isDirectory()) {
|
|
7732
7936
|
await walkImportFiles(path, out);
|
|
7733
7937
|
} else if (entry.isFile() && isImportCandidate(path)) {
|
|
@@ -7742,7 +7946,7 @@ function isImportCandidate(path) {
|
|
|
7742
7946
|
async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
|
|
7743
7947
|
const st = await stat5(file);
|
|
7744
7948
|
const sourceKind = classifyImportSource(file);
|
|
7745
|
-
const hash = createHash7("sha256").update(await
|
|
7949
|
+
const hash = createHash7("sha256").update(await readFile17(file)).digest("hex");
|
|
7746
7950
|
const baseEntry = {
|
|
7747
7951
|
source_path: file,
|
|
7748
7952
|
source_kind: sourceKind,
|
|
@@ -7765,7 +7969,7 @@ async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
|
|
|
7765
7969
|
reason: "policy_source_not_imported"
|
|
7766
7970
|
};
|
|
7767
7971
|
}
|
|
7768
|
-
const text = await
|
|
7972
|
+
const text = await readFile17(file, "utf8");
|
|
7769
7973
|
const extracted = extractImportText(text, sourceKind);
|
|
7770
7974
|
if (!extracted) {
|
|
7771
7975
|
return {
|
|
@@ -7794,8 +7998,8 @@ async function writeImportCapture(vault, entry, today) {
|
|
|
7794
7998
|
const content = hiddenString(entry, "__content");
|
|
7795
7999
|
const project = hiddenString(entry, "__project");
|
|
7796
8000
|
const relPath = await availableImportPath(vault, entry.proposed_path);
|
|
7797
|
-
const absPath =
|
|
7798
|
-
await
|
|
8001
|
+
const absPath = join27(vault, relPath);
|
|
8002
|
+
await mkdir7(join27(vault, "raw", "transcripts"), { recursive: true });
|
|
7799
8003
|
await writeFile5(absPath, renderImportCapture(entry, content, project, today), "utf8");
|
|
7800
8004
|
const validation = await runValidate({ file: absPath });
|
|
7801
8005
|
return {
|
|
@@ -7811,7 +8015,7 @@ async function availableImportPath(vault, proposed) {
|
|
|
7811
8015
|
const stem = proposed.slice(0, -ext.length);
|
|
7812
8016
|
let candidate = proposed;
|
|
7813
8017
|
let i = 2;
|
|
7814
|
-
while (await readIfExists2(
|
|
8018
|
+
while (await readIfExists2(join27(vault, candidate))) {
|
|
7815
8019
|
candidate = `${stem}-${i}${ext}`;
|
|
7816
8020
|
i++;
|
|
7817
8021
|
}
|
|
@@ -7996,10 +8200,10 @@ function memoryCacheRelPath(project) {
|
|
|
7996
8200
|
}
|
|
7997
8201
|
async function readMemoryCache(vault, project) {
|
|
7998
8202
|
if (project) {
|
|
7999
|
-
const projectCache = await readIfExists2(
|
|
8203
|
+
const projectCache = await readIfExists2(join27(vault, memoryCacheRelPath(project)));
|
|
8000
8204
|
if (projectCache) return projectCache;
|
|
8001
8205
|
}
|
|
8002
|
-
return readIfExists2(
|
|
8206
|
+
return readIfExists2(join27(vault, ".skillwiki", "memory-topics.json"));
|
|
8003
8207
|
}
|
|
8004
8208
|
function dedupePages(pages) {
|
|
8005
8209
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8090,8 +8294,8 @@ function slugify2(value) {
|
|
|
8090
8294
|
}
|
|
8091
8295
|
|
|
8092
8296
|
// src/commands/query.ts
|
|
8093
|
-
import { readFile as
|
|
8094
|
-
import { join as
|
|
8297
|
+
import { readFile as readFile18, stat as stat6 } from "fs/promises";
|
|
8298
|
+
import { join as join28 } from "path";
|
|
8095
8299
|
var W_KEYWORD = 2;
|
|
8096
8300
|
var W_SOURCE_OVERLAP = 4;
|
|
8097
8301
|
var W_WIKILINK = 3;
|
|
@@ -8212,7 +8416,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8212
8416
|
return score;
|
|
8213
8417
|
}
|
|
8214
8418
|
async function loadOrBuildGraph(vault) {
|
|
8215
|
-
const graphPath =
|
|
8419
|
+
const graphPath = join28(vault, ".skillwiki", "graph.json");
|
|
8216
8420
|
let needsBuild = false;
|
|
8217
8421
|
try {
|
|
8218
8422
|
const fileStat = await stat6(graphPath);
|
|
@@ -8226,7 +8430,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
8226
8430
|
if (buildResult.exitCode !== 0) return null;
|
|
8227
8431
|
}
|
|
8228
8432
|
try {
|
|
8229
|
-
const raw = await
|
|
8433
|
+
const raw = await readFile18(graphPath, "utf8");
|
|
8230
8434
|
return JSON.parse(raw);
|
|
8231
8435
|
} catch {
|
|
8232
8436
|
return null;
|
|
@@ -8241,7 +8445,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
8241
8445
|
import { z } from "zod";
|
|
8242
8446
|
|
|
8243
8447
|
// src/mcp/vault-resolve.ts
|
|
8244
|
-
import { join as
|
|
8448
|
+
import { join as join29, resolve as resolve7 } from "path";
|
|
8245
8449
|
|
|
8246
8450
|
// src/mcp/allowlist.ts
|
|
8247
8451
|
import { resolve as resolve6, sep as sep4 } from "path";
|
|
@@ -8303,7 +8507,7 @@ async function resolveMcpVault(input) {
|
|
|
8303
8507
|
return ok({ vault: vaultPath, source });
|
|
8304
8508
|
}
|
|
8305
8509
|
function defaultGraphOut(vault) {
|
|
8306
|
-
return
|
|
8510
|
+
return join29(vault, ".skillwiki", "graph.json");
|
|
8307
8511
|
}
|
|
8308
8512
|
|
|
8309
8513
|
// src/mcp/result-format.ts
|
|
@@ -8320,7 +8524,7 @@ function formatToolResult(payload) {
|
|
|
8320
8524
|
// src/mcp/audit-log.ts
|
|
8321
8525
|
import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
|
|
8322
8526
|
import { homedir } from "os";
|
|
8323
|
-
import { join as
|
|
8527
|
+
import { join as join30 } from "path";
|
|
8324
8528
|
function auditEnabled() {
|
|
8325
8529
|
const v = process.env.SKILLWIKI_MCP_AUDIT;
|
|
8326
8530
|
if (v === "0" || v === "false") return false;
|
|
@@ -8332,7 +8536,7 @@ function auditSink() {
|
|
|
8332
8536
|
function auditFilePath() {
|
|
8333
8537
|
const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
|
|
8334
8538
|
if (custom && custom.length > 0) return custom;
|
|
8335
|
-
return
|
|
8539
|
+
return join30(homedir(), ".skillwiki", "mcp-audit.jsonl");
|
|
8336
8540
|
}
|
|
8337
8541
|
function auditMcpToolCall(entry) {
|
|
8338
8542
|
if (!auditEnabled()) return;
|
|
@@ -8342,7 +8546,7 @@ function auditMcpToolCall(entry) {
|
|
|
8342
8546
|
return;
|
|
8343
8547
|
}
|
|
8344
8548
|
const path = auditFilePath();
|
|
8345
|
-
mkdirSync5(
|
|
8549
|
+
mkdirSync5(join30(path, ".."), { recursive: true });
|
|
8346
8550
|
appendFileSync(path, line, "utf8");
|
|
8347
8551
|
}
|
|
8348
8552
|
async function runMcpToolHandler(tool, input, fn) {
|
|
@@ -8562,8 +8766,8 @@ function registerMcpMutatingTools(server) {
|
|
|
8562
8766
|
}
|
|
8563
8767
|
|
|
8564
8768
|
// src/mcp/resources.ts
|
|
8565
|
-
import { readFile as
|
|
8566
|
-
import { join as
|
|
8769
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
8770
|
+
import { join as join32 } from "path";
|
|
8567
8771
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8568
8772
|
|
|
8569
8773
|
// src/mcp/lint-bucket.ts
|
|
@@ -8691,8 +8895,8 @@ async function fetchQueryPreview(input) {
|
|
|
8691
8895
|
}
|
|
8692
8896
|
|
|
8693
8897
|
// src/mcp/graph-html.ts
|
|
8694
|
-
import { readFile as
|
|
8695
|
-
import { join as
|
|
8898
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
8899
|
+
import { join as join31 } from "path";
|
|
8696
8900
|
import { existsSync as existsSync15 } from "fs";
|
|
8697
8901
|
var TYPE_COLORS = {
|
|
8698
8902
|
entities: "#e74c3c",
|
|
@@ -8761,7 +8965,7 @@ ${nodeSvg}
|
|
|
8761
8965
|
return { html, node_count: nodes.length, edge_count: edges.length, truncated };
|
|
8762
8966
|
}
|
|
8763
8967
|
async function fetchGraphHtmlReport(input) {
|
|
8764
|
-
const graphPath = input.graphPath ??
|
|
8968
|
+
const graphPath = input.graphPath ?? join31(input.vault, ".skillwiki", "graph.json");
|
|
8765
8969
|
const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
|
|
8766
8970
|
if (!existsSync15(graphPath)) {
|
|
8767
8971
|
return {
|
|
@@ -8771,7 +8975,7 @@ async function fetchGraphHtmlReport(input) {
|
|
|
8771
8975
|
}
|
|
8772
8976
|
let raw;
|
|
8773
8977
|
try {
|
|
8774
|
-
raw = await
|
|
8978
|
+
raw = await readFile19(graphPath, "utf8");
|
|
8775
8979
|
} catch (e) {
|
|
8776
8980
|
return {
|
|
8777
8981
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -8835,7 +9039,7 @@ async function fetchStaleSummary(input) {
|
|
|
8835
9039
|
|
|
8836
9040
|
// src/mcp/resources.ts
|
|
8837
9041
|
async function readVaultFile(vault, rel) {
|
|
8838
|
-
return
|
|
9042
|
+
return readFile20(join32(vault, rel), "utf8");
|
|
8839
9043
|
}
|
|
8840
9044
|
async function tailLines(text, lines) {
|
|
8841
9045
|
const parts = text.split(/\r?\n/);
|
|
@@ -8921,9 +9125,9 @@ function registerMcpResources(server) {
|
|
|
8921
9125
|
if (!v.ok) {
|
|
8922
9126
|
return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
|
|
8923
9127
|
}
|
|
8924
|
-
const path =
|
|
9128
|
+
const path = join32(v.data.vault, ".skillwiki", "graph.json");
|
|
8925
9129
|
try {
|
|
8926
|
-
const raw = await
|
|
9130
|
+
const raw = await readFile20(path, "utf8");
|
|
8927
9131
|
const graph = JSON.parse(raw);
|
|
8928
9132
|
const adjacency = graph.adjacency ?? {};
|
|
8929
9133
|
const nodes = Object.keys(adjacency);
|
|
@@ -9236,6 +9440,8 @@ export {
|
|
|
9236
9440
|
readLastOp,
|
|
9237
9441
|
appendLastOp,
|
|
9238
9442
|
clearLastOp,
|
|
9443
|
+
writeLogEvent,
|
|
9444
|
+
readLogEvents,
|
|
9239
9445
|
runLogAppend,
|
|
9240
9446
|
renderIndexUpsert,
|
|
9241
9447
|
upsertIndexEntry,
|
|
@@ -9266,6 +9472,7 @@ export {
|
|
|
9266
9472
|
runStale,
|
|
9267
9473
|
runPagesize,
|
|
9268
9474
|
runLogRotate,
|
|
9475
|
+
scanConflictMarkerBlocksInText,
|
|
9269
9476
|
runTopicMapCheck,
|
|
9270
9477
|
runIndexLinkFormat,
|
|
9271
9478
|
normalizeRemoteRoot,
|