skillwiki 0.5.3 → 0.5.4
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/cli.js +253 -153
- 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
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { readFileSync as readFileSync10 } from "fs";
|
|
12
|
-
import { join as
|
|
12
|
+
import { join as join39 } from "path";
|
|
13
13
|
import { Command as Command2 } from "commander";
|
|
14
14
|
|
|
15
15
|
// ../shared/src/exit-codes.ts
|
|
@@ -60,7 +60,8 @@ var ExitCode = {
|
|
|
60
60
|
SYNC_PULL_FAILED: 43,
|
|
61
61
|
BACKUP_SYNC_FAILED: 44,
|
|
62
62
|
BACKUP_RESTORE_CONFLICTS: 45,
|
|
63
|
-
USAGE: 46
|
|
63
|
+
USAGE: 46,
|
|
64
|
+
BODY_TRUNCATION_GUARD: 47
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
// ../shared/src/json-output.ts
|
|
@@ -307,11 +308,11 @@ async function runHash(input) {
|
|
|
307
308
|
}
|
|
308
309
|
const split = splitFrontmatter(text);
|
|
309
310
|
if (!split.ok) return { exitCode: ExitCode.MISSING_CLOSING_DELIMITER, result: split };
|
|
310
|
-
const
|
|
311
|
-
const sha256 = createHash("sha256").update(
|
|
311
|
+
const bodyBytes2 = Buffer.from(split.data.body, "utf8");
|
|
312
|
+
const sha256 = createHash("sha256").update(bodyBytes2).digest("hex");
|
|
312
313
|
return {
|
|
313
314
|
exitCode: ExitCode.OK,
|
|
314
|
-
result: ok({ path: input.file, sha256, byte_count:
|
|
315
|
+
result: ok({ path: input.file, sha256, byte_count: bodyBytes2.byteLength, humanHint: sha256 })
|
|
315
316
|
};
|
|
316
317
|
}
|
|
317
318
|
|
|
@@ -1957,9 +1958,9 @@ async function runStale(input) {
|
|
|
1957
1958
|
if (input.project && !project.includes(input.project)) continue;
|
|
1958
1959
|
let inferred = false;
|
|
1959
1960
|
if (input.forceScan && !kind) {
|
|
1960
|
-
const
|
|
1961
|
-
if (!LOOP_CYCLE_PATTERN.test(
|
|
1962
|
-
const m =
|
|
1961
|
+
const basename2 = t.relPath.split("/").pop();
|
|
1962
|
+
if (!LOOP_CYCLE_PATTERN.test(basename2)) {
|
|
1963
|
+
const m = basename2.match(KIND_FROM_FILENAME);
|
|
1963
1964
|
if (m) {
|
|
1964
1965
|
kind = m[1];
|
|
1965
1966
|
inferred = true;
|
|
@@ -2362,8 +2363,8 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
|
|
|
2362
2363
|
|
|
2363
2364
|
// src/commands/lint.ts
|
|
2364
2365
|
import { existsSync as existsSync3 } from "fs";
|
|
2365
|
-
import { readFile as
|
|
2366
|
-
import { join as
|
|
2366
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
2367
|
+
import { join as join19 } from "path";
|
|
2367
2368
|
|
|
2368
2369
|
// src/commands/topic-map-check.ts
|
|
2369
2370
|
var DEFAULT_THRESHOLD = 200;
|
|
@@ -2490,6 +2491,81 @@ async function runDedup(input) {
|
|
|
2490
2491
|
};
|
|
2491
2492
|
}
|
|
2492
2493
|
|
|
2494
|
+
// src/utils/safe-write.ts
|
|
2495
|
+
import { open, readFile as readFile14, rename as rename4, unlink as unlink2, writeFile as writeFile8 } from "fs/promises";
|
|
2496
|
+
import { randomBytes } from "crypto";
|
|
2497
|
+
import { dirname as dirname7, basename, join as join18 } from "path";
|
|
2498
|
+
var DEFAULT_MIN_BODY_RATIO = 0.5;
|
|
2499
|
+
var DEFAULT_MIN_OLD_BODY_BYTES = 200;
|
|
2500
|
+
function bodyBytes(text) {
|
|
2501
|
+
const split = splitFrontmatter(text);
|
|
2502
|
+
if (!split.ok) return Buffer.byteLength(text, "utf8");
|
|
2503
|
+
return Buffer.byteLength(split.data.body, "utf8");
|
|
2504
|
+
}
|
|
2505
|
+
async function readIfExists(absPath) {
|
|
2506
|
+
try {
|
|
2507
|
+
return await readFile14(absPath, "utf8");
|
|
2508
|
+
} catch (e) {
|
|
2509
|
+
if (e.code === "ENOENT") return null;
|
|
2510
|
+
throw e;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
async function safeWritePage(absPath, newContent, opts = {}) {
|
|
2514
|
+
const minRatio = opts.minBodyRatio === void 0 ? DEFAULT_MIN_BODY_RATIO : opts.minBodyRatio;
|
|
2515
|
+
const minOldBytes = opts.minOldBodyBytes ?? DEFAULT_MIN_OLD_BODY_BYTES;
|
|
2516
|
+
let oldContent;
|
|
2517
|
+
try {
|
|
2518
|
+
oldContent = await readIfExists(absPath);
|
|
2519
|
+
} catch (e) {
|
|
2520
|
+
return err("WRITE_FAILED", { path: absPath, phase: "read-existing", message: String(e) });
|
|
2521
|
+
}
|
|
2522
|
+
const isNew = oldContent === null;
|
|
2523
|
+
const oldBodyBytes = isNew ? 0 : bodyBytes(oldContent);
|
|
2524
|
+
const newBodyBytes = bodyBytes(newContent);
|
|
2525
|
+
const bodyRatio = oldBodyBytes > 0 ? newBodyBytes / oldBodyBytes : null;
|
|
2526
|
+
let guardSkippedSmall = false;
|
|
2527
|
+
if (!isNew && minRatio !== null && bodyRatio !== null && bodyRatio < minRatio) {
|
|
2528
|
+
if (oldBodyBytes < minOldBytes) {
|
|
2529
|
+
guardSkippedSmall = true;
|
|
2530
|
+
} else {
|
|
2531
|
+
return err("BODY_TRUNCATION_GUARD", {
|
|
2532
|
+
path: absPath,
|
|
2533
|
+
oldBodyBytes,
|
|
2534
|
+
newBodyBytes,
|
|
2535
|
+
bodyRatio,
|
|
2536
|
+
minBodyRatio: minRatio,
|
|
2537
|
+
hint: "Refusing to write \u2014 new body lost too much content. Likely a parse-modify-serialize bug or a write race. Verify the page source before retrying."
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (!isNew && oldContent === newContent) {
|
|
2542
|
+
return ok({ isNew: false, oldBodyBytes, newBodyBytes, bodyRatio, guardSkippedSmall });
|
|
2543
|
+
}
|
|
2544
|
+
const dir = dirname7(absPath);
|
|
2545
|
+
const tmpName = `.${basename(absPath)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
2546
|
+
const tmpPath = join18(dir, tmpName);
|
|
2547
|
+
try {
|
|
2548
|
+
const handle = await open(tmpPath, "w");
|
|
2549
|
+
try {
|
|
2550
|
+
await handle.writeFile(newContent, "utf8");
|
|
2551
|
+
try {
|
|
2552
|
+
await handle.sync();
|
|
2553
|
+
} catch {
|
|
2554
|
+
}
|
|
2555
|
+
} finally {
|
|
2556
|
+
await handle.close();
|
|
2557
|
+
}
|
|
2558
|
+
await rename4(tmpPath, absPath);
|
|
2559
|
+
return ok({ isNew, oldBodyBytes, newBodyBytes, bodyRatio, guardSkippedSmall });
|
|
2560
|
+
} catch (e) {
|
|
2561
|
+
try {
|
|
2562
|
+
await unlink2(tmpPath);
|
|
2563
|
+
} catch {
|
|
2564
|
+
}
|
|
2565
|
+
return err("WRITE_FAILED", { path: absPath, phase: "atomic-write", message: String(e) });
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2493
2569
|
// src/commands/raw-body-dedup.ts
|
|
2494
2570
|
import { createHash as createHash2 } from "crypto";
|
|
2495
2571
|
async function runRawBodyDedup(vault) {
|
|
@@ -2813,7 +2889,7 @@ async function runLint(input) {
|
|
|
2813
2889
|
let rawPath = entry.replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
2814
2890
|
rawPath = rawPath.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
2815
2891
|
if (!rawPath.startsWith("raw/") && !rawPath.startsWith("_archive/raw/")) continue;
|
|
2816
|
-
if (!existsSync3(
|
|
2892
|
+
if (!existsSync3(join19(input.vault, rawPath)) && !existsSync3(join19(input.vault, rawPath + ".md")) && !rawPath.startsWith("_archive/") && !existsSync3(join19(input.vault, "_archive", rawPath)) && !existsSync3(join19(input.vault, "_archive", rawPath + ".md"))) {
|
|
2817
2893
|
brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
|
|
2818
2894
|
}
|
|
2819
2895
|
}
|
|
@@ -2904,11 +2980,11 @@ async function runLint(input) {
|
|
|
2904
2980
|
const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
|
|
2905
2981
|
if (!slugMatch) continue;
|
|
2906
2982
|
const slug = slugMatch[1];
|
|
2907
|
-
const knowledgePath =
|
|
2983
|
+
const knowledgePath = join19(input.vault, "projects", slug, "knowledge.md");
|
|
2908
2984
|
if (!existsSync3(knowledgePath)) continue;
|
|
2909
2985
|
const pageRef = page.relPath.replace(/\.md$/, "");
|
|
2910
2986
|
try {
|
|
2911
|
-
const knowledgeContent = await
|
|
2987
|
+
const knowledgeContent = await readFile15(knowledgePath, "utf8");
|
|
2912
2988
|
if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
|
|
2913
2989
|
orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
|
|
2914
2990
|
}
|
|
@@ -2955,7 +3031,7 @@ async function runLint(input) {
|
|
|
2955
3031
|
for (const relPath of legacyPages) {
|
|
2956
3032
|
try {
|
|
2957
3033
|
const absPath = `${input.vault}/${relPath}`;
|
|
2958
|
-
const raw = await
|
|
3034
|
+
const raw = await readFile15(absPath, "utf8");
|
|
2959
3035
|
const split = splitFrontmatter(raw);
|
|
2960
3036
|
if (!split.ok) {
|
|
2961
3037
|
unresolved.push(relPath);
|
|
@@ -3033,7 +3109,11 @@ async function runLint(input) {
|
|
|
3033
3109
|
${rawFm}
|
|
3034
3110
|
---
|
|
3035
3111
|
${newBody}`;
|
|
3036
|
-
await
|
|
3112
|
+
const w = await safeWritePage(absPath, newContent);
|
|
3113
|
+
if (!w.ok) {
|
|
3114
|
+
unresolved.push(relPath);
|
|
3115
|
+
continue;
|
|
3116
|
+
}
|
|
3037
3117
|
fixed.push(relPath);
|
|
3038
3118
|
} catch {
|
|
3039
3119
|
unresolved.push(relPath);
|
|
@@ -3050,7 +3130,7 @@ ${newBody}`;
|
|
|
3050
3130
|
for (const relPath of noOverview) {
|
|
3051
3131
|
try {
|
|
3052
3132
|
const absPath = `${input.vault}/${relPath}`;
|
|
3053
|
-
const raw = await
|
|
3133
|
+
const raw = await readFile15(absPath, "utf8");
|
|
3054
3134
|
const split = splitFrontmatter(raw);
|
|
3055
3135
|
if (!split.ok) {
|
|
3056
3136
|
unresolved.push(relPath);
|
|
@@ -3071,7 +3151,11 @@ ${rawFm}
|
|
|
3071
3151
|
${overviewSection}
|
|
3072
3152
|
|
|
3073
3153
|
${trimmedBody}`;
|
|
3074
|
-
await
|
|
3154
|
+
const w = await safeWritePage(absPath, newContent);
|
|
3155
|
+
if (!w.ok) {
|
|
3156
|
+
unresolved.push(relPath);
|
|
3157
|
+
continue;
|
|
3158
|
+
}
|
|
3075
3159
|
fixed.push(relPath);
|
|
3076
3160
|
} catch {
|
|
3077
3161
|
unresolved.push(relPath);
|
|
@@ -3087,7 +3171,7 @@ ${trimmedBody}`;
|
|
|
3087
3171
|
for (const relPath of missingTldrFlags) {
|
|
3088
3172
|
try {
|
|
3089
3173
|
const absPath = `${input.vault}/${relPath}`;
|
|
3090
|
-
const raw = await
|
|
3174
|
+
const raw = await readFile15(absPath, "utf8");
|
|
3091
3175
|
const split = splitFrontmatter(raw);
|
|
3092
3176
|
if (!split.ok) {
|
|
3093
3177
|
unresolved.push(relPath);
|
|
@@ -3115,7 +3199,11 @@ ${trimmedBody}`;
|
|
|
3115
3199
|
const newContent = `---
|
|
3116
3200
|
${trimmedFm}---
|
|
3117
3201
|
${lines.join("\n")}`;
|
|
3118
|
-
await
|
|
3202
|
+
const w = await safeWritePage(absPath, newContent);
|
|
3203
|
+
if (!w.ok) {
|
|
3204
|
+
unresolved.push(relPath);
|
|
3205
|
+
continue;
|
|
3206
|
+
}
|
|
3119
3207
|
fixed.push(relPath);
|
|
3120
3208
|
} catch {
|
|
3121
3209
|
unresolved.push(relPath);
|
|
@@ -3133,7 +3221,7 @@ ${lines.join("\n")}`;
|
|
|
3133
3221
|
for (const relPath of wikilinkCitationFlags) {
|
|
3134
3222
|
try {
|
|
3135
3223
|
const absPath = `${input.vault}/${relPath}`;
|
|
3136
|
-
const raw = await
|
|
3224
|
+
const raw = await readFile15(absPath, "utf8");
|
|
3137
3225
|
const split = splitFrontmatter(raw);
|
|
3138
3226
|
if (!split.ok) {
|
|
3139
3227
|
unresolved.push(relPath);
|
|
@@ -3197,7 +3285,11 @@ ${lines.join("\n")}`;
|
|
|
3197
3285
|
${rawFm}
|
|
3198
3286
|
---
|
|
3199
3287
|
${newBody}`;
|
|
3200
|
-
await
|
|
3288
|
+
const w = await safeWritePage(absPath, newContent);
|
|
3289
|
+
if (!w.ok) {
|
|
3290
|
+
unresolved.push(relPath);
|
|
3291
|
+
continue;
|
|
3292
|
+
}
|
|
3201
3293
|
wikilinkFixed.push(relPath);
|
|
3202
3294
|
} catch {
|
|
3203
3295
|
unresolved.push(relPath);
|
|
@@ -3286,14 +3378,14 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
|
|
|
3286
3378
|
}
|
|
3287
3379
|
|
|
3288
3380
|
// src/commands/config.ts
|
|
3289
|
-
import { readFile as
|
|
3381
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
3290
3382
|
import { existsSync as existsSync4 } from "fs";
|
|
3291
|
-
import { join as
|
|
3383
|
+
import { join as join20 } from "path";
|
|
3292
3384
|
function validateKey(key) {
|
|
3293
3385
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
3294
3386
|
}
|
|
3295
3387
|
function configPath(home) {
|
|
3296
|
-
return
|
|
3388
|
+
return join20(home, ".skillwiki", ".env");
|
|
3297
3389
|
}
|
|
3298
3390
|
async function runConfigGet(input) {
|
|
3299
3391
|
if (!validateKey(input.key)) {
|
|
@@ -3311,7 +3403,7 @@ async function runConfigSet(input) {
|
|
|
3311
3403
|
try {
|
|
3312
3404
|
let originalContent;
|
|
3313
3405
|
try {
|
|
3314
|
-
originalContent = await
|
|
3406
|
+
originalContent = await readFile16(filePath, "utf8");
|
|
3315
3407
|
} catch {
|
|
3316
3408
|
}
|
|
3317
3409
|
const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
|
|
@@ -3348,13 +3440,13 @@ async function runConfigPath(input) {
|
|
|
3348
3440
|
|
|
3349
3441
|
// src/commands/doctor.ts
|
|
3350
3442
|
import { existsSync as existsSync6, lstatSync, readlinkSync, readdirSync, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
|
|
3351
|
-
import { join as
|
|
3443
|
+
import { join as join23, resolve as resolve4 } from "path";
|
|
3352
3444
|
import { execSync } from "child_process";
|
|
3353
3445
|
import { platform } from "os";
|
|
3354
3446
|
|
|
3355
3447
|
// src/utils/auto-update.ts
|
|
3356
3448
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
3357
|
-
import { join as
|
|
3449
|
+
import { join as join21, dirname as dirname8 } from "path";
|
|
3358
3450
|
import { spawn } from "child_process";
|
|
3359
3451
|
|
|
3360
3452
|
// src/utils/update-consts.ts
|
|
@@ -3365,7 +3457,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
|
|
|
3365
3457
|
|
|
3366
3458
|
// src/utils/auto-update.ts
|
|
3367
3459
|
function cachePath(home) {
|
|
3368
|
-
return
|
|
3460
|
+
return join21(home, ".skillwiki", CACHE_FILENAME);
|
|
3369
3461
|
}
|
|
3370
3462
|
function readCacheRaw(home) {
|
|
3371
3463
|
try {
|
|
@@ -3384,7 +3476,7 @@ function readCache(home) {
|
|
|
3384
3476
|
}
|
|
3385
3477
|
function writeCache(home, cache) {
|
|
3386
3478
|
const p = cachePath(home);
|
|
3387
|
-
mkdirSync2(
|
|
3479
|
+
mkdirSync2(dirname8(p), { recursive: true });
|
|
3388
3480
|
writeFileSync3(p, JSON.stringify(cache, null, 2));
|
|
3389
3481
|
}
|
|
3390
3482
|
function latestFromCache(home, currentVersion) {
|
|
@@ -3415,12 +3507,12 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
3415
3507
|
|
|
3416
3508
|
// src/utils/plugin-registry.ts
|
|
3417
3509
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3418
|
-
import { join as
|
|
3419
|
-
var REGISTRY_PATH =
|
|
3510
|
+
import { join as join22 } from "path";
|
|
3511
|
+
var REGISTRY_PATH = join22(".claude", "plugins", "installed_plugins.json");
|
|
3420
3512
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
3421
3513
|
function readInstalledPlugins(home) {
|
|
3422
3514
|
try {
|
|
3423
|
-
const raw = readFileSync5(
|
|
3515
|
+
const raw = readFileSync5(join22(home, REGISTRY_PATH), "utf8");
|
|
3424
3516
|
return JSON.parse(raw);
|
|
3425
3517
|
} catch {
|
|
3426
3518
|
return null;
|
|
@@ -3463,12 +3555,12 @@ function detectCliChannels(argv, home) {
|
|
|
3463
3555
|
}
|
|
3464
3556
|
const plugin = findPlugin(home);
|
|
3465
3557
|
if (plugin) {
|
|
3466
|
-
const pluginBin =
|
|
3558
|
+
const pluginBin = join23(plugin.installPath, "bin", "skillwiki");
|
|
3467
3559
|
if (existsSync6(pluginBin)) {
|
|
3468
3560
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
3469
3561
|
}
|
|
3470
3562
|
}
|
|
3471
|
-
const installBin =
|
|
3563
|
+
const installBin = join23(home, ".claude", "skills", "bin", "skillwiki");
|
|
3472
3564
|
if (existsSync6(installBin)) {
|
|
3473
3565
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
3474
3566
|
}
|
|
@@ -3549,9 +3641,9 @@ function checkVaultStructure(resolvedPath) {
|
|
|
3549
3641
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
3550
3642
|
}
|
|
3551
3643
|
const missing = [];
|
|
3552
|
-
if (!existsSync6(
|
|
3644
|
+
if (!existsSync6(join23(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
3553
3645
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
3554
|
-
if (!existsSync6(
|
|
3646
|
+
if (!existsSync6(join23(resolvedPath, dir))) missing.push(dir + "/");
|
|
3555
3647
|
}
|
|
3556
3648
|
if (missing.length === 0) {
|
|
3557
3649
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -3559,7 +3651,7 @@ function checkVaultStructure(resolvedPath) {
|
|
|
3559
3651
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
3560
3652
|
}
|
|
3561
3653
|
function checkSkillsInstalled(home, cwd) {
|
|
3562
|
-
const srcDir = cwd ?
|
|
3654
|
+
const srcDir = cwd ? join23(cwd, "packages", "skills") : void 0;
|
|
3563
3655
|
if (srcDir && existsSync6(srcDir)) {
|
|
3564
3656
|
const found = findSkillMd(srcDir);
|
|
3565
3657
|
if (found.length > 0) {
|
|
@@ -3573,7 +3665,7 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
3573
3665
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
3574
3666
|
}
|
|
3575
3667
|
}
|
|
3576
|
-
const skillsDir =
|
|
3668
|
+
const skillsDir = join23(home, ".claude", "skills");
|
|
3577
3669
|
if (existsSync6(skillsDir)) {
|
|
3578
3670
|
const found = findSkillMd(skillsDir);
|
|
3579
3671
|
if (found.length > 0) {
|
|
@@ -3584,10 +3676,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
3584
3676
|
}
|
|
3585
3677
|
function checkDuplicateSkills(home) {
|
|
3586
3678
|
const plugin = findPlugin(home);
|
|
3587
|
-
const skillsDir =
|
|
3679
|
+
const skillsDir = join23(home, ".claude", "skills");
|
|
3588
3680
|
const agentSkillDirs = [
|
|
3589
|
-
{ label: "~/.codex/skills/", path:
|
|
3590
|
-
{ label: "~/.agents/skills/", path:
|
|
3681
|
+
{ label: "~/.codex/skills/", path: join23(home, ".codex", "skills") },
|
|
3682
|
+
{ label: "~/.agents/skills/", path: join23(home, ".agents", "skills") }
|
|
3591
3683
|
];
|
|
3592
3684
|
if (!plugin) {
|
|
3593
3685
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -3664,7 +3756,7 @@ async function checkProfiles(home) {
|
|
|
3664
3756
|
}
|
|
3665
3757
|
async function checkProjectLocalOverride(cwd) {
|
|
3666
3758
|
const dir = cwd ?? process.cwd();
|
|
3667
|
-
const envPath =
|
|
3759
|
+
const envPath = join23(dir, ".skillwiki", ".env");
|
|
3668
3760
|
if (existsSync6(envPath)) {
|
|
3669
3761
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
3670
3762
|
}
|
|
@@ -3674,7 +3766,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
3674
3766
|
if (resolvedPath === void 0) {
|
|
3675
3767
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
3676
3768
|
}
|
|
3677
|
-
if (!existsSync6(
|
|
3769
|
+
if (!existsSync6(join23(resolvedPath, ".git"))) {
|
|
3678
3770
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
3679
3771
|
}
|
|
3680
3772
|
try {
|
|
@@ -3697,9 +3789,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
3697
3789
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
3698
3790
|
}
|
|
3699
3791
|
const missing = [];
|
|
3700
|
-
if (!existsSync6(
|
|
3701
|
-
if (!existsSync6(
|
|
3702
|
-
if (!existsSync6(
|
|
3792
|
+
if (!existsSync6(join23(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
3793
|
+
if (!existsSync6(join23(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
3794
|
+
if (!existsSync6(join23(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
3703
3795
|
if (missing.length === 0) {
|
|
3704
3796
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
3705
3797
|
}
|
|
@@ -3709,7 +3801,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
3709
3801
|
if (resolvedPath === void 0) {
|
|
3710
3802
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
3711
3803
|
}
|
|
3712
|
-
const rawDir =
|
|
3804
|
+
const rawDir = join23(resolvedPath, "raw");
|
|
3713
3805
|
if (!existsSync6(rawDir)) {
|
|
3714
3806
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
3715
3807
|
}
|
|
@@ -3725,7 +3817,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
3725
3817
|
if (entry.name === ".DS_Store") {
|
|
3726
3818
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
3727
3819
|
} else if (entry.isDirectory()) {
|
|
3728
|
-
walk2(
|
|
3820
|
+
walk2(join23(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
3729
3821
|
}
|
|
3730
3822
|
}
|
|
3731
3823
|
})(rawDir, "");
|
|
@@ -3738,7 +3830,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
3738
3830
|
if (resolvedPath === void 0) {
|
|
3739
3831
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
3740
3832
|
}
|
|
3741
|
-
if (!existsSync6(
|
|
3833
|
+
if (!existsSync6(join23(resolvedPath, ".git"))) {
|
|
3742
3834
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
3743
3835
|
}
|
|
3744
3836
|
let timestamp;
|
|
@@ -3812,7 +3904,7 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
3812
3904
|
if (!mountPoint) {
|
|
3813
3905
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
3814
3906
|
}
|
|
3815
|
-
const conceptsDir =
|
|
3907
|
+
const conceptsDir = join23(resolvedPath, "concepts");
|
|
3816
3908
|
if (!existsSync6(conceptsDir)) {
|
|
3817
3909
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
3818
3910
|
}
|
|
@@ -3862,9 +3954,9 @@ function findSkillMd(dir) {
|
|
|
3862
3954
|
}
|
|
3863
3955
|
for (const entry of entries) {
|
|
3864
3956
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
3865
|
-
results.push(
|
|
3957
|
+
results.push(join23(dir, entry.name));
|
|
3866
3958
|
} else if (entry.isDirectory()) {
|
|
3867
|
-
results.push(...findSkillMd(
|
|
3959
|
+
results.push(...findSkillMd(join23(dir, entry.name)));
|
|
3868
3960
|
}
|
|
3869
3961
|
}
|
|
3870
3962
|
return results;
|
|
@@ -3878,7 +3970,7 @@ function findSkillNames(dir) {
|
|
|
3878
3970
|
return results;
|
|
3879
3971
|
}
|
|
3880
3972
|
for (const entry of entries) {
|
|
3881
|
-
if (entry.isDirectory() && existsSync6(
|
|
3973
|
+
if (entry.isDirectory() && existsSync6(join23(dir, entry.name, "SKILL.md"))) {
|
|
3882
3974
|
results.push(entry.name);
|
|
3883
3975
|
}
|
|
3884
3976
|
}
|
|
@@ -3932,8 +4024,8 @@ async function runDoctor(input) {
|
|
|
3932
4024
|
}
|
|
3933
4025
|
|
|
3934
4026
|
// src/commands/archive.ts
|
|
3935
|
-
import { rename as
|
|
3936
|
-
import { join as
|
|
4027
|
+
import { rename as rename5, mkdir as mkdir8, readFile as readFile17, writeFile as writeFile9 } from "fs/promises";
|
|
4028
|
+
import { join as join24, dirname as dirname9 } from "path";
|
|
3937
4029
|
async function runArchive(input) {
|
|
3938
4030
|
const scan = await scanVault(input.vault);
|
|
3939
4031
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -3949,13 +4041,13 @@ async function runArchive(input) {
|
|
|
3949
4041
|
}
|
|
3950
4042
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
3951
4043
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
3952
|
-
const archivePath =
|
|
3953
|
-
await mkdir8(
|
|
4044
|
+
const archivePath = join24("_archive", relPath).replace(/\\/g, "/");
|
|
4045
|
+
await mkdir8(dirname9(join24(input.vault, archivePath)), { recursive: true });
|
|
3954
4046
|
let indexUpdated = false;
|
|
3955
4047
|
if (!isRaw) {
|
|
3956
|
-
const indexPath =
|
|
4048
|
+
const indexPath = join24(input.vault, "index.md");
|
|
3957
4049
|
try {
|
|
3958
|
-
const idx = await
|
|
4050
|
+
const idx = await readFile17(indexPath, "utf8");
|
|
3959
4051
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
3960
4052
|
const originalLines = idx.split("\n");
|
|
3961
4053
|
const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
|
|
@@ -3967,7 +4059,7 @@ async function runArchive(input) {
|
|
|
3967
4059
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
3968
4060
|
}
|
|
3969
4061
|
}
|
|
3970
|
-
await
|
|
4062
|
+
await rename5(join24(input.vault, relPath), join24(input.vault, archivePath));
|
|
3971
4063
|
appendLastOp(input.vault, {
|
|
3972
4064
|
operation: "archive",
|
|
3973
4065
|
summary: `moved ${relPath} to ${archivePath}`,
|
|
@@ -3979,7 +4071,6 @@ async function runArchive(input) {
|
|
|
3979
4071
|
|
|
3980
4072
|
// src/commands/drift.ts
|
|
3981
4073
|
import { createHash as createHash3 } from "crypto";
|
|
3982
|
-
import { writeFile as writeFile10 } from "fs/promises";
|
|
3983
4074
|
|
|
3984
4075
|
// src/utils/fetch.ts
|
|
3985
4076
|
async function controlledFetch(url, opts) {
|
|
@@ -4066,7 +4157,7 @@ async function runDrift(input) {
|
|
|
4066
4157
|
${newFm}
|
|
4067
4158
|
---
|
|
4068
4159
|
${body}`;
|
|
4069
|
-
await
|
|
4160
|
+
await safeWritePage(raw.absPath, newText);
|
|
4070
4161
|
results.push({
|
|
4071
4162
|
raw_path: raw.relPath,
|
|
4072
4163
|
source_url: sourceUrl,
|
|
@@ -4109,7 +4200,6 @@ ${body}`;
|
|
|
4109
4200
|
}
|
|
4110
4201
|
|
|
4111
4202
|
// src/commands/migrate-citations.ts
|
|
4112
|
-
import { writeFile as writeFile11 } from "fs/promises";
|
|
4113
4203
|
var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
|
|
4114
4204
|
function moveMarkersToParagraphEnd(body) {
|
|
4115
4205
|
const lines = body.split("\n");
|
|
@@ -4232,7 +4322,11 @@ ${migratedBody}${newFooter}`;
|
|
|
4232
4322
|
continue;
|
|
4233
4323
|
}
|
|
4234
4324
|
if (!input.dryRun) {
|
|
4235
|
-
await
|
|
4325
|
+
const w = await safeWritePage(page.absPath, newText);
|
|
4326
|
+
if (!w.ok) {
|
|
4327
|
+
skipped.push(page.relPath);
|
|
4328
|
+
continue;
|
|
4329
|
+
}
|
|
4236
4330
|
}
|
|
4237
4331
|
migrated.push(page.relPath);
|
|
4238
4332
|
}
|
|
@@ -4262,7 +4356,6 @@ ${migratedBody}${newFooter}`;
|
|
|
4262
4356
|
}
|
|
4263
4357
|
|
|
4264
4358
|
// src/commands/frontmatter-fix.ts
|
|
4265
|
-
import { writeFile as writeFile12 } from "fs/promises";
|
|
4266
4359
|
function isoToday() {
|
|
4267
4360
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4268
4361
|
}
|
|
@@ -4304,7 +4397,11 @@ ${newBody}`;
|
|
|
4304
4397
|
continue;
|
|
4305
4398
|
}
|
|
4306
4399
|
if (!input.dryRun) {
|
|
4307
|
-
await
|
|
4400
|
+
const w = await safeWritePage(page.absPath, newText);
|
|
4401
|
+
if (!w.ok) {
|
|
4402
|
+
skipped.push(page.relPath);
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4308
4405
|
}
|
|
4309
4406
|
fixed.push(page.relPath);
|
|
4310
4407
|
}
|
|
@@ -4337,14 +4434,14 @@ ${newBody}`;
|
|
|
4337
4434
|
// src/commands/update.ts
|
|
4338
4435
|
import { execSync as execSync2 } from "child_process";
|
|
4339
4436
|
import { readFileSync as readFileSync7 } from "fs";
|
|
4340
|
-
import { join as
|
|
4437
|
+
import { join as join25 } from "path";
|
|
4341
4438
|
function resolveGlobalSkillsRoot() {
|
|
4342
4439
|
try {
|
|
4343
4440
|
const globalRoot = execSync2("npm root -g", {
|
|
4344
4441
|
encoding: "utf8",
|
|
4345
4442
|
timeout: 5e3
|
|
4346
4443
|
}).trim();
|
|
4347
|
-
return
|
|
4444
|
+
return join25(globalRoot, "skillwiki", "skills");
|
|
4348
4445
|
} catch {
|
|
4349
4446
|
return null;
|
|
4350
4447
|
}
|
|
@@ -4370,7 +4467,7 @@ async function runUpdate(input) {
|
|
|
4370
4467
|
);
|
|
4371
4468
|
const currentVersion = pkg2.version;
|
|
4372
4469
|
const tag = input.distTag ?? "latest";
|
|
4373
|
-
const target =
|
|
4470
|
+
const target = join25(input.home, ".claude", "skills");
|
|
4374
4471
|
let latest;
|
|
4375
4472
|
try {
|
|
4376
4473
|
latest = execSync2(`npm view skillwiki@${tag} version`, {
|
|
@@ -4441,14 +4538,14 @@ async function runUpdate(input) {
|
|
|
4441
4538
|
// src/commands/self-update.ts
|
|
4442
4539
|
import { execSync as execSync3 } from "child_process";
|
|
4443
4540
|
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
4444
|
-
import { join as
|
|
4541
|
+
import { join as join26 } from "path";
|
|
4445
4542
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
4446
4543
|
async function runSelfUpdate(input) {
|
|
4447
4544
|
const currentVersion = JSON.parse(
|
|
4448
4545
|
readFileSync8(new URL("../../package.json", import.meta.url), "utf8")
|
|
4449
4546
|
).version;
|
|
4450
4547
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
4451
|
-
const localPkgPath =
|
|
4548
|
+
const localPkgPath = join26(sourceRoot, "packages", "cli", "package.json");
|
|
4452
4549
|
const hasLocalSource = existsSync7(localPkgPath);
|
|
4453
4550
|
if (input.check) {
|
|
4454
4551
|
let availableVersion = null;
|
|
@@ -4580,10 +4677,10 @@ async function runSelfUpdate(input) {
|
|
|
4580
4677
|
}
|
|
4581
4678
|
|
|
4582
4679
|
// src/commands/transcripts.ts
|
|
4583
|
-
import { readdir as readdir5, stat as stat6, readFile as
|
|
4584
|
-
import { join as
|
|
4680
|
+
import { readdir as readdir5, stat as stat6, readFile as readFile18 } from "fs/promises";
|
|
4681
|
+
import { join as join27 } from "path";
|
|
4585
4682
|
async function runTranscripts(input) {
|
|
4586
|
-
const dir =
|
|
4683
|
+
const dir = join27(input.vault, "raw", "transcripts");
|
|
4587
4684
|
let entries;
|
|
4588
4685
|
try {
|
|
4589
4686
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -4593,8 +4690,8 @@ async function runTranscripts(input) {
|
|
|
4593
4690
|
const transcripts = [];
|
|
4594
4691
|
for (const entry of entries) {
|
|
4595
4692
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
4596
|
-
const filePath =
|
|
4597
|
-
const content = await
|
|
4693
|
+
const filePath = join27(dir, entry.name);
|
|
4694
|
+
const content = await readFile18(filePath, "utf8");
|
|
4598
4695
|
const fm = extractFrontmatter(content);
|
|
4599
4696
|
if (!fm.ok) continue;
|
|
4600
4697
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -4611,12 +4708,12 @@ async function runTranscripts(input) {
|
|
|
4611
4708
|
}
|
|
4612
4709
|
|
|
4613
4710
|
// src/commands/project-index.ts
|
|
4614
|
-
import { readdir as readdir6, readFile as
|
|
4615
|
-
import { join as
|
|
4711
|
+
import { readdir as readdir6, readFile as readFile19, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
4712
|
+
import { join as join28, dirname as dirname10 } from "path";
|
|
4616
4713
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
4617
4714
|
async function runProjectIndex(input) {
|
|
4618
4715
|
const slug = input.slug;
|
|
4619
|
-
const projectDir =
|
|
4716
|
+
const projectDir = join28(input.vault, "projects", slug);
|
|
4620
4717
|
try {
|
|
4621
4718
|
await readdir6(projectDir);
|
|
4622
4719
|
} catch {
|
|
@@ -4627,15 +4724,15 @@ async function runProjectIndex(input) {
|
|
|
4627
4724
|
}
|
|
4628
4725
|
const wikilinkPattern = `[[${slug}]]`;
|
|
4629
4726
|
const entries = [];
|
|
4630
|
-
const compoundDir =
|
|
4727
|
+
const compoundDir = join28(input.vault, "projects", slug, "compound");
|
|
4631
4728
|
try {
|
|
4632
4729
|
const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
|
|
4633
4730
|
for (const entry of compoundFiles) {
|
|
4634
4731
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
4635
|
-
const filePath =
|
|
4732
|
+
const filePath = join28(compoundDir, entry.name);
|
|
4636
4733
|
let text;
|
|
4637
4734
|
try {
|
|
4638
|
-
text = await
|
|
4735
|
+
text = await readFile19(filePath, "utf8");
|
|
4639
4736
|
} catch {
|
|
4640
4737
|
continue;
|
|
4641
4738
|
}
|
|
@@ -4652,16 +4749,16 @@ async function runProjectIndex(input) {
|
|
|
4652
4749
|
for (const dir of LAYER2_DIRS) {
|
|
4653
4750
|
let files;
|
|
4654
4751
|
try {
|
|
4655
|
-
files = await readdir6(
|
|
4752
|
+
files = await readdir6(join28(input.vault, dir), { withFileTypes: true });
|
|
4656
4753
|
} catch {
|
|
4657
4754
|
continue;
|
|
4658
4755
|
}
|
|
4659
4756
|
for (const entry of files) {
|
|
4660
4757
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
4661
|
-
const filePath =
|
|
4758
|
+
const filePath = join28(input.vault, dir, entry.name);
|
|
4662
4759
|
let text;
|
|
4663
4760
|
try {
|
|
4664
|
-
text = await
|
|
4761
|
+
text = await readFile19(filePath, "utf8");
|
|
4665
4762
|
} catch {
|
|
4666
4763
|
continue;
|
|
4667
4764
|
}
|
|
@@ -4682,11 +4779,11 @@ async function runProjectIndex(input) {
|
|
|
4682
4779
|
const tb = typeOrder[b.type] ?? 99;
|
|
4683
4780
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
4684
4781
|
});
|
|
4685
|
-
const indexPath =
|
|
4782
|
+
const indexPath = join28(projectDir, "knowledge.md");
|
|
4686
4783
|
let existing = false;
|
|
4687
4784
|
let stale = false;
|
|
4688
4785
|
try {
|
|
4689
|
-
const existingText = await
|
|
4786
|
+
const existingText = await readFile19(indexPath, "utf8");
|
|
4690
4787
|
existing = true;
|
|
4691
4788
|
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
4692
4789
|
const existingPages = new Set(existingEntries.map((l) => {
|
|
@@ -4726,8 +4823,8 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
4726
4823
|
}
|
|
4727
4824
|
if (input.apply) {
|
|
4728
4825
|
try {
|
|
4729
|
-
await mkdir9(
|
|
4730
|
-
await
|
|
4826
|
+
await mkdir9(dirname10(indexPath), { recursive: true });
|
|
4827
|
+
await writeFile10(indexPath, body, "utf8");
|
|
4731
4828
|
} catch (e) {
|
|
4732
4829
|
return {
|
|
4733
4830
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -4755,10 +4852,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
4755
4852
|
}
|
|
4756
4853
|
|
|
4757
4854
|
// src/commands/compound.ts
|
|
4758
|
-
import { writeFile as
|
|
4759
|
-
import { join as
|
|
4855
|
+
import { writeFile as writeFile11, mkdir as mkdir10, readdir as readdir7, unlink as unlink3 } from "fs/promises";
|
|
4856
|
+
import { join as join29 } from "path";
|
|
4760
4857
|
import { existsSync as existsSync8 } from "fs";
|
|
4761
|
-
import { readFile as
|
|
4858
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
4762
4859
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
4763
4860
|
var FIELD_RE = {
|
|
4764
4861
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -4856,17 +4953,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
4856
4953
|
};
|
|
4857
4954
|
}
|
|
4858
4955
|
async function runCompound(input) {
|
|
4859
|
-
const logPath =
|
|
4956
|
+
const logPath = join29(input.vault, "log.md");
|
|
4860
4957
|
let logText;
|
|
4861
4958
|
try {
|
|
4862
|
-
logText = await
|
|
4959
|
+
logText = await readFile20(logPath, "utf8");
|
|
4863
4960
|
} catch {
|
|
4864
4961
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
4865
4962
|
}
|
|
4866
4963
|
const entries = parseRetroEntries(logText);
|
|
4867
4964
|
const promoted = [];
|
|
4868
4965
|
const skipped = [];
|
|
4869
|
-
const compoundDir =
|
|
4966
|
+
const compoundDir = join29(input.vault, "projects", input.project, "compound");
|
|
4870
4967
|
for (const entry of entries) {
|
|
4871
4968
|
const generalizeValue = entry.generalize.trim();
|
|
4872
4969
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -4874,7 +4971,7 @@ async function runCompound(input) {
|
|
|
4874
4971
|
continue;
|
|
4875
4972
|
}
|
|
4876
4973
|
const slug = slugify(entry.cycleName);
|
|
4877
|
-
const compoundPath =
|
|
4974
|
+
const compoundPath = join29(compoundDir, `${slug}.md`);
|
|
4878
4975
|
if (existsSync8(compoundPath)) {
|
|
4879
4976
|
skipped.push(entry.date);
|
|
4880
4977
|
continue;
|
|
@@ -4916,7 +5013,7 @@ async function runCompound(input) {
|
|
|
4916
5013
|
if (!existsSync8(compoundDir)) {
|
|
4917
5014
|
await mkdir10(compoundDir, { recursive: true });
|
|
4918
5015
|
}
|
|
4919
|
-
await
|
|
5016
|
+
await writeFile11(compoundPath, content, "utf8");
|
|
4920
5017
|
}
|
|
4921
5018
|
promoted.push(`${slug}.md`);
|
|
4922
5019
|
}
|
|
@@ -4935,7 +5032,7 @@ async function runCompound(input) {
|
|
|
4935
5032
|
};
|
|
4936
5033
|
}
|
|
4937
5034
|
async function runCompoundDelete(input) {
|
|
4938
|
-
const projectDir =
|
|
5035
|
+
const projectDir = join29(input.vault, "projects", input.project);
|
|
4939
5036
|
if (!existsSync8(projectDir)) {
|
|
4940
5037
|
return {
|
|
4941
5038
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
@@ -4943,7 +5040,7 @@ async function runCompoundDelete(input) {
|
|
|
4943
5040
|
};
|
|
4944
5041
|
}
|
|
4945
5042
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
4946
|
-
const compoundPath =
|
|
5043
|
+
const compoundPath = join29(projectDir, "compound", `${entryName}.md`);
|
|
4947
5044
|
if (!existsSync8(compoundPath)) {
|
|
4948
5045
|
return {
|
|
4949
5046
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -4951,7 +5048,7 @@ async function runCompoundDelete(input) {
|
|
|
4951
5048
|
};
|
|
4952
5049
|
}
|
|
4953
5050
|
try {
|
|
4954
|
-
await
|
|
5051
|
+
await unlink3(compoundPath);
|
|
4955
5052
|
} catch (e) {
|
|
4956
5053
|
return {
|
|
4957
5054
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -4977,7 +5074,7 @@ knowledge.md regenerated`
|
|
|
4977
5074
|
};
|
|
4978
5075
|
}
|
|
4979
5076
|
async function runCompoundList(input) {
|
|
4980
|
-
const compoundDir =
|
|
5077
|
+
const compoundDir = join29(input.vault, "projects", input.project, "compound");
|
|
4981
5078
|
if (!existsSync8(compoundDir)) {
|
|
4982
5079
|
return {
|
|
4983
5080
|
exitCode: ExitCode.OK,
|
|
@@ -5008,10 +5105,10 @@ could not read compound directory`
|
|
|
5008
5105
|
const entries = [];
|
|
5009
5106
|
for (const dirent of dirents) {
|
|
5010
5107
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
5011
|
-
const filePath =
|
|
5108
|
+
const filePath = join29(compoundDir, dirent.name);
|
|
5012
5109
|
let text;
|
|
5013
5110
|
try {
|
|
5014
|
-
text = await
|
|
5111
|
+
text = await readFile20(filePath, "utf8");
|
|
5015
5112
|
} catch {
|
|
5016
5113
|
continue;
|
|
5017
5114
|
}
|
|
@@ -5040,9 +5137,9 @@ no compound entries found`;
|
|
|
5040
5137
|
}
|
|
5041
5138
|
|
|
5042
5139
|
// src/commands/observe.ts
|
|
5043
|
-
import { mkdir as mkdir11, writeFile as
|
|
5140
|
+
import { mkdir as mkdir11, writeFile as writeFile12 } from "fs/promises";
|
|
5044
5141
|
import { existsSync as existsSync9, statSync as statSync3 } from "fs";
|
|
5045
|
-
import { join as
|
|
5142
|
+
import { join as join30 } from "path";
|
|
5046
5143
|
import { createHash as createHash4 } from "crypto";
|
|
5047
5144
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
5048
5145
|
function slugify2(text) {
|
|
@@ -5071,7 +5168,7 @@ async function runObserve(input) {
|
|
|
5071
5168
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
5072
5169
|
};
|
|
5073
5170
|
}
|
|
5074
|
-
const transcriptsDir =
|
|
5171
|
+
const transcriptsDir = join30(input.vault, "raw", "transcripts");
|
|
5075
5172
|
try {
|
|
5076
5173
|
await mkdir11(transcriptsDir, { recursive: true });
|
|
5077
5174
|
} catch {
|
|
@@ -5083,7 +5180,7 @@ async function runObserve(input) {
|
|
|
5083
5180
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5084
5181
|
const slug = slugify2(input.text);
|
|
5085
5182
|
const fileName = `${today}-observation-${slug}.md`;
|
|
5086
|
-
const filePath =
|
|
5183
|
+
const filePath = join30(transcriptsDir, fileName);
|
|
5087
5184
|
const body = `
|
|
5088
5185
|
${input.text.trim()}
|
|
5089
5186
|
`;
|
|
@@ -5101,7 +5198,7 @@ ${input.text.trim()}
|
|
|
5101
5198
|
frontmatterLines.push("---");
|
|
5102
5199
|
const content = frontmatterLines.join("\n") + body;
|
|
5103
5200
|
try {
|
|
5104
|
-
await
|
|
5201
|
+
await writeFile12(filePath, content, "utf8");
|
|
5105
5202
|
} catch (e) {
|
|
5106
5203
|
return {
|
|
5107
5204
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -5123,8 +5220,8 @@ ${input.text.trim()}
|
|
|
5123
5220
|
}
|
|
5124
5221
|
|
|
5125
5222
|
// src/commands/ingest.ts
|
|
5126
|
-
import { readFile as
|
|
5127
|
-
import { join as
|
|
5223
|
+
import { readFile as readFile21, writeFile as writeFile13, mkdir as mkdir12 } from "fs/promises";
|
|
5224
|
+
import { join as join31 } from "path";
|
|
5128
5225
|
import { createHash as createHash5 } from "crypto";
|
|
5129
5226
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
5130
5227
|
var TYPE_DIR = {
|
|
@@ -5283,7 +5380,7 @@ async function runIngest(input) {
|
|
|
5283
5380
|
sourceContent = fetchResult.data.body;
|
|
5284
5381
|
} else {
|
|
5285
5382
|
try {
|
|
5286
|
-
sourceContent = await
|
|
5383
|
+
sourceContent = await readFile21(input.source, "utf8");
|
|
5287
5384
|
} catch {
|
|
5288
5385
|
return {
|
|
5289
5386
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -5298,8 +5395,8 @@ async function runIngest(input) {
|
|
|
5298
5395
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
5299
5396
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
5300
5397
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
5301
|
-
const rawAbsPath =
|
|
5302
|
-
const typedAbsPath =
|
|
5398
|
+
const rawAbsPath = join31(input.vault, rawRelPath);
|
|
5399
|
+
const typedAbsPath = join31(input.vault, typedRelPath);
|
|
5303
5400
|
const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
|
|
5304
5401
|
const typedContent = buildTypedContent(
|
|
5305
5402
|
input.title,
|
|
@@ -5362,8 +5459,8 @@ async function runIngest(input) {
|
|
|
5362
5459
|
};
|
|
5363
5460
|
}
|
|
5364
5461
|
try {
|
|
5365
|
-
await mkdir12(
|
|
5366
|
-
await
|
|
5462
|
+
await mkdir12(join31(input.vault, "raw", "articles"), { recursive: true });
|
|
5463
|
+
await writeFile13(rawAbsPath, rawContent, "utf8");
|
|
5367
5464
|
} catch (e) {
|
|
5368
5465
|
return {
|
|
5369
5466
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -5371,8 +5468,8 @@ async function runIngest(input) {
|
|
|
5371
5468
|
};
|
|
5372
5469
|
}
|
|
5373
5470
|
try {
|
|
5374
|
-
await mkdir12(
|
|
5375
|
-
await
|
|
5471
|
+
await mkdir12(join31(input.vault, typedDir), { recursive: true });
|
|
5472
|
+
await writeFile13(typedAbsPath, typedContent, "utf8");
|
|
5376
5473
|
} catch (e) {
|
|
5377
5474
|
return {
|
|
5378
5475
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -5403,7 +5500,6 @@ async function runIngest(input) {
|
|
|
5403
5500
|
}
|
|
5404
5501
|
|
|
5405
5502
|
// src/commands/tag-sync.ts
|
|
5406
|
-
import { writeFile as writeFile17 } from "fs/promises";
|
|
5407
5503
|
var ENUM_MIRRORS = {
|
|
5408
5504
|
provenance: ["research", "project", "mixed"],
|
|
5409
5505
|
confidence: ["high", "medium", "low"]
|
|
@@ -5518,7 +5614,11 @@ ${newFm}
|
|
|
5518
5614
|
---
|
|
5519
5615
|
${body}`;
|
|
5520
5616
|
if (!input.dryRun) {
|
|
5521
|
-
await
|
|
5617
|
+
const w = await safeWritePage(page.absPath, newText);
|
|
5618
|
+
if (!w.ok) {
|
|
5619
|
+
unchanged++;
|
|
5620
|
+
continue;
|
|
5621
|
+
}
|
|
5522
5622
|
}
|
|
5523
5623
|
synced.push(page.relPath);
|
|
5524
5624
|
}
|
|
@@ -5548,10 +5648,10 @@ ${body}`;
|
|
|
5548
5648
|
|
|
5549
5649
|
// src/commands/sync.ts
|
|
5550
5650
|
import { existsSync as existsSync10 } from "fs";
|
|
5551
|
-
import { join as
|
|
5651
|
+
import { join as join32 } from "path";
|
|
5552
5652
|
function runSyncStatus(input) {
|
|
5553
5653
|
const vault = input.vault;
|
|
5554
|
-
if (!existsSync10(
|
|
5654
|
+
if (!existsSync10(join32(vault, ".git"))) {
|
|
5555
5655
|
return {
|
|
5556
5656
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
5557
5657
|
result: ok({
|
|
@@ -5620,7 +5720,7 @@ function runSyncStatus(input) {
|
|
|
5620
5720
|
}
|
|
5621
5721
|
async function runSyncPush(input) {
|
|
5622
5722
|
const vault = input.vault;
|
|
5623
|
-
if (!existsSync10(
|
|
5723
|
+
if (!existsSync10(join32(vault, ".git"))) {
|
|
5624
5724
|
return {
|
|
5625
5725
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
5626
5726
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -5705,7 +5805,7 @@ async function runSyncPush(input) {
|
|
|
5705
5805
|
}
|
|
5706
5806
|
async function runSyncPull(input) {
|
|
5707
5807
|
const vault = input.vault;
|
|
5708
|
-
if (!existsSync10(
|
|
5808
|
+
if (!existsSync10(join32(vault, ".git"))) {
|
|
5709
5809
|
return {
|
|
5710
5810
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
5711
5811
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -5781,7 +5881,7 @@ async function runSyncPull(input) {
|
|
|
5781
5881
|
|
|
5782
5882
|
// src/commands/backup.ts
|
|
5783
5883
|
import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync9, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
5784
|
-
import { join as
|
|
5884
|
+
import { join as join33, relative as relative3, dirname as dirname11 } from "path";
|
|
5785
5885
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
5786
5886
|
|
|
5787
5887
|
// src/utils/s3-client.ts
|
|
@@ -5805,7 +5905,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
5805
5905
|
function* walkMarkdown(dir, base) {
|
|
5806
5906
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
5807
5907
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
5808
|
-
const full =
|
|
5908
|
+
const full = join33(dir, entry.name);
|
|
5809
5909
|
if (entry.isDirectory()) {
|
|
5810
5910
|
yield* walkMarkdown(full, base);
|
|
5811
5911
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -5828,7 +5928,7 @@ async function runBackupSync(input) {
|
|
|
5828
5928
|
let failed = 0;
|
|
5829
5929
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
5830
5930
|
for (const relPath of files) {
|
|
5831
|
-
const absPath =
|
|
5931
|
+
const absPath = join33(input.vault, relPath);
|
|
5832
5932
|
const localStat = statSync4(absPath);
|
|
5833
5933
|
let needsUpload = true;
|
|
5834
5934
|
try {
|
|
@@ -5904,7 +6004,7 @@ async function runBackupRestore(input) {
|
|
|
5904
6004
|
const objects = list.Contents ?? [];
|
|
5905
6005
|
for (const obj of objects) {
|
|
5906
6006
|
if (!obj.Key) continue;
|
|
5907
|
-
const localPath =
|
|
6007
|
+
const localPath = join33(target, obj.Key);
|
|
5908
6008
|
try {
|
|
5909
6009
|
const localStat = statSync4(localPath);
|
|
5910
6010
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -5917,7 +6017,7 @@ async function runBackupRestore(input) {
|
|
|
5917
6017
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
5918
6018
|
const body = await resp.Body?.transformToByteArray();
|
|
5919
6019
|
if (body) {
|
|
5920
|
-
mkdirSync3(
|
|
6020
|
+
mkdirSync3(dirname11(localPath), { recursive: true });
|
|
5921
6021
|
writeFileSync4(localPath, Buffer.from(body));
|
|
5922
6022
|
downloaded++;
|
|
5923
6023
|
}
|
|
@@ -5951,8 +6051,8 @@ async function runBackupRestore(input) {
|
|
|
5951
6051
|
|
|
5952
6052
|
// src/commands/status.ts
|
|
5953
6053
|
import { existsSync as existsSync11, statSync as statSync5 } from "fs";
|
|
5954
|
-
import { readFile as
|
|
5955
|
-
import { join as
|
|
6054
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
6055
|
+
import { join as join34 } from "path";
|
|
5956
6056
|
async function runStatus(input) {
|
|
5957
6057
|
if (!existsSync11(input.vault)) {
|
|
5958
6058
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
@@ -5979,7 +6079,7 @@ async function runStatus(input) {
|
|
|
5979
6079
|
const compound = scan.data.compound.length;
|
|
5980
6080
|
let schemaVersion = "v1";
|
|
5981
6081
|
try {
|
|
5982
|
-
const schemaContent = await
|
|
6082
|
+
const schemaContent = await readFile22(join34(input.vault, "SCHEMA.md"), "utf8");
|
|
5983
6083
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
5984
6084
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
5985
6085
|
} catch {
|
|
@@ -6039,8 +6139,8 @@ async function runStatus(input) {
|
|
|
6039
6139
|
}
|
|
6040
6140
|
|
|
6041
6141
|
// src/commands/seed.ts
|
|
6042
|
-
import { mkdir as mkdir13, writeFile as
|
|
6043
|
-
import { join as
|
|
6142
|
+
import { mkdir as mkdir13, writeFile as writeFile14, stat as stat7 } from "fs/promises";
|
|
6143
|
+
import { join as join35 } from "path";
|
|
6044
6144
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6045
6145
|
var EXAMPLE_PAGES = {
|
|
6046
6146
|
"entities/example-project.md": `---
|
|
@@ -6109,30 +6209,30 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
6109
6209
|
`;
|
|
6110
6210
|
async function runSeed(input) {
|
|
6111
6211
|
try {
|
|
6112
|
-
await stat7(
|
|
6212
|
+
await stat7(join35(input.vault, "SCHEMA.md"));
|
|
6113
6213
|
} catch {
|
|
6114
6214
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
6115
6215
|
}
|
|
6116
6216
|
const created = [];
|
|
6117
6217
|
const skipped = [];
|
|
6118
6218
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
6119
|
-
const absPath =
|
|
6219
|
+
const absPath = join35(input.vault, relPath);
|
|
6120
6220
|
try {
|
|
6121
6221
|
await stat7(absPath);
|
|
6122
6222
|
skipped.push(relPath);
|
|
6123
6223
|
} catch {
|
|
6124
|
-
await mkdir13(
|
|
6125
|
-
await
|
|
6224
|
+
await mkdir13(join35(absPath, ".."), { recursive: true });
|
|
6225
|
+
await writeFile14(absPath, content, "utf8");
|
|
6126
6226
|
created.push(relPath);
|
|
6127
6227
|
}
|
|
6128
6228
|
}
|
|
6129
|
-
const rawPath =
|
|
6229
|
+
const rawPath = join35(input.vault, "raw", "articles", "example-source.md");
|
|
6130
6230
|
try {
|
|
6131
6231
|
await stat7(rawPath);
|
|
6132
6232
|
skipped.push("raw/articles/example-source.md");
|
|
6133
6233
|
} catch {
|
|
6134
|
-
await mkdir13(
|
|
6135
|
-
await
|
|
6234
|
+
await mkdir13(join35(rawPath, ".."), { recursive: true });
|
|
6235
|
+
await writeFile14(rawPath, EXAMPLE_RAW, "utf8");
|
|
6136
6236
|
created.push("raw/articles/example-source.md");
|
|
6137
6237
|
}
|
|
6138
6238
|
if (created.length > 0) {
|
|
@@ -6154,9 +6254,9 @@ async function runSeed(input) {
|
|
|
6154
6254
|
}
|
|
6155
6255
|
|
|
6156
6256
|
// src/commands/canvas.ts
|
|
6157
|
-
import { readFile as
|
|
6257
|
+
import { readFile as readFile23, writeFile as writeFile15 } from "fs/promises";
|
|
6158
6258
|
import { existsSync as existsSync12 } from "fs";
|
|
6159
|
-
import { join as
|
|
6259
|
+
import { join as join36 } from "path";
|
|
6160
6260
|
var NODE_WIDTH = 240;
|
|
6161
6261
|
var NODE_HEIGHT = 60;
|
|
6162
6262
|
var COLUMN_SPACING = 400;
|
|
@@ -6234,7 +6334,7 @@ function buildCanvasEdges(adjacency) {
|
|
|
6234
6334
|
return edges;
|
|
6235
6335
|
}
|
|
6236
6336
|
async function runCanvasGenerate(input) {
|
|
6237
|
-
const graphPath = input.graphPath ??
|
|
6337
|
+
const graphPath = input.graphPath ?? join36(input.vault, ".skillwiki", "graph.json");
|
|
6238
6338
|
if (!existsSync12(graphPath)) {
|
|
6239
6339
|
return {
|
|
6240
6340
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6246,7 +6346,7 @@ async function runCanvasGenerate(input) {
|
|
|
6246
6346
|
}
|
|
6247
6347
|
let raw;
|
|
6248
6348
|
try {
|
|
6249
|
-
raw = await
|
|
6349
|
+
raw = await readFile23(graphPath, "utf8");
|
|
6250
6350
|
} catch (e) {
|
|
6251
6351
|
return {
|
|
6252
6352
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6272,9 +6372,9 @@ async function runCanvasGenerate(input) {
|
|
|
6272
6372
|
const nodes = buildCanvasNodes(paths);
|
|
6273
6373
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
6274
6374
|
const canvas = { nodes, edges };
|
|
6275
|
-
const outPath =
|
|
6375
|
+
const outPath = join36(input.vault, "vault-graph.canvas");
|
|
6276
6376
|
try {
|
|
6277
|
-
await
|
|
6377
|
+
await writeFile15(outPath, JSON.stringify(canvas, null, 2));
|
|
6278
6378
|
} catch (e) {
|
|
6279
6379
|
return {
|
|
6280
6380
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6294,8 +6394,8 @@ written: ${outPath}`
|
|
|
6294
6394
|
}
|
|
6295
6395
|
|
|
6296
6396
|
// src/commands/query.ts
|
|
6297
|
-
import { readFile as
|
|
6298
|
-
import { join as
|
|
6397
|
+
import { readFile as readFile24, stat as stat8 } from "fs/promises";
|
|
6398
|
+
import { join as join37 } from "path";
|
|
6299
6399
|
var W_KEYWORD = 2;
|
|
6300
6400
|
var W_SOURCE_OVERLAP = 4;
|
|
6301
6401
|
var W_WIKILINK = 3;
|
|
@@ -6416,7 +6516,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
6416
6516
|
return score;
|
|
6417
6517
|
}
|
|
6418
6518
|
async function loadOrBuildGraph(vault) {
|
|
6419
|
-
const graphPath =
|
|
6519
|
+
const graphPath = join37(vault, ".skillwiki", "graph.json");
|
|
6420
6520
|
let needsBuild = false;
|
|
6421
6521
|
try {
|
|
6422
6522
|
const fileStat = await stat8(graphPath);
|
|
@@ -6430,7 +6530,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
6430
6530
|
if (buildResult.exitCode !== 0) return null;
|
|
6431
6531
|
}
|
|
6432
6532
|
try {
|
|
6433
|
-
const raw = await
|
|
6533
|
+
const raw = await readFile24(graphPath, "utf8");
|
|
6434
6534
|
return JSON.parse(raw);
|
|
6435
6535
|
} catch {
|
|
6436
6536
|
return null;
|
|
@@ -6439,13 +6539,13 @@ async function loadOrBuildGraph(vault) {
|
|
|
6439
6539
|
|
|
6440
6540
|
// src/utils/auto-commit.ts
|
|
6441
6541
|
import { existsSync as existsSync13 } from "fs";
|
|
6442
|
-
import { join as
|
|
6542
|
+
import { join as join38 } from "path";
|
|
6443
6543
|
async function postCommit(vault, exitCode) {
|
|
6444
6544
|
if (exitCode !== 0) return;
|
|
6445
6545
|
const home = process.env.HOME ?? "";
|
|
6446
6546
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
6447
6547
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
6448
|
-
if (!existsSync13(
|
|
6548
|
+
if (!existsSync13(join38(vault, ".git"))) return;
|
|
6449
6549
|
const lastOps = readLastOp(vault);
|
|
6450
6550
|
if (lastOps.length === 0) return;
|
|
6451
6551
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -6496,7 +6596,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
6496
6596
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
6497
6597
|
});
|
|
6498
6598
|
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
6499
|
-
const out = opts.out ??
|
|
6599
|
+
const out = opts.out ?? join39(vault, ".skillwiki", "graph.json");
|
|
6500
6600
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
6501
6601
|
});
|
|
6502
6602
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|