skillwiki 0.10.6 → 0.10.8
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-NJFCTMYZ.js → chunk-AVCGIWSL.js} +470 -255
- package/dist/{chunk-R6BKJWVC.js → chunk-TYN2IHBY.js} +2 -0
- package/dist/cli.js +740 -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
|
+
]);
|
|
315
|
+
}
|
|
316
|
+
function preferredMarker(operationId, eventKind) {
|
|
317
|
+
if (!eventKind || eventKind === "page-publish") {
|
|
318
|
+
return `<!-- skillwiki-page-publish:${operationId} -->`;
|
|
319
|
+
}
|
|
320
|
+
return `<!-- skillwiki-log-op:${operationId} -->`;
|
|
169
321
|
}
|
|
170
|
-
async function appendWhileLocked(logPath, content,
|
|
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;
|
|
@@ -5604,7 +5808,8 @@ function hasOriginMain(resolvedPath) {
|
|
|
5604
5808
|
execSync2("git rev-parse --verify --quiet origin/main", {
|
|
5605
5809
|
cwd: resolvedPath,
|
|
5606
5810
|
encoding: "utf8",
|
|
5607
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
5811
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5812
|
+
timeout: 2e3
|
|
5608
5813
|
});
|
|
5609
5814
|
return true;
|
|
5610
5815
|
} catch {
|
|
@@ -5615,14 +5820,15 @@ function checkVaultGitDirty(resolvedPath) {
|
|
|
5615
5820
|
if (resolvedPath === void 0) {
|
|
5616
5821
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
|
|
5617
5822
|
}
|
|
5618
|
-
if (!existsSync13(
|
|
5823
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5619
5824
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
|
|
5620
5825
|
}
|
|
5621
5826
|
try {
|
|
5622
5827
|
const lines = execSync2("git status --porcelain", {
|
|
5623
5828
|
cwd: resolvedPath,
|
|
5624
5829
|
encoding: "utf8",
|
|
5625
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
5830
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5831
|
+
timeout: 5e3
|
|
5626
5832
|
}).trim().split("\n").filter(Boolean);
|
|
5627
5833
|
if (lines.length > 0) {
|
|
5628
5834
|
return check("warn", "vault_git_dirty", "Vault git dirty state", `${lines.length} dirty file(s) in vault worktree`);
|
|
@@ -5683,7 +5889,7 @@ function remoteMainHash(resolvedPath) {
|
|
|
5683
5889
|
}
|
|
5684
5890
|
function checkStaleRemoteMain(resolvedPath) {
|
|
5685
5891
|
if (resolvedPath === void 0) return void 0;
|
|
5686
|
-
if (!existsSync13(
|
|
5892
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) return void 0;
|
|
5687
5893
|
const localOrigin = gitRefHash(resolvedPath, "origin/main");
|
|
5688
5894
|
if (!localOrigin) return void 0;
|
|
5689
5895
|
const remoteMain = remoteMainHash(resolvedPath);
|
|
@@ -5699,7 +5905,7 @@ function checkVaultLocalGit(resolvedPath) {
|
|
|
5699
5905
|
if (resolvedPath === void 0) {
|
|
5700
5906
|
return check("warn", "vault_local_git", "Vault local git", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
5701
5907
|
}
|
|
5702
|
-
if (!existsSync13(
|
|
5908
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5703
5909
|
return check("warn", "vault_local_git", "Vault local git", "Not a git repository - sync features unavailable");
|
|
5704
5910
|
}
|
|
5705
5911
|
try {
|
|
@@ -5718,7 +5924,7 @@ function checkVaultGithubRemote(resolvedPath, exec) {
|
|
|
5718
5924
|
if (resolvedPath === void 0) {
|
|
5719
5925
|
return check("pass", "vault_github_remote", "Vault GitHub remote", "No vault path \u2014 check skipped");
|
|
5720
5926
|
}
|
|
5721
|
-
if (!existsSync13(
|
|
5927
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5722
5928
|
return check("pass", "vault_github_remote", "Vault GitHub remote", "No git repo \u2014 check skipped");
|
|
5723
5929
|
}
|
|
5724
5930
|
const state = probeGithubReachability(resolvedPath, exec);
|
|
@@ -5762,7 +5968,7 @@ function checkVaultPromotionLag(resolvedPath) {
|
|
|
5762
5968
|
if (resolvedPath === void 0) {
|
|
5763
5969
|
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No vault path \u2014 check skipped");
|
|
5764
5970
|
}
|
|
5765
|
-
if (!existsSync13(
|
|
5971
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5766
5972
|
return check("pass", "vault_promotion_lag", "Vault promotion lag", "No git repo \u2014 check skipped");
|
|
5767
5973
|
}
|
|
5768
5974
|
try {
|
|
@@ -5789,7 +5995,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
5789
5995
|
if (resolvedPath === void 0) {
|
|
5790
5996
|
return check("pass", id, label, "No vault path \u2014 check skipped");
|
|
5791
5997
|
}
|
|
5792
|
-
if (!existsSync13(
|
|
5998
|
+
if (!existsSync13(join25(resolvedPath, ".git"))) {
|
|
5793
5999
|
return check("pass", id, label, "No git repo \u2014 check skipped");
|
|
5794
6000
|
}
|
|
5795
6001
|
if (!hasOriginMain(resolvedPath)) {
|
|
@@ -5799,7 +6005,8 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
5799
6005
|
const count = parseInt(execSync2(`git rev-list --count ${range}`, {
|
|
5800
6006
|
cwd: resolvedPath,
|
|
5801
6007
|
encoding: "utf8",
|
|
5802
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
6008
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6009
|
+
timeout: 5e3
|
|
5803
6010
|
}).trim(), 10);
|
|
5804
6011
|
if (count > 0) {
|
|
5805
6012
|
return check("warn", id, label, `${count} commit(s) ${nonZeroSuffix}`);
|
|
@@ -5905,11 +6112,11 @@ async function checkFleetIdentity(input) {
|
|
|
5905
6112
|
}
|
|
5906
6113
|
function pullLogPaths(home) {
|
|
5907
6114
|
const paths = platform2() === "darwin" ? [
|
|
5908
|
-
|
|
5909
|
-
|
|
6115
|
+
join25(home, "Library", "Logs", "wiki-pull.log"),
|
|
6116
|
+
join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
|
|
5910
6117
|
] : [
|
|
5911
|
-
|
|
5912
|
-
|
|
6118
|
+
join25(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
|
|
6119
|
+
join25(home, "Library", "Logs", "wiki-pull.log")
|
|
5913
6120
|
];
|
|
5914
6121
|
return [...new Set(paths)];
|
|
5915
6122
|
}
|
|
@@ -5949,7 +6156,7 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
5949
6156
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
5950
6157
|
}
|
|
5951
6158
|
const mountPoint = fuse.mountPoint;
|
|
5952
|
-
const conceptsDir =
|
|
6159
|
+
const conceptsDir = join25(resolvedPath, "concepts");
|
|
5953
6160
|
if (!existsSync13(conceptsDir)) {
|
|
5954
6161
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
5955
6162
|
}
|
|
@@ -6132,7 +6339,7 @@ function checkWriteTest(resolvedPath) {
|
|
|
6132
6339
|
if (!fuse) {
|
|
6133
6340
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
6134
6341
|
}
|
|
6135
|
-
const conceptsDir =
|
|
6342
|
+
const conceptsDir = join25(resolvedPath, "concepts");
|
|
6136
6343
|
if (!existsSync13(conceptsDir)) {
|
|
6137
6344
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
6138
6345
|
}
|
|
@@ -6234,7 +6441,7 @@ function checkVaultSyncPullHelper(home, env) {
|
|
|
6234
6441
|
);
|
|
6235
6442
|
}
|
|
6236
6443
|
function checkVaultSyncReviewRequiredJournals(vaultPath) {
|
|
6237
|
-
if (!vaultPath || !existsSync13(
|
|
6444
|
+
if (!vaultPath || !existsSync13(join25(vaultPath, ".git"))) {
|
|
6238
6445
|
return check("pass", "vault_sync_review_required_journals", "Review-required journals", "No git vault \u2014 check skipped");
|
|
6239
6446
|
}
|
|
6240
6447
|
try {
|
|
@@ -6255,7 +6462,7 @@ function checkVaultSyncReviewRequiredJournals(vaultPath) {
|
|
|
6255
6462
|
}
|
|
6256
6463
|
function readVaultSyncConfig(home) {
|
|
6257
6464
|
try {
|
|
6258
|
-
const content = readFileSync10(
|
|
6465
|
+
const content = readFileSync10(join25(home, ".skillwiki", ".env"), "utf8");
|
|
6259
6466
|
let installed = false;
|
|
6260
6467
|
let role;
|
|
6261
6468
|
let serviceScope;
|
|
@@ -6323,14 +6530,14 @@ function vaultSyncChecks(input) {
|
|
|
6323
6530
|
];
|
|
6324
6531
|
}
|
|
6325
6532
|
const isMac = os === "darwin";
|
|
6326
|
-
const logDir = input.logDir ?? (isMac ?
|
|
6327
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
6328
|
-
const filterPath = input.filterPath ??
|
|
6329
|
-
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");
|
|
6330
6537
|
const legacySnapshotPath = "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
6331
6538
|
const snapshotPath = input.snapshotScriptPath ?? (existsSync13(packagedSnapshotPath) ? packagedSnapshotPath : legacySnapshotPath);
|
|
6332
6539
|
function snapshotLastStatusCheck() {
|
|
6333
|
-
const snapshotLog =
|
|
6540
|
+
const snapshotLog = join25(logDir, "wiki-snapshot.log");
|
|
6334
6541
|
try {
|
|
6335
6542
|
const logContent = readFileSync10(snapshotLog, "utf8");
|
|
6336
6543
|
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
@@ -6379,7 +6586,7 @@ function vaultSyncChecks(input) {
|
|
|
6379
6586
|
if (input.vaultSyncRole === "snapshotter") {
|
|
6380
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}`);
|
|
6381
6588
|
const serviceScope = input.vaultSyncServiceScope ?? "user";
|
|
6382
|
-
const userTimerPath =
|
|
6589
|
+
const userTimerPath = join25(home, ".config", "systemd", "user", "wiki-snapshot.timer");
|
|
6383
6590
|
const systemTimerPath = "/etc/systemd/system/wiki-snapshot.timer";
|
|
6384
6591
|
let c22;
|
|
6385
6592
|
if (serviceScope === "user" && existsSync13(userTimerPath)) {
|
|
@@ -6451,7 +6658,7 @@ function vaultSyncChecks(input) {
|
|
|
6451
6658
|
}
|
|
6452
6659
|
return [c12, c22, c32, cFetch2, c42, c52];
|
|
6453
6660
|
}
|
|
6454
|
-
const pushScriptPath =
|
|
6661
|
+
const pushScriptPath = join25(shareDir, "wiki-push.sh");
|
|
6455
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`);
|
|
6456
6663
|
let c2;
|
|
6457
6664
|
try {
|
|
@@ -6503,7 +6710,7 @@ function vaultSyncChecks(input) {
|
|
|
6503
6710
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
6504
6711
|
);
|
|
6505
6712
|
}
|
|
6506
|
-
const logFile =
|
|
6713
|
+
const logFile = join25(logDir, "wiki-push.log");
|
|
6507
6714
|
let c3;
|
|
6508
6715
|
try {
|
|
6509
6716
|
const logContent = readFileSync10(logFile, "utf8");
|
|
@@ -6574,7 +6781,7 @@ function vaultSyncChecks(input) {
|
|
|
6574
6781
|
`Log directory not found at ${logDir}`
|
|
6575
6782
|
);
|
|
6576
6783
|
}
|
|
6577
|
-
const fetchLogFile =
|
|
6784
|
+
const fetchLogFile = join25(logDir, "wiki-fetch.log");
|
|
6578
6785
|
let cFetch;
|
|
6579
6786
|
try {
|
|
6580
6787
|
const logContent = readFileSync10(fetchLogFile, "utf8");
|
|
@@ -6718,15 +6925,15 @@ function findSkillMd(dir) {
|
|
|
6718
6925
|
}
|
|
6719
6926
|
for (const entry of entries) {
|
|
6720
6927
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
6721
|
-
results.push(
|
|
6928
|
+
results.push(join25(dir, entry.name));
|
|
6722
6929
|
} else if (entry.isDirectory()) {
|
|
6723
|
-
results.push(...findSkillMd(
|
|
6930
|
+
results.push(...findSkillMd(join25(dir, entry.name)));
|
|
6724
6931
|
}
|
|
6725
6932
|
}
|
|
6726
6933
|
return results;
|
|
6727
6934
|
}
|
|
6728
6935
|
function findInstalledSkillMd(dir) {
|
|
6729
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
6936
|
+
const directSkills = findSkillNames(dir).map((name) => join25(dir, name, "SKILL.md"));
|
|
6730
6937
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
6731
6938
|
}
|
|
6732
6939
|
function findSkillNames(dir) {
|
|
@@ -6738,13 +6945,16 @@ function findSkillNames(dir) {
|
|
|
6738
6945
|
return results;
|
|
6739
6946
|
}
|
|
6740
6947
|
for (const entry of entries) {
|
|
6741
|
-
if (entry.isDirectory() && existsSync13(
|
|
6948
|
+
if (entry.isDirectory() && existsSync13(join25(dir, entry.name, "SKILL.md"))) {
|
|
6742
6949
|
results.push(entry.name);
|
|
6743
6950
|
}
|
|
6744
6951
|
}
|
|
6745
6952
|
return results;
|
|
6746
6953
|
}
|
|
6747
6954
|
var METRIC_TYPES = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
6955
|
+
function doctorReadOnlyScanRoot(resolvedPath) {
|
|
6956
|
+
return resolveReadOnlyVaultRoot(resolvedPath).root;
|
|
6957
|
+
}
|
|
6748
6958
|
async function vaultMetrics(resolvedPath) {
|
|
6749
6959
|
const ids = [
|
|
6750
6960
|
["vault_metric_pages", "Vault pages by type"],
|
|
@@ -6755,7 +6965,8 @@ async function vaultMetrics(resolvedPath) {
|
|
|
6755
6965
|
];
|
|
6756
6966
|
const noVault = () => ids.map(([id, label]) => check("info", id, label, "no vault configured"));
|
|
6757
6967
|
if (!resolvedPath) return noVault();
|
|
6758
|
-
const
|
|
6968
|
+
const scanRoot = doctorReadOnlyScanRoot(resolvedPath);
|
|
6969
|
+
const scan = await scanVault(scanRoot);
|
|
6759
6970
|
if (!scan.ok) return noVault();
|
|
6760
6971
|
const tk = scan.data.typedKnowledge;
|
|
6761
6972
|
const perType = METRIC_TYPES.map((d) => `${d} ${tk.filter((p) => p.relPath.startsWith(d + "/")).length}`).join(", ");
|
|
@@ -6782,7 +6993,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
6782
6993
|
}
|
|
6783
6994
|
let logLines = 0;
|
|
6784
6995
|
try {
|
|
6785
|
-
logLines = readFileSync10(
|
|
6996
|
+
logLines = readFileSync10(join25(scanRoot, "log.md"), "utf8").split("\n").length;
|
|
6786
6997
|
} catch {
|
|
6787
6998
|
}
|
|
6788
6999
|
return [
|
|
@@ -6844,8 +7055,9 @@ async function runDoctor(input) {
|
|
|
6844
7055
|
checks.push(checkVaultS3Remote(input.home, input.execProbe, input.env ?? process.env));
|
|
6845
7056
|
checks.push(checkVaultSnapshotterReachable(fleetLoad, input.checkSnapshotter, input.execProbe));
|
|
6846
7057
|
checks.push(checkVaultPromotionLag(gitCheckPath));
|
|
6847
|
-
|
|
6848
|
-
checks.push(
|
|
7058
|
+
const readOnlyScanRoot = resolvedPath ? doctorReadOnlyScanRoot(resolvedPath) : void 0;
|
|
7059
|
+
checks.push(checkDotStoreClean(readOnlyScanRoot));
|
|
7060
|
+
checks.push(checkVaultConflictMarkers(readOnlyScanRoot));
|
|
6849
7061
|
checks.push(checkS3MountPerf(resolvedPath));
|
|
6850
7062
|
checks.push(checkS3MountFreshness(resolvedPath));
|
|
6851
7063
|
checks.push(checkRcloneFlagAudit(resolvedPath));
|
|
@@ -6913,9 +7125,9 @@ function readCliPackageJson(baseUrl = import.meta.url) {
|
|
|
6913
7125
|
}
|
|
6914
7126
|
|
|
6915
7127
|
// src/commands/observe.ts
|
|
6916
|
-
import { mkdir as
|
|
7128
|
+
import { mkdir as mkdir6, writeFile as writeFile4 } from "fs/promises";
|
|
6917
7129
|
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
6918
|
-
import { join as
|
|
7130
|
+
import { join as join26 } from "path";
|
|
6919
7131
|
import { createHash as createHash6 } from "crypto";
|
|
6920
7132
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6921
7133
|
function slugify(text) {
|
|
@@ -6944,9 +7156,9 @@ async function runObserve(input) {
|
|
|
6944
7156
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6945
7157
|
};
|
|
6946
7158
|
}
|
|
6947
|
-
const transcriptsDir =
|
|
7159
|
+
const transcriptsDir = join26(input.vault, "raw", "transcripts");
|
|
6948
7160
|
try {
|
|
6949
|
-
await
|
|
7161
|
+
await mkdir6(transcriptsDir, { recursive: true });
|
|
6950
7162
|
} catch {
|
|
6951
7163
|
return {
|
|
6952
7164
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
@@ -6956,7 +7168,7 @@ async function runObserve(input) {
|
|
|
6956
7168
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6957
7169
|
const slug = slugify(input.text);
|
|
6958
7170
|
const fileName = `${today}-observation-${slug}.md`;
|
|
6959
|
-
const filePath =
|
|
7171
|
+
const filePath = join26(transcriptsDir, fileName);
|
|
6960
7172
|
const body = `
|
|
6961
7173
|
${input.text.trim()}
|
|
6962
7174
|
`;
|
|
@@ -6997,8 +7209,8 @@ ${input.text.trim()}
|
|
|
6997
7209
|
|
|
6998
7210
|
// src/commands/memory.ts
|
|
6999
7211
|
import { createHash as createHash7 } from "crypto";
|
|
7000
|
-
import { mkdir as
|
|
7001
|
-
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";
|
|
7002
7214
|
|
|
7003
7215
|
// src/utils/memory-authority.ts
|
|
7004
7216
|
var TIER_RANK = {
|
|
@@ -7117,8 +7329,8 @@ async function runMemoryIndex(input) {
|
|
|
7117
7329
|
}
|
|
7118
7330
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
7119
7331
|
const relCachePath = memoryCacheRelPath(input.project);
|
|
7120
|
-
const absCachePath =
|
|
7121
|
-
await
|
|
7332
|
+
const absCachePath = join27(input.vault, relCachePath);
|
|
7333
|
+
await mkdir7(join27(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
|
|
7122
7334
|
await writeFile5(absCachePath, `${JSON.stringify({
|
|
7123
7335
|
generated_at: generatedAt,
|
|
7124
7336
|
project: input.project,
|
|
@@ -7310,7 +7522,7 @@ async function buildMemoryIndexState(pages, project) {
|
|
|
7310
7522
|
}
|
|
7311
7523
|
async function checkMemoryIndex(vault, project, current) {
|
|
7312
7524
|
const relCachePath = memoryCacheRelPath(project);
|
|
7313
|
-
const cacheText = await readIfExists2(
|
|
7525
|
+
const cacheText = await readIfExists2(join27(vault, relCachePath));
|
|
7314
7526
|
if (!cacheText) {
|
|
7315
7527
|
return {
|
|
7316
7528
|
ok: true,
|
|
@@ -7703,7 +7915,7 @@ function renderMemoryIndexStatusHint(status) {
|
|
|
7703
7915
|
}
|
|
7704
7916
|
async function readIfExists2(path) {
|
|
7705
7917
|
try {
|
|
7706
|
-
return await
|
|
7918
|
+
return await readFile17(path, "utf8");
|
|
7707
7919
|
} catch {
|
|
7708
7920
|
return "";
|
|
7709
7921
|
}
|
|
@@ -7716,10 +7928,10 @@ async function collectImportFiles(source) {
|
|
|
7716
7928
|
return files.sort((a, b) => a.localeCompare(b));
|
|
7717
7929
|
}
|
|
7718
7930
|
async function walkImportFiles(dir, out) {
|
|
7719
|
-
const entries = await
|
|
7931
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
7720
7932
|
for (const entry of entries) {
|
|
7721
7933
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
7722
|
-
const path =
|
|
7934
|
+
const path = join27(dir, entry.name);
|
|
7723
7935
|
if (entry.isDirectory()) {
|
|
7724
7936
|
await walkImportFiles(path, out);
|
|
7725
7937
|
} else if (entry.isFile() && isImportCandidate(path)) {
|
|
@@ -7734,7 +7946,7 @@ function isImportCandidate(path) {
|
|
|
7734
7946
|
async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
|
|
7735
7947
|
const st = await stat5(file);
|
|
7736
7948
|
const sourceKind = classifyImportSource(file);
|
|
7737
|
-
const hash = createHash7("sha256").update(await
|
|
7949
|
+
const hash = createHash7("sha256").update(await readFile17(file)).digest("hex");
|
|
7738
7950
|
const baseEntry = {
|
|
7739
7951
|
source_path: file,
|
|
7740
7952
|
source_kind: sourceKind,
|
|
@@ -7757,7 +7969,7 @@ async function buildImportEntry(file, sourceRoot, project, today, maxBytes) {
|
|
|
7757
7969
|
reason: "policy_source_not_imported"
|
|
7758
7970
|
};
|
|
7759
7971
|
}
|
|
7760
|
-
const text = await
|
|
7972
|
+
const text = await readFile17(file, "utf8");
|
|
7761
7973
|
const extracted = extractImportText(text, sourceKind);
|
|
7762
7974
|
if (!extracted) {
|
|
7763
7975
|
return {
|
|
@@ -7786,8 +7998,8 @@ async function writeImportCapture(vault, entry, today) {
|
|
|
7786
7998
|
const content = hiddenString(entry, "__content");
|
|
7787
7999
|
const project = hiddenString(entry, "__project");
|
|
7788
8000
|
const relPath = await availableImportPath(vault, entry.proposed_path);
|
|
7789
|
-
const absPath =
|
|
7790
|
-
await
|
|
8001
|
+
const absPath = join27(vault, relPath);
|
|
8002
|
+
await mkdir7(join27(vault, "raw", "transcripts"), { recursive: true });
|
|
7791
8003
|
await writeFile5(absPath, renderImportCapture(entry, content, project, today), "utf8");
|
|
7792
8004
|
const validation = await runValidate({ file: absPath });
|
|
7793
8005
|
return {
|
|
@@ -7803,7 +8015,7 @@ async function availableImportPath(vault, proposed) {
|
|
|
7803
8015
|
const stem = proposed.slice(0, -ext.length);
|
|
7804
8016
|
let candidate = proposed;
|
|
7805
8017
|
let i = 2;
|
|
7806
|
-
while (await readIfExists2(
|
|
8018
|
+
while (await readIfExists2(join27(vault, candidate))) {
|
|
7807
8019
|
candidate = `${stem}-${i}${ext}`;
|
|
7808
8020
|
i++;
|
|
7809
8021
|
}
|
|
@@ -7988,10 +8200,10 @@ function memoryCacheRelPath(project) {
|
|
|
7988
8200
|
}
|
|
7989
8201
|
async function readMemoryCache(vault, project) {
|
|
7990
8202
|
if (project) {
|
|
7991
|
-
const projectCache = await readIfExists2(
|
|
8203
|
+
const projectCache = await readIfExists2(join27(vault, memoryCacheRelPath(project)));
|
|
7992
8204
|
if (projectCache) return projectCache;
|
|
7993
8205
|
}
|
|
7994
|
-
return readIfExists2(
|
|
8206
|
+
return readIfExists2(join27(vault, ".skillwiki", "memory-topics.json"));
|
|
7995
8207
|
}
|
|
7996
8208
|
function dedupePages(pages) {
|
|
7997
8209
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -8082,8 +8294,8 @@ function slugify2(value) {
|
|
|
8082
8294
|
}
|
|
8083
8295
|
|
|
8084
8296
|
// src/commands/query.ts
|
|
8085
|
-
import { readFile as
|
|
8086
|
-
import { join as
|
|
8297
|
+
import { readFile as readFile18, stat as stat6 } from "fs/promises";
|
|
8298
|
+
import { join as join28 } from "path";
|
|
8087
8299
|
var W_KEYWORD = 2;
|
|
8088
8300
|
var W_SOURCE_OVERLAP = 4;
|
|
8089
8301
|
var W_WIKILINK = 3;
|
|
@@ -8204,7 +8416,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8204
8416
|
return score;
|
|
8205
8417
|
}
|
|
8206
8418
|
async function loadOrBuildGraph(vault) {
|
|
8207
|
-
const graphPath =
|
|
8419
|
+
const graphPath = join28(vault, ".skillwiki", "graph.json");
|
|
8208
8420
|
let needsBuild = false;
|
|
8209
8421
|
try {
|
|
8210
8422
|
const fileStat = await stat6(graphPath);
|
|
@@ -8218,7 +8430,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
8218
8430
|
if (buildResult.exitCode !== 0) return null;
|
|
8219
8431
|
}
|
|
8220
8432
|
try {
|
|
8221
|
-
const raw = await
|
|
8433
|
+
const raw = await readFile18(graphPath, "utf8");
|
|
8222
8434
|
return JSON.parse(raw);
|
|
8223
8435
|
} catch {
|
|
8224
8436
|
return null;
|
|
@@ -8233,7 +8445,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
8233
8445
|
import { z } from "zod";
|
|
8234
8446
|
|
|
8235
8447
|
// src/mcp/vault-resolve.ts
|
|
8236
|
-
import { join as
|
|
8448
|
+
import { join as join29, resolve as resolve7 } from "path";
|
|
8237
8449
|
|
|
8238
8450
|
// src/mcp/allowlist.ts
|
|
8239
8451
|
import { resolve as resolve6, sep as sep4 } from "path";
|
|
@@ -8295,7 +8507,7 @@ async function resolveMcpVault(input) {
|
|
|
8295
8507
|
return ok({ vault: vaultPath, source });
|
|
8296
8508
|
}
|
|
8297
8509
|
function defaultGraphOut(vault) {
|
|
8298
|
-
return
|
|
8510
|
+
return join29(vault, ".skillwiki", "graph.json");
|
|
8299
8511
|
}
|
|
8300
8512
|
|
|
8301
8513
|
// src/mcp/result-format.ts
|
|
@@ -8312,7 +8524,7 @@ function formatToolResult(payload) {
|
|
|
8312
8524
|
// src/mcp/audit-log.ts
|
|
8313
8525
|
import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
|
|
8314
8526
|
import { homedir } from "os";
|
|
8315
|
-
import { join as
|
|
8527
|
+
import { join as join30 } from "path";
|
|
8316
8528
|
function auditEnabled() {
|
|
8317
8529
|
const v = process.env.SKILLWIKI_MCP_AUDIT;
|
|
8318
8530
|
if (v === "0" || v === "false") return false;
|
|
@@ -8324,7 +8536,7 @@ function auditSink() {
|
|
|
8324
8536
|
function auditFilePath() {
|
|
8325
8537
|
const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
|
|
8326
8538
|
if (custom && custom.length > 0) return custom;
|
|
8327
|
-
return
|
|
8539
|
+
return join30(homedir(), ".skillwiki", "mcp-audit.jsonl");
|
|
8328
8540
|
}
|
|
8329
8541
|
function auditMcpToolCall(entry) {
|
|
8330
8542
|
if (!auditEnabled()) return;
|
|
@@ -8334,7 +8546,7 @@ function auditMcpToolCall(entry) {
|
|
|
8334
8546
|
return;
|
|
8335
8547
|
}
|
|
8336
8548
|
const path = auditFilePath();
|
|
8337
|
-
mkdirSync5(
|
|
8549
|
+
mkdirSync5(join30(path, ".."), { recursive: true });
|
|
8338
8550
|
appendFileSync(path, line, "utf8");
|
|
8339
8551
|
}
|
|
8340
8552
|
async function runMcpToolHandler(tool, input, fn) {
|
|
@@ -8554,8 +8766,8 @@ function registerMcpMutatingTools(server) {
|
|
|
8554
8766
|
}
|
|
8555
8767
|
|
|
8556
8768
|
// src/mcp/resources.ts
|
|
8557
|
-
import { readFile as
|
|
8558
|
-
import { join as
|
|
8769
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
8770
|
+
import { join as join32 } from "path";
|
|
8559
8771
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8560
8772
|
|
|
8561
8773
|
// src/mcp/lint-bucket.ts
|
|
@@ -8683,8 +8895,8 @@ async function fetchQueryPreview(input) {
|
|
|
8683
8895
|
}
|
|
8684
8896
|
|
|
8685
8897
|
// src/mcp/graph-html.ts
|
|
8686
|
-
import { readFile as
|
|
8687
|
-
import { join as
|
|
8898
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
8899
|
+
import { join as join31 } from "path";
|
|
8688
8900
|
import { existsSync as existsSync15 } from "fs";
|
|
8689
8901
|
var TYPE_COLORS = {
|
|
8690
8902
|
entities: "#e74c3c",
|
|
@@ -8753,7 +8965,7 @@ ${nodeSvg}
|
|
|
8753
8965
|
return { html, node_count: nodes.length, edge_count: edges.length, truncated };
|
|
8754
8966
|
}
|
|
8755
8967
|
async function fetchGraphHtmlReport(input) {
|
|
8756
|
-
const graphPath = input.graphPath ??
|
|
8968
|
+
const graphPath = input.graphPath ?? join31(input.vault, ".skillwiki", "graph.json");
|
|
8757
8969
|
const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
|
|
8758
8970
|
if (!existsSync15(graphPath)) {
|
|
8759
8971
|
return {
|
|
@@ -8763,7 +8975,7 @@ async function fetchGraphHtmlReport(input) {
|
|
|
8763
8975
|
}
|
|
8764
8976
|
let raw;
|
|
8765
8977
|
try {
|
|
8766
|
-
raw = await
|
|
8978
|
+
raw = await readFile19(graphPath, "utf8");
|
|
8767
8979
|
} catch (e) {
|
|
8768
8980
|
return {
|
|
8769
8981
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -8827,7 +9039,7 @@ async function fetchStaleSummary(input) {
|
|
|
8827
9039
|
|
|
8828
9040
|
// src/mcp/resources.ts
|
|
8829
9041
|
async function readVaultFile(vault, rel) {
|
|
8830
|
-
return
|
|
9042
|
+
return readFile20(join32(vault, rel), "utf8");
|
|
8831
9043
|
}
|
|
8832
9044
|
async function tailLines(text, lines) {
|
|
8833
9045
|
const parts = text.split(/\r?\n/);
|
|
@@ -8913,9 +9125,9 @@ function registerMcpResources(server) {
|
|
|
8913
9125
|
if (!v.ok) {
|
|
8914
9126
|
return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
|
|
8915
9127
|
}
|
|
8916
|
-
const path =
|
|
9128
|
+
const path = join32(v.data.vault, ".skillwiki", "graph.json");
|
|
8917
9129
|
try {
|
|
8918
|
-
const raw = await
|
|
9130
|
+
const raw = await readFile20(path, "utf8");
|
|
8919
9131
|
const graph = JSON.parse(raw);
|
|
8920
9132
|
const adjacency = graph.adjacency ?? {};
|
|
8921
9133
|
const nodes = Object.keys(adjacency);
|
|
@@ -9228,6 +9440,8 @@ export {
|
|
|
9228
9440
|
readLastOp,
|
|
9229
9441
|
appendLastOp,
|
|
9230
9442
|
clearLastOp,
|
|
9443
|
+
writeLogEvent,
|
|
9444
|
+
readLogEvents,
|
|
9231
9445
|
runLogAppend,
|
|
9232
9446
|
renderIndexUpsert,
|
|
9233
9447
|
upsertIndexEntry,
|
|
@@ -9258,6 +9472,7 @@ export {
|
|
|
9258
9472
|
runStale,
|
|
9259
9473
|
runPagesize,
|
|
9260
9474
|
runLogRotate,
|
|
9475
|
+
scanConflictMarkerBlocksInText,
|
|
9261
9476
|
runTopicMapCheck,
|
|
9262
9477
|
runIndexLinkFormat,
|
|
9263
9478
|
normalizeRemoteRoot,
|