skillwiki 0.8.1-beta.8 → 0.8.1-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +361 -254
- 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 readFileSync12 } from "fs";
|
|
12
|
-
import { join as
|
|
12
|
+
import { join as join44 } from "path";
|
|
13
13
|
import { Command as Command2 } from "commander";
|
|
14
14
|
|
|
15
15
|
// ../shared/src/exit-codes.ts
|
|
@@ -500,6 +500,7 @@ import { dirname } from "path";
|
|
|
500
500
|
import { readFile as readFile3, readdir, stat } from "fs/promises";
|
|
501
501
|
import { join as join3, relative as relative2, sep as sep2 } from "path";
|
|
502
502
|
var TYPED_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
503
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
503
504
|
async function scanVault(root) {
|
|
504
505
|
try {
|
|
505
506
|
await stat(join3(root, "SCHEMA.md"));
|
|
@@ -510,6 +511,7 @@ async function scanVault(root) {
|
|
|
510
511
|
const rels = all.map((p) => ({ absPath: p, relPath: relative2(root, p).split(sep2).join("/") }));
|
|
511
512
|
return ok({
|
|
512
513
|
root,
|
|
514
|
+
allMarkdown: rels,
|
|
513
515
|
typedKnowledge: rels.filter((p) => TYPED_DIRS.some((d) => p.relPath.startsWith(d + "/"))),
|
|
514
516
|
raw: rels.filter((p) => p.relPath.startsWith("raw/")),
|
|
515
517
|
workItems: rels.filter((p) => /^projects\/[^/]+\/work\/[^/]+\/(spec|plan|log)\.md$/.test(p.relPath)),
|
|
@@ -521,8 +523,10 @@ async function walk(dir) {
|
|
|
521
523
|
const out = [];
|
|
522
524
|
for (const e of entries) {
|
|
523
525
|
const p = join3(dir, e.name);
|
|
524
|
-
if (e.isDirectory())
|
|
525
|
-
|
|
526
|
+
if (e.isDirectory()) {
|
|
527
|
+
if (SKIP_DIRS.has(e.name)) continue;
|
|
528
|
+
out.push(...await walk(p));
|
|
529
|
+
} else if (e.isFile() && e.name.endsWith(".md")) out.push(p);
|
|
526
530
|
}
|
|
527
531
|
return out;
|
|
528
532
|
}
|
|
@@ -2579,9 +2583,9 @@ ${content}
|
|
|
2579
2583
|
}
|
|
2580
2584
|
|
|
2581
2585
|
// src/commands/lint.ts
|
|
2582
|
-
import { existsSync as
|
|
2583
|
-
import { readFile as
|
|
2584
|
-
import { join as
|
|
2586
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2587
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
2588
|
+
import { join as join22 } from "path";
|
|
2585
2589
|
|
|
2586
2590
|
// src/commands/sparse-community.ts
|
|
2587
2591
|
async function runSparseCommunity(input) {
|
|
@@ -2837,28 +2841,91 @@ async function runRawBodyDedup(vault) {
|
|
|
2837
2841
|
}
|
|
2838
2842
|
|
|
2839
2843
|
// src/commands/path-too-long.ts
|
|
2844
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2845
|
+
import { mkdir as mkdir8, readFile as readFile16, rename as rename6, unlink as unlink3 } from "fs/promises";
|
|
2846
|
+
import { dirname as dirname8, join as join21, posix } from "path";
|
|
2840
2847
|
var MAX_PATH_LENGTH = 240;
|
|
2841
2848
|
async function runPathTooLong(input) {
|
|
2842
2849
|
const scan = await scanVault(input.vault);
|
|
2843
2850
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2844
|
-
const
|
|
2845
|
-
const violations = [];
|
|
2846
|
-
for (const page of allPages) {
|
|
2847
|
-
if (page.relPath.length > MAX_PATH_LENGTH) {
|
|
2848
|
-
violations.push({ relPath: page.relPath, length: page.relPath.length });
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2851
|
+
const violations = findPathTooLongViolations(scan.data.allMarkdown);
|
|
2851
2852
|
if (violations.length > 0) {
|
|
2852
2853
|
return {
|
|
2853
2854
|
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
2854
2855
|
result: ok({
|
|
2855
2856
|
violations,
|
|
2856
|
-
humanHint: violations.map((v) => `${v.relPath}: ${v.length} chars (max ${MAX_PATH_LENGTH})`).join("\n")
|
|
2857
|
+
humanHint: violations.map((v) => `${v.relPath}: ${v.length} chars (max ${MAX_PATH_LENGTH}) -> ${v.suggestedRelPath}`).join("\n")
|
|
2857
2858
|
})
|
|
2858
2859
|
};
|
|
2859
2860
|
}
|
|
2860
2861
|
return { exitCode: ExitCode.OK, result: ok({ violations, humanHint: "all paths within length limit" }) };
|
|
2861
2862
|
}
|
|
2863
|
+
async function fixPathTooLong(input) {
|
|
2864
|
+
const scan = await scanVault(input.vault);
|
|
2865
|
+
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2866
|
+
const violations = findPathTooLongViolations(scan.data.allMarkdown);
|
|
2867
|
+
const fixed = [];
|
|
2868
|
+
const unresolved = [];
|
|
2869
|
+
for (const violation of violations) {
|
|
2870
|
+
const target = await resolveFixTarget(input.vault, violation.relPath, violation.suggestedRelPath);
|
|
2871
|
+
if (!target || target.relPath === violation.relPath || target.relPath.length > MAX_PATH_LENGTH) {
|
|
2872
|
+
unresolved.push(violation.relPath);
|
|
2873
|
+
continue;
|
|
2874
|
+
}
|
|
2875
|
+
try {
|
|
2876
|
+
if (target.mode === "dedupe") {
|
|
2877
|
+
await unlink3(join21(input.vault, violation.relPath));
|
|
2878
|
+
} else {
|
|
2879
|
+
await mkdir8(dirname8(join21(input.vault, target.relPath)), { recursive: true });
|
|
2880
|
+
await rename6(join21(input.vault, violation.relPath), join21(input.vault, target.relPath));
|
|
2881
|
+
}
|
|
2882
|
+
fixed.push({ from: violation.relPath, to: target.relPath });
|
|
2883
|
+
} catch {
|
|
2884
|
+
unresolved.push(violation.relPath);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
const rewired = [];
|
|
2888
|
+
if (fixed.length > 0) {
|
|
2889
|
+
const afterScan = await scanVault(input.vault);
|
|
2890
|
+
if (afterScan.ok) {
|
|
2891
|
+
for (const page of afterScan.data.allMarkdown) {
|
|
2892
|
+
if (!shouldRewriteReferences(page.relPath)) continue;
|
|
2893
|
+
try {
|
|
2894
|
+
const original = await readFile16(page.absPath, "utf8");
|
|
2895
|
+
let updated = original;
|
|
2896
|
+
for (const fix of fixed) {
|
|
2897
|
+
updated = replacePathReferences(updated, fix.from, fix.to);
|
|
2898
|
+
}
|
|
2899
|
+
if (updated !== original) {
|
|
2900
|
+
const write = await safeWritePage(page.absPath, updated);
|
|
2901
|
+
if (write.ok) rewired.push(page.relPath);
|
|
2902
|
+
else unresolved.push(`${page.relPath} (rewire)`);
|
|
2903
|
+
}
|
|
2904
|
+
} catch {
|
|
2905
|
+
unresolved.push(`${page.relPath} (rewire)`);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
const hintLines = [
|
|
2911
|
+
`fixed: ${fixed.length}`,
|
|
2912
|
+
`rewired: ${rewired.length}`,
|
|
2913
|
+
`unresolved: ${unresolved.length}`
|
|
2914
|
+
];
|
|
2915
|
+
for (const f of fixed) hintLines.push(` ${f.from} -> ${f.to}`);
|
|
2916
|
+
for (const u of unresolved) hintLines.push(` unresolved: ${u}`);
|
|
2917
|
+
return {
|
|
2918
|
+
exitCode: unresolved.length > 0 ? ExitCode.LINT_HAS_ERRORS : ExitCode.OK,
|
|
2919
|
+
result: ok({ fixed, unresolved, rewired, humanHint: hintLines.join("\n") })
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
function findPathTooLongViolations(pages) {
|
|
2923
|
+
return pages.filter((page) => page.relPath.length > MAX_PATH_LENGTH).map((page) => ({
|
|
2924
|
+
relPath: page.relPath,
|
|
2925
|
+
length: page.relPath.length,
|
|
2926
|
+
suggestedRelPath: truncateFilename(page.relPath)
|
|
2927
|
+
}));
|
|
2928
|
+
}
|
|
2862
2929
|
function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
2863
2930
|
if (relPath.length <= maxLength) return relPath;
|
|
2864
2931
|
const lastSlash = relPath.lastIndexOf("/");
|
|
@@ -2872,15 +2939,62 @@ function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
|
2872
2939
|
const maxPrefixLen = maxLength - dirPrefix.length - suffix.length;
|
|
2873
2940
|
if (maxPrefixLen <= 0) {
|
|
2874
2941
|
const fallback = dirPrefix + hash + ext;
|
|
2875
|
-
|
|
2876
|
-
const dirBudget = maxLength - suffix.length;
|
|
2877
|
-
return dirPrefix.slice(0, Math.max(0, dirBudget)) + suffix;
|
|
2878
|
-
}
|
|
2879
|
-
return fallback;
|
|
2942
|
+
return fallback.length <= maxLength ? fallback : relPath;
|
|
2880
2943
|
}
|
|
2881
2944
|
const prefix = base.slice(0, maxPrefixLen).replace(/[-_\s]+$/, "");
|
|
2882
2945
|
return dirPrefix + prefix + suffix;
|
|
2883
2946
|
}
|
|
2947
|
+
async function resolveFixTarget(vault, original, preferred) {
|
|
2948
|
+
for (const candidate of candidateRelPaths(preferred)) {
|
|
2949
|
+
if (candidate === original || candidate.length > MAX_PATH_LENGTH) continue;
|
|
2950
|
+
const candidatePath = join21(vault, candidate);
|
|
2951
|
+
if (!existsSync4(candidatePath)) return { relPath: candidate, mode: "rename" };
|
|
2952
|
+
if (await hasSameContent(join21(vault, original), candidatePath)) {
|
|
2953
|
+
return { relPath: candidate, mode: "dedupe" };
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
return null;
|
|
2957
|
+
}
|
|
2958
|
+
function candidateRelPaths(preferred) {
|
|
2959
|
+
const candidates = [preferred];
|
|
2960
|
+
if (preferred.length > MAX_PATH_LENGTH) return candidates;
|
|
2961
|
+
const dir = posix.dirname(preferred) === "." ? "" : posix.dirname(preferred);
|
|
2962
|
+
const filename = posix.basename(preferred);
|
|
2963
|
+
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
2964
|
+
const base = ext ? filename.slice(0, -3) : filename;
|
|
2965
|
+
const dirPrefix = dir ? `${dir}/` : "";
|
|
2966
|
+
for (let i = 2; i < 100; i++) {
|
|
2967
|
+
const suffix = `-${i}${ext}`;
|
|
2968
|
+
const prefixBudget = MAX_PATH_LENGTH - dirPrefix.length - suffix.length;
|
|
2969
|
+
if (prefixBudget <= 0) break;
|
|
2970
|
+
candidates.push(`${dirPrefix}${base.slice(0, prefixBudget).replace(/[-_\s]+$/, "")}${suffix}`);
|
|
2971
|
+
}
|
|
2972
|
+
return candidates;
|
|
2973
|
+
}
|
|
2974
|
+
async function hasSameContent(a, b) {
|
|
2975
|
+
try {
|
|
2976
|
+
const [left, right] = await Promise.all([readFile16(a), readFile16(b)]);
|
|
2977
|
+
return left.equals(right);
|
|
2978
|
+
} catch {
|
|
2979
|
+
return false;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
function shouldRewriteReferences(relPath) {
|
|
2983
|
+
if (relPath.startsWith("raw/")) return false;
|
|
2984
|
+
if (relPath.startsWith("_archive/")) return false;
|
|
2985
|
+
return true;
|
|
2986
|
+
}
|
|
2987
|
+
function replacePathReferences(content, oldRelPath, newRelPath) {
|
|
2988
|
+
let updated = content.replaceAll(oldRelPath, newRelPath);
|
|
2989
|
+
const oldStem = posix.basename(oldRelPath).replace(/\.md$/, "");
|
|
2990
|
+
const newStem = posix.basename(newRelPath).replace(/\.md$/, "");
|
|
2991
|
+
if (oldStem !== newStem) {
|
|
2992
|
+
const oldStemEscaped = oldStem.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2993
|
+
const stemWikilinkRe = new RegExp(`\\[\\[${oldStemEscaped}(\\|[^\\]]*)?\\]\\]`, "g");
|
|
2994
|
+
updated = updated.replace(stemWikilinkRe, (_match, alias) => `[[${newStem}${alias ?? ""}]]`);
|
|
2995
|
+
}
|
|
2996
|
+
return updated;
|
|
2997
|
+
}
|
|
2884
2998
|
function computeShortHash(input) {
|
|
2885
2999
|
let hash = 2166136261;
|
|
2886
3000
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -3069,7 +3183,15 @@ function extractSourceEntries(rawFm) {
|
|
|
3069
3183
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
|
|
3070
3184
|
var WARNING_ORDER = ["raw_body_duplicate", "raw_subdirectory_duplicate", "file_source_url", "index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "orphans", "compound_refs", "legacy_citation_style", "orphaned_citations", "duplicate_frontmatter", "work_item_health", "orphaned_project_pages", "missing_overview", "missing_diagram"];
|
|
3071
3185
|
var INFO_ORDER = ["bridges", "sparse_community", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
|
|
3186
|
+
var KNOWN_BUCKETS = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
|
|
3072
3187
|
async function runLint(input) {
|
|
3188
|
+
if (input.only && !KNOWN_BUCKETS.includes(input.only)) {
|
|
3189
|
+
return {
|
|
3190
|
+
exitCode: ExitCode.USAGE,
|
|
3191
|
+
result: { ok: false, error: "UNKNOWN_BUCKET", detail: `Unknown bucket "${input.only}". Valid: ${KNOWN_BUCKETS.join(", ")}` }
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
const shouldFix = (bucket) => !!input.fix && (!input.only || input.only === bucket);
|
|
3073
3195
|
const buckets = {};
|
|
3074
3196
|
const fixed = [];
|
|
3075
3197
|
const unresolved = [];
|
|
@@ -3193,7 +3315,7 @@ async function runLint(input) {
|
|
|
3193
3315
|
let rawPath = entry.replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
3194
3316
|
rawPath = rawPath.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
3195
3317
|
if (!rawPath.startsWith("raw/") && !rawPath.startsWith("_archive/raw/")) continue;
|
|
3196
|
-
if (!
|
|
3318
|
+
if (!existsSync5(join22(input.vault, rawPath)) && !existsSync5(join22(input.vault, rawPath + ".md")) && !rawPath.startsWith("_archive/") && !existsSync5(join22(input.vault, "_archive", rawPath)) && !existsSync5(join22(input.vault, "_archive", rawPath + ".md"))) {
|
|
3197
3319
|
brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
|
|
3198
3320
|
}
|
|
3199
3321
|
}
|
|
@@ -3284,11 +3406,11 @@ async function runLint(input) {
|
|
|
3284
3406
|
const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
|
|
3285
3407
|
if (!slugMatch) continue;
|
|
3286
3408
|
const slug = slugMatch[1];
|
|
3287
|
-
const knowledgePath =
|
|
3288
|
-
if (!
|
|
3409
|
+
const knowledgePath = join22(input.vault, "projects", slug, "knowledge.md");
|
|
3410
|
+
if (!existsSync5(knowledgePath)) continue;
|
|
3289
3411
|
const pageRef = page.relPath.replace(/\.md$/, "");
|
|
3290
3412
|
try {
|
|
3291
|
-
const knowledgeContent = await
|
|
3413
|
+
const knowledgeContent = await readFile17(knowledgePath, "utf8");
|
|
3292
3414
|
if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
|
|
3293
3415
|
orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
|
|
3294
3416
|
}
|
|
@@ -3329,13 +3451,13 @@ async function runLint(input) {
|
|
|
3329
3451
|
}
|
|
3330
3452
|
}
|
|
3331
3453
|
if (staleSectionFlags.length > 0) buckets.stale_sections = staleSectionFlags;
|
|
3332
|
-
if (
|
|
3454
|
+
if (shouldFix("legacy_citation_style") && legacyPages.length > 0) {
|
|
3333
3455
|
const FENCE_RE2 = /```[\s\S]*?```/g;
|
|
3334
3456
|
const INLINE_MARKER = /\^\[raw\/[^\]]+\]/g;
|
|
3335
3457
|
for (const relPath of legacyPages) {
|
|
3336
3458
|
try {
|
|
3337
3459
|
const absPath = `${input.vault}/${relPath}`;
|
|
3338
|
-
const raw = await
|
|
3460
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3339
3461
|
const split = splitFrontmatter(raw);
|
|
3340
3462
|
if (!split.ok) {
|
|
3341
3463
|
unresolved.push(relPath);
|
|
@@ -3430,11 +3552,11 @@ ${newBody}`;
|
|
|
3430
3552
|
else delete buckets.legacy_citation_style;
|
|
3431
3553
|
}
|
|
3432
3554
|
}
|
|
3433
|
-
if (
|
|
3555
|
+
if (shouldFix("missing_overview") && noOverview.length > 0) {
|
|
3434
3556
|
for (const relPath of noOverview) {
|
|
3435
3557
|
try {
|
|
3436
3558
|
const absPath = `${input.vault}/${relPath}`;
|
|
3437
|
-
const raw = await
|
|
3559
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3438
3560
|
const split = splitFrontmatter(raw);
|
|
3439
3561
|
if (!split.ok) {
|
|
3440
3562
|
unresolved.push(relPath);
|
|
@@ -3471,11 +3593,11 @@ ${trimmedBody}`;
|
|
|
3471
3593
|
if (remaining.length > 0) buckets.missing_overview = remaining;
|
|
3472
3594
|
else delete buckets.missing_overview;
|
|
3473
3595
|
}
|
|
3474
|
-
if (
|
|
3596
|
+
if (shouldFix("missing_tldr") && missingTldrFlags.length > 0) {
|
|
3475
3597
|
for (const relPath of missingTldrFlags) {
|
|
3476
3598
|
try {
|
|
3477
3599
|
const absPath = `${input.vault}/${relPath}`;
|
|
3478
|
-
const raw = await
|
|
3600
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3479
3601
|
const split = splitFrontmatter(raw);
|
|
3480
3602
|
if (!split.ok) {
|
|
3481
3603
|
unresolved.push(relPath);
|
|
@@ -3518,14 +3640,14 @@ ${lines.join("\n")}`;
|
|
|
3518
3640
|
if (remaining.length > 0) buckets.missing_tldr = remaining;
|
|
3519
3641
|
else delete buckets.missing_tldr;
|
|
3520
3642
|
}
|
|
3521
|
-
if (
|
|
3643
|
+
if (shouldFix("wikilink_citation") && wikilinkCitationFlags.length > 0) {
|
|
3522
3644
|
const WIKILINK_RE = /\[\[raw\/([^\]|]+)(?:\|[^\]]*)?\]\]/g;
|
|
3523
3645
|
const FENCE_RE2 = /```[\s\S]*?```/g;
|
|
3524
3646
|
const wikilinkFixed = [];
|
|
3525
3647
|
for (const relPath of wikilinkCitationFlags) {
|
|
3526
3648
|
try {
|
|
3527
3649
|
const absPath = `${input.vault}/${relPath}`;
|
|
3528
|
-
const raw = await
|
|
3650
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3529
3651
|
const split = splitFrontmatter(raw);
|
|
3530
3652
|
if (!split.ok) {
|
|
3531
3653
|
unresolved.push(relPath);
|
|
@@ -3607,12 +3729,12 @@ ${newBody}`;
|
|
|
3607
3729
|
else delete buckets.wikilink_citation;
|
|
3608
3730
|
}
|
|
3609
3731
|
}
|
|
3610
|
-
if (
|
|
3732
|
+
if (shouldFix("file_source_url") && fileSourceUrlFlags.length > 0) {
|
|
3611
3733
|
const FILE_FIXED = [];
|
|
3612
3734
|
for (const relPath of fileSourceUrlFlags) {
|
|
3613
3735
|
try {
|
|
3614
3736
|
const absPath = `${input.vault}/${relPath}`;
|
|
3615
|
-
const raw = await
|
|
3737
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3616
3738
|
const parts = raw.split("---", 3);
|
|
3617
3739
|
if (parts.length < 3) {
|
|
3618
3740
|
unresolved.push(relPath);
|
|
@@ -3647,36 +3769,11 @@ ${newBody}`;
|
|
|
3647
3769
|
}
|
|
3648
3770
|
}
|
|
3649
3771
|
const pathViolations = buckets.path_too_long;
|
|
3650
|
-
if (
|
|
3651
|
-
const
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
const newRelPath = truncateFilename(v.relPath);
|
|
3656
|
-
const newAbsPath = `${input.vault}/${newRelPath}`;
|
|
3657
|
-
await rename6(absPath, newAbsPath);
|
|
3658
|
-
const oldPathEscaped = v.relPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3659
|
-
for (const page of allPages) {
|
|
3660
|
-
if (page.relPath === v.relPath) continue;
|
|
3661
|
-
const content = await readFile16(page.absPath, "utf8");
|
|
3662
|
-
if (!content.includes(v.relPath)) continue;
|
|
3663
|
-
let updated = content;
|
|
3664
|
-
const citationRe = new RegExp(`\\^\\[${oldPathEscaped}\\]`, "g");
|
|
3665
|
-
updated = updated.replace(citationRe, `^[${newRelPath}]`);
|
|
3666
|
-
const wikilinkRe = new RegExp(`\\[\\[${oldPathEscaped}(\\|[^\\]]*)?\\]\\]`, "g");
|
|
3667
|
-
updated = updated.replace(wikilinkRe, (_m, alias) => `[[${newRelPath}${alias ?? ""}]]`);
|
|
3668
|
-
if (updated !== content) {
|
|
3669
|
-
const w = await safeWritePage(page.absPath, updated);
|
|
3670
|
-
if (!w.ok) {
|
|
3671
|
-
unresolved.push(`${page.relPath} (rewire)`);
|
|
3672
|
-
}
|
|
3673
|
-
}
|
|
3674
|
-
}
|
|
3675
|
-
pathFixed.push(v.relPath);
|
|
3676
|
-
} catch {
|
|
3677
|
-
unresolved.push(v.relPath);
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3772
|
+
if (shouldFix("path_too_long") && pathViolations && pathViolations.length > 0) {
|
|
3773
|
+
const pathFix = await fixPathTooLong({ vault: input.vault });
|
|
3774
|
+
const pathFixed = pathFix.result.ok ? pathFix.result.data.fixed.map((f) => f.from) : [];
|
|
3775
|
+
if (pathFix.result.ok) unresolved.push(...pathFix.result.data.unresolved);
|
|
3776
|
+
else unresolved.push(...pathViolations.map((v) => v.relPath));
|
|
3680
3777
|
fixed.push(...pathFixed);
|
|
3681
3778
|
if (pathFixed.length > 0) {
|
|
3682
3779
|
const fixedSet = new Set(pathFixed);
|
|
@@ -3690,13 +3787,6 @@ ${newBody}`;
|
|
|
3690
3787
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
3691
3788
|
const infoOut = INFO_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
3692
3789
|
if (input.only) {
|
|
3693
|
-
const allKnown = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
|
|
3694
|
-
if (!allKnown.includes(input.only)) {
|
|
3695
|
-
return {
|
|
3696
|
-
exitCode: ExitCode.USAGE,
|
|
3697
|
-
result: { ok: false, error: "UNKNOWN_BUCKET", detail: `Unknown bucket "${input.only}". Valid: ${allKnown.join(", ")}` }
|
|
3698
|
-
};
|
|
3699
|
-
}
|
|
3700
3790
|
const match = [...errorOut, ...warningOut, ...infoOut].filter((b) => b.kind === input.only);
|
|
3701
3791
|
const severity = ERROR_ORDER.includes(input.only) ? "error" : WARNING_ORDER.includes(input.only) ? "warning" : "info";
|
|
3702
3792
|
const filtered = severity === "error" ? { error: match, warning: [], info: [] } : severity === "warning" ? { error: [], warning: match, info: [] } : { error: [], warning: [], info: match };
|
|
@@ -3760,14 +3850,14 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
|
|
|
3760
3850
|
}
|
|
3761
3851
|
|
|
3762
3852
|
// src/commands/config.ts
|
|
3763
|
-
import { readFile as
|
|
3764
|
-
import { existsSync as
|
|
3765
|
-
import { join as
|
|
3853
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
3854
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3855
|
+
import { join as join23 } from "path";
|
|
3766
3856
|
function validateKey(key) {
|
|
3767
3857
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
3768
3858
|
}
|
|
3769
3859
|
function configPath(home) {
|
|
3770
|
-
return
|
|
3860
|
+
return join23(home, ".skillwiki", ".env");
|
|
3771
3861
|
}
|
|
3772
3862
|
async function runConfigGet(input) {
|
|
3773
3863
|
if (!validateKey(input.key)) {
|
|
@@ -3785,7 +3875,7 @@ async function runConfigSet(input) {
|
|
|
3785
3875
|
try {
|
|
3786
3876
|
let originalContent;
|
|
3787
3877
|
try {
|
|
3788
|
-
originalContent = await
|
|
3878
|
+
originalContent = await readFile18(filePath, "utf8");
|
|
3789
3879
|
} catch {
|
|
3790
3880
|
}
|
|
3791
3881
|
const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
|
|
@@ -3817,18 +3907,18 @@ async function runConfigList(input) {
|
|
|
3817
3907
|
}
|
|
3818
3908
|
async function runConfigPath(input) {
|
|
3819
3909
|
const filePath = configPath(input.home);
|
|
3820
|
-
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists:
|
|
3910
|
+
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync6(filePath), humanHint: filePath }) };
|
|
3821
3911
|
}
|
|
3822
3912
|
|
|
3823
3913
|
// src/commands/doctor.ts
|
|
3824
|
-
import { existsSync as
|
|
3825
|
-
import { join as
|
|
3914
|
+
import { existsSync as existsSync9, lstatSync, readlinkSync, readdirSync, statSync as statSync3, readFileSync as readFileSync7 } from "fs";
|
|
3915
|
+
import { join as join27, resolve as resolve4 } from "path";
|
|
3826
3916
|
import { execSync as execSync2 } from "child_process";
|
|
3827
3917
|
import { platform as platform2 } from "os";
|
|
3828
3918
|
|
|
3829
3919
|
// src/utils/auto-update.ts
|
|
3830
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as
|
|
3831
|
-
import { join as
|
|
3920
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
3921
|
+
import { join as join24, dirname as dirname9 } from "path";
|
|
3832
3922
|
import { spawn } from "child_process";
|
|
3833
3923
|
|
|
3834
3924
|
// src/utils/update-consts.ts
|
|
@@ -3839,7 +3929,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
|
|
|
3839
3929
|
|
|
3840
3930
|
// src/utils/auto-update.ts
|
|
3841
3931
|
function cachePath(home) {
|
|
3842
|
-
return
|
|
3932
|
+
return join24(home, ".skillwiki", CACHE_FILENAME);
|
|
3843
3933
|
}
|
|
3844
3934
|
function readCacheRaw(home) {
|
|
3845
3935
|
try {
|
|
@@ -3858,7 +3948,7 @@ function readCache(home) {
|
|
|
3858
3948
|
}
|
|
3859
3949
|
function writeCache(home, cache) {
|
|
3860
3950
|
const p = cachePath(home);
|
|
3861
|
-
mkdirSync3(
|
|
3951
|
+
mkdirSync3(dirname9(p), { recursive: true });
|
|
3862
3952
|
writeFileSync4(p, JSON.stringify(cache, null, 2));
|
|
3863
3953
|
}
|
|
3864
3954
|
function latestFromCache(home, currentVersion) {
|
|
@@ -3877,7 +3967,7 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
3877
3967
|
const { isStale: isStale2 } = readCache(home);
|
|
3878
3968
|
if (!isStale2) return;
|
|
3879
3969
|
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
3880
|
-
if (!
|
|
3970
|
+
if (!existsSync7(bgScript)) return;
|
|
3881
3971
|
const child = spawn(process.execPath, [bgScript, home, currentVersion], {
|
|
3882
3972
|
detached: true,
|
|
3883
3973
|
stdio: "ignore"
|
|
@@ -3889,12 +3979,12 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
3889
3979
|
|
|
3890
3980
|
// src/utils/plugin-registry.ts
|
|
3891
3981
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3892
|
-
import { join as
|
|
3893
|
-
var REGISTRY_PATH =
|
|
3982
|
+
import { join as join25 } from "path";
|
|
3983
|
+
var REGISTRY_PATH = join25(".claude", "plugins", "installed_plugins.json");
|
|
3894
3984
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
3895
3985
|
function readInstalledPlugins(home) {
|
|
3896
3986
|
try {
|
|
3897
|
-
const raw = readFileSync5(
|
|
3987
|
+
const raw = readFileSync5(join25(home, REGISTRY_PATH), "utf8");
|
|
3898
3988
|
return JSON.parse(raw);
|
|
3899
3989
|
} catch {
|
|
3900
3990
|
return null;
|
|
@@ -3911,8 +4001,8 @@ function findPlugin(home, key = PLUGIN_KEY) {
|
|
|
3911
4001
|
// src/utils/s3-mount-health.ts
|
|
3912
4002
|
import { execSync } from "child_process";
|
|
3913
4003
|
import { platform } from "os";
|
|
3914
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, readFileSync as
|
|
3915
|
-
import { join as
|
|
4004
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, readFileSync as readFile19 } from "fs";
|
|
4005
|
+
import { join as join26 } from "path";
|
|
3916
4006
|
var OS = platform();
|
|
3917
4007
|
function findRcloneMountPid() {
|
|
3918
4008
|
try {
|
|
@@ -4070,7 +4160,7 @@ function detectFuseMount(vaultPath) {
|
|
|
4070
4160
|
return null;
|
|
4071
4161
|
}
|
|
4072
4162
|
function writeTest(dir) {
|
|
4073
|
-
const testFile =
|
|
4163
|
+
const testFile = join26(dir, `.doctor-write-test-${process.pid}.tmp`);
|
|
4074
4164
|
const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
|
|
4075
4165
|
const start = Date.now();
|
|
4076
4166
|
try {
|
|
@@ -4081,7 +4171,7 @@ function writeTest(dir) {
|
|
|
4081
4171
|
const writeMs = Date.now() - start;
|
|
4082
4172
|
const readStart = Date.now();
|
|
4083
4173
|
try {
|
|
4084
|
-
const back =
|
|
4174
|
+
const back = readFile19(testFile, "utf8");
|
|
4085
4175
|
const readMs = Date.now() - readStart;
|
|
4086
4176
|
if (back !== payload) {
|
|
4087
4177
|
try {
|
|
@@ -4170,13 +4260,13 @@ function detectCliChannels(argv, home) {
|
|
|
4170
4260
|
}
|
|
4171
4261
|
const plugin = findPlugin(home);
|
|
4172
4262
|
if (plugin) {
|
|
4173
|
-
const pluginBin =
|
|
4174
|
-
if (
|
|
4263
|
+
const pluginBin = join27(plugin.installPath, "bin", "skillwiki");
|
|
4264
|
+
if (existsSync9(pluginBin)) {
|
|
4175
4265
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
4176
4266
|
}
|
|
4177
4267
|
}
|
|
4178
|
-
const installBin =
|
|
4179
|
-
if (
|
|
4268
|
+
const installBin = join27(home, ".claude", "skills", "bin", "skillwiki");
|
|
4269
|
+
if (existsSync9(installBin)) {
|
|
4180
4270
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
4181
4271
|
}
|
|
4182
4272
|
return channels;
|
|
@@ -4228,7 +4318,7 @@ function checkCliChannels(argv, home) {
|
|
|
4228
4318
|
}
|
|
4229
4319
|
async function checkConfigFile(home) {
|
|
4230
4320
|
const cfgPath = configPath(home);
|
|
4231
|
-
if (!
|
|
4321
|
+
if (!existsSync9(cfgPath)) {
|
|
4232
4322
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
4233
4323
|
}
|
|
4234
4324
|
try {
|
|
@@ -4243,7 +4333,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
4243
4333
|
if (resolvedPath === void 0) {
|
|
4244
4334
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4245
4335
|
}
|
|
4246
|
-
if (
|
|
4336
|
+
if (existsSync9(resolvedPath) && statSync3(resolvedPath).isDirectory()) {
|
|
4247
4337
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
4248
4338
|
}
|
|
4249
4339
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -4252,13 +4342,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4252
4342
|
if (resolvedPath === void 0) {
|
|
4253
4343
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4254
4344
|
}
|
|
4255
|
-
if (!
|
|
4345
|
+
if (!existsSync9(resolvedPath)) {
|
|
4256
4346
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
4257
4347
|
}
|
|
4258
4348
|
const missing = [];
|
|
4259
|
-
if (!
|
|
4349
|
+
if (!existsSync9(join27(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
4260
4350
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
4261
|
-
if (!
|
|
4351
|
+
if (!existsSync9(join27(resolvedPath, dir))) missing.push(dir + "/");
|
|
4262
4352
|
}
|
|
4263
4353
|
if (missing.length === 0) {
|
|
4264
4354
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -4266,8 +4356,8 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4266
4356
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
4267
4357
|
}
|
|
4268
4358
|
function checkSkillsInstalled(home, cwd) {
|
|
4269
|
-
const srcDir = cwd ?
|
|
4270
|
-
if (srcDir &&
|
|
4359
|
+
const srcDir = cwd ? join27(cwd, "packages", "skills") : void 0;
|
|
4360
|
+
if (srcDir && existsSync9(srcDir)) {
|
|
4271
4361
|
const found = findInstalledSkillMd(srcDir);
|
|
4272
4362
|
if (found.length > 0) {
|
|
4273
4363
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
|
|
@@ -4280,8 +4370,8 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4280
4370
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
4281
4371
|
}
|
|
4282
4372
|
}
|
|
4283
|
-
const skillsDir =
|
|
4284
|
-
if (
|
|
4373
|
+
const skillsDir = join27(home, ".claude", "skills");
|
|
4374
|
+
if (existsSync9(skillsDir)) {
|
|
4285
4375
|
const found = findInstalledSkillMd(skillsDir);
|
|
4286
4376
|
if (found.length > 0) {
|
|
4287
4377
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
|
|
@@ -4291,10 +4381,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4291
4381
|
}
|
|
4292
4382
|
function checkDuplicateSkills(home) {
|
|
4293
4383
|
const plugin = findPlugin(home);
|
|
4294
|
-
const skillsDir =
|
|
4384
|
+
const skillsDir = join27(home, ".claude", "skills");
|
|
4295
4385
|
const agentSkillDirs = [
|
|
4296
|
-
{ label: "~/.codex/skills/", path:
|
|
4297
|
-
{ label: "~/.agents/skills/", path:
|
|
4386
|
+
{ label: "~/.codex/skills/", path: join27(home, ".codex", "skills") },
|
|
4387
|
+
{ label: "~/.agents/skills/", path: join27(home, ".agents", "skills") }
|
|
4298
4388
|
];
|
|
4299
4389
|
if (!plugin) {
|
|
4300
4390
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -4371,8 +4461,8 @@ async function checkProfiles(home) {
|
|
|
4371
4461
|
}
|
|
4372
4462
|
async function checkProjectLocalOverride(cwd) {
|
|
4373
4463
|
const dir = cwd ?? process.cwd();
|
|
4374
|
-
const envPath =
|
|
4375
|
-
if (
|
|
4464
|
+
const envPath = join27(dir, ".skillwiki", ".env");
|
|
4465
|
+
if (existsSync9(envPath)) {
|
|
4376
4466
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
4377
4467
|
}
|
|
4378
4468
|
return check("pass", "project_local", "Project-local config", "None");
|
|
@@ -4381,7 +4471,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
4381
4471
|
if (resolvedPath === void 0) {
|
|
4382
4472
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4383
4473
|
}
|
|
4384
|
-
if (!
|
|
4474
|
+
if (!existsSync9(join27(resolvedPath, ".git"))) {
|
|
4385
4475
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
4386
4476
|
}
|
|
4387
4477
|
try {
|
|
@@ -4404,9 +4494,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
4404
4494
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4405
4495
|
}
|
|
4406
4496
|
const missing = [];
|
|
4407
|
-
if (!
|
|
4408
|
-
if (!
|
|
4409
|
-
if (!
|
|
4497
|
+
if (!existsSync9(join27(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
4498
|
+
if (!existsSync9(join27(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
4499
|
+
if (!existsSync9(join27(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
4410
4500
|
if (missing.length === 0) {
|
|
4411
4501
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
4412
4502
|
}
|
|
@@ -4416,8 +4506,8 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4416
4506
|
if (resolvedPath === void 0) {
|
|
4417
4507
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4418
4508
|
}
|
|
4419
|
-
const rawDir =
|
|
4420
|
-
if (!
|
|
4509
|
+
const rawDir = join27(resolvedPath, "raw");
|
|
4510
|
+
if (!existsSync9(rawDir)) {
|
|
4421
4511
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
4422
4512
|
}
|
|
4423
4513
|
const found = [];
|
|
@@ -4432,7 +4522,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4432
4522
|
if (entry.name === ".DS_Store") {
|
|
4433
4523
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
4434
4524
|
} else if (entry.isDirectory()) {
|
|
4435
|
-
walk2(
|
|
4525
|
+
walk2(join27(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
4436
4526
|
}
|
|
4437
4527
|
}
|
|
4438
4528
|
})(rawDir, "");
|
|
@@ -4445,7 +4535,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
4445
4535
|
if (resolvedPath === void 0) {
|
|
4446
4536
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4447
4537
|
}
|
|
4448
|
-
if (!
|
|
4538
|
+
if (!existsSync9(join27(resolvedPath, ".git"))) {
|
|
4449
4539
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
4450
4540
|
}
|
|
4451
4541
|
let timestamp;
|
|
@@ -4486,8 +4576,8 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
4486
4576
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
4487
4577
|
}
|
|
4488
4578
|
const mountPoint = fuse.mountPoint;
|
|
4489
|
-
const conceptsDir =
|
|
4490
|
-
if (!
|
|
4579
|
+
const conceptsDir = join27(resolvedPath, "concepts");
|
|
4580
|
+
if (!existsSync9(conceptsDir)) {
|
|
4491
4581
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
4492
4582
|
}
|
|
4493
4583
|
const start = Date.now();
|
|
@@ -4669,8 +4759,8 @@ function checkWriteTest(resolvedPath) {
|
|
|
4669
4759
|
if (!fuse) {
|
|
4670
4760
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
4671
4761
|
}
|
|
4672
|
-
const conceptsDir =
|
|
4673
|
-
if (!
|
|
4762
|
+
const conceptsDir = join27(resolvedPath, "concepts");
|
|
4763
|
+
if (!existsSync9(conceptsDir)) {
|
|
4674
4764
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
4675
4765
|
}
|
|
4676
4766
|
const result = writeTest(conceptsDir);
|
|
@@ -4756,7 +4846,7 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
4756
4846
|
}
|
|
4757
4847
|
function readVaultSyncConfig(home) {
|
|
4758
4848
|
try {
|
|
4759
|
-
const content = readFileSync7(
|
|
4849
|
+
const content = readFileSync7(join27(home, ".skillwiki", ".env"), "utf8");
|
|
4760
4850
|
let installed = false;
|
|
4761
4851
|
let role;
|
|
4762
4852
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -4790,12 +4880,12 @@ function vaultSyncChecks(input) {
|
|
|
4790
4880
|
];
|
|
4791
4881
|
}
|
|
4792
4882
|
const isMac = os === "darwin";
|
|
4793
|
-
const logDir = input.logDir ?? (isMac ?
|
|
4794
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
4795
|
-
const filterPath = input.filterPath ??
|
|
4883
|
+
const logDir = input.logDir ?? (isMac ? join27(home, "Library", "Logs") : join27(home, ".local", "state", "vault-sync", "log"));
|
|
4884
|
+
const shareDir = input.shareDir ?? (isMac ? join27(home, "Library", "Application Support", "vault-sync", "bin") : join27(home, ".local", "share", "vault-sync", "bin"));
|
|
4885
|
+
const filterPath = input.filterPath ?? join27(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
4796
4886
|
const snapshotPath = input.snapshotScriptPath ?? "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
4797
|
-
const pushScriptPath =
|
|
4798
|
-
const c1 =
|
|
4887
|
+
const pushScriptPath = join27(shareDir, "wiki-push.sh");
|
|
4888
|
+
const c1 = existsSync9(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`);
|
|
4799
4889
|
let c2;
|
|
4800
4890
|
try {
|
|
4801
4891
|
if (isMac) {
|
|
@@ -4846,7 +4936,7 @@ function vaultSyncChecks(input) {
|
|
|
4846
4936
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
4847
4937
|
);
|
|
4848
4938
|
}
|
|
4849
|
-
const logFile =
|
|
4939
|
+
const logFile = join27(logDir, "wiki-push.log");
|
|
4850
4940
|
let c3;
|
|
4851
4941
|
try {
|
|
4852
4942
|
const logContent = readFileSync7(logFile, "utf8");
|
|
@@ -4905,7 +4995,7 @@ function vaultSyncChecks(input) {
|
|
|
4905
4995
|
}
|
|
4906
4996
|
}
|
|
4907
4997
|
} catch {
|
|
4908
|
-
c3 =
|
|
4998
|
+
c3 = existsSync9(logDir) ? check(
|
|
4909
4999
|
"warn",
|
|
4910
5000
|
"vault_sync_last_push_age",
|
|
4911
5001
|
"Vault sync last push recency",
|
|
@@ -4917,7 +5007,7 @@ function vaultSyncChecks(input) {
|
|
|
4917
5007
|
`Log directory not found at ${logDir}`
|
|
4918
5008
|
);
|
|
4919
5009
|
}
|
|
4920
|
-
const fetchLogFile =
|
|
5010
|
+
const fetchLogFile = join27(logDir, "wiki-fetch.log");
|
|
4921
5011
|
let cFetch;
|
|
4922
5012
|
try {
|
|
4923
5013
|
const logContent = readFileSync7(fetchLogFile, "utf8");
|
|
@@ -4964,7 +5054,7 @@ function vaultSyncChecks(input) {
|
|
|
4964
5054
|
}
|
|
4965
5055
|
let c4;
|
|
4966
5056
|
try {
|
|
4967
|
-
if (!
|
|
5057
|
+
if (!existsSync9(filterPath)) {
|
|
4968
5058
|
c4 = check(
|
|
4969
5059
|
"error",
|
|
4970
5060
|
"vault_sync_filter_present",
|
|
@@ -5013,7 +5103,7 @@ function vaultSyncChecks(input) {
|
|
|
5013
5103
|
);
|
|
5014
5104
|
} else {
|
|
5015
5105
|
try {
|
|
5016
|
-
if (!
|
|
5106
|
+
if (!existsSync9(snapshotPath)) {
|
|
5017
5107
|
c5 = check(
|
|
5018
5108
|
"error",
|
|
5019
5109
|
"vault_sync_snapshot_guard",
|
|
@@ -5059,15 +5149,15 @@ function findSkillMd(dir) {
|
|
|
5059
5149
|
}
|
|
5060
5150
|
for (const entry of entries) {
|
|
5061
5151
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
5062
|
-
results.push(
|
|
5152
|
+
results.push(join27(dir, entry.name));
|
|
5063
5153
|
} else if (entry.isDirectory()) {
|
|
5064
|
-
results.push(...findSkillMd(
|
|
5154
|
+
results.push(...findSkillMd(join27(dir, entry.name)));
|
|
5065
5155
|
}
|
|
5066
5156
|
}
|
|
5067
5157
|
return results;
|
|
5068
5158
|
}
|
|
5069
5159
|
function findInstalledSkillMd(dir) {
|
|
5070
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
5160
|
+
const directSkills = findSkillNames(dir).map((name) => join27(dir, name, "SKILL.md"));
|
|
5071
5161
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
5072
5162
|
}
|
|
5073
5163
|
function findSkillNames(dir) {
|
|
@@ -5079,7 +5169,7 @@ function findSkillNames(dir) {
|
|
|
5079
5169
|
return results;
|
|
5080
5170
|
}
|
|
5081
5171
|
for (const entry of entries) {
|
|
5082
|
-
if (entry.isDirectory() &&
|
|
5172
|
+
if (entry.isDirectory() && existsSync9(join27(dir, entry.name, "SKILL.md"))) {
|
|
5083
5173
|
results.push(entry.name);
|
|
5084
5174
|
}
|
|
5085
5175
|
}
|
|
@@ -5123,7 +5213,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
5123
5213
|
}
|
|
5124
5214
|
let logLines = 0;
|
|
5125
5215
|
try {
|
|
5126
|
-
logLines = readFileSync7(
|
|
5216
|
+
logLines = readFileSync7(join27(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
5127
5217
|
} catch {
|
|
5128
5218
|
}
|
|
5129
5219
|
return [
|
|
@@ -5194,8 +5284,8 @@ async function runDoctor(input) {
|
|
|
5194
5284
|
}
|
|
5195
5285
|
|
|
5196
5286
|
// src/commands/archive.ts
|
|
5197
|
-
import { rename as rename7, mkdir as
|
|
5198
|
-
import { join as
|
|
5287
|
+
import { rename as rename7, mkdir as mkdir9, readFile as readFile20, writeFile as writeFile10 } from "fs/promises";
|
|
5288
|
+
import { join as join28, dirname as dirname10 } from "path";
|
|
5199
5289
|
function countWikilinks(body, slug) {
|
|
5200
5290
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5201
5291
|
const re = new RegExp(`\\[\\[${escaped}(?:[|#][^\\]]*)?\\]\\]`, "g");
|
|
@@ -5223,7 +5313,7 @@ async function runArchive(input) {
|
|
|
5223
5313
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
5224
5314
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
5225
5315
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
5226
|
-
const archivePath =
|
|
5316
|
+
const archivePath = join28("_archive", relPath).replace(/\\/g, "/");
|
|
5227
5317
|
let cascade;
|
|
5228
5318
|
if (input.cascade) {
|
|
5229
5319
|
const wikilinkRefs = [];
|
|
@@ -5247,7 +5337,7 @@ async function runArchive(input) {
|
|
|
5247
5337
|
const indexRefs = [];
|
|
5248
5338
|
if (!isRaw) {
|
|
5249
5339
|
try {
|
|
5250
|
-
const idx = await
|
|
5340
|
+
const idx = await readFile20(join28(input.vault, "index.md"), "utf8");
|
|
5251
5341
|
idx.split("\n").forEach((line, i) => {
|
|
5252
5342
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
5253
5343
|
});
|
|
@@ -5273,8 +5363,8 @@ async function runArchive(input) {
|
|
|
5273
5363
|
}
|
|
5274
5364
|
if (input.cascade && input.apply && cascade) {
|
|
5275
5365
|
for (const ref of cascade.source_array_refs) {
|
|
5276
|
-
const absPath =
|
|
5277
|
-
const text = await
|
|
5366
|
+
const absPath = join28(input.vault, ref.page);
|
|
5367
|
+
const text = await readFile20(absPath, "utf8");
|
|
5278
5368
|
const split = splitFrontmatter(text);
|
|
5279
5369
|
if (!split.ok) continue;
|
|
5280
5370
|
const before = split.data.rawFrontmatter;
|
|
@@ -5291,12 +5381,12 @@ ${fmRewritten}
|
|
|
5291
5381
|
}
|
|
5292
5382
|
}
|
|
5293
5383
|
}
|
|
5294
|
-
await
|
|
5384
|
+
await mkdir9(dirname10(join28(input.vault, archivePath)), { recursive: true });
|
|
5295
5385
|
let indexUpdated = false;
|
|
5296
5386
|
if (!isRaw) {
|
|
5297
|
-
const indexPath =
|
|
5387
|
+
const indexPath = join28(input.vault, "index.md");
|
|
5298
5388
|
try {
|
|
5299
|
-
const idx = await
|
|
5389
|
+
const idx = await readFile20(indexPath, "utf8");
|
|
5300
5390
|
const originalLines = idx.split("\n");
|
|
5301
5391
|
const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
|
|
5302
5392
|
if (filtered.length !== originalLines.length) {
|
|
@@ -5307,7 +5397,7 @@ ${fmRewritten}
|
|
|
5307
5397
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
5308
5398
|
}
|
|
5309
5399
|
}
|
|
5310
|
-
await rename7(
|
|
5400
|
+
await rename7(join28(input.vault, relPath), join28(input.vault, archivePath));
|
|
5311
5401
|
appendLastOp(input.vault, {
|
|
5312
5402
|
operation: input.cascade ? "archive-cascade" : "archive",
|
|
5313
5403
|
summary: `moved ${relPath} to ${archivePath}${input.cascade ? ` (cascade: ${cascade?.source_array_refs.length ?? 0} source arrays updated)` : ""}`,
|
|
@@ -5694,14 +5784,14 @@ ${newBody}`;
|
|
|
5694
5784
|
// src/commands/update.ts
|
|
5695
5785
|
import { execSync as execSync3 } from "child_process";
|
|
5696
5786
|
import { readFileSync as readFileSync8 } from "fs";
|
|
5697
|
-
import { join as
|
|
5787
|
+
import { join as join29 } from "path";
|
|
5698
5788
|
function resolveGlobalSkillsRoot() {
|
|
5699
5789
|
try {
|
|
5700
5790
|
const globalRoot = execSync3("npm root -g", {
|
|
5701
5791
|
encoding: "utf8",
|
|
5702
5792
|
timeout: 5e3
|
|
5703
5793
|
}).trim();
|
|
5704
|
-
return
|
|
5794
|
+
return join29(globalRoot, "skillwiki", "skills");
|
|
5705
5795
|
} catch {
|
|
5706
5796
|
return null;
|
|
5707
5797
|
}
|
|
@@ -5727,7 +5817,7 @@ async function runUpdate(input) {
|
|
|
5727
5817
|
);
|
|
5728
5818
|
const currentVersion = pkg2.version;
|
|
5729
5819
|
const tag = input.distTag ?? "latest";
|
|
5730
|
-
const target =
|
|
5820
|
+
const target = join29(input.home, ".claude", "skills");
|
|
5731
5821
|
let latest;
|
|
5732
5822
|
try {
|
|
5733
5823
|
latest = execSync3(`npm view skillwiki@${tag} version`, {
|
|
@@ -5797,16 +5887,16 @@ async function runUpdate(input) {
|
|
|
5797
5887
|
|
|
5798
5888
|
// src/commands/self-update.ts
|
|
5799
5889
|
import { execSync as execSync4 } from "child_process";
|
|
5800
|
-
import { existsSync as
|
|
5801
|
-
import { join as
|
|
5890
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5891
|
+
import { join as join30 } from "path";
|
|
5802
5892
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
5803
5893
|
async function runSelfUpdate(input) {
|
|
5804
5894
|
const currentVersion = JSON.parse(
|
|
5805
5895
|
readFileSync9(new URL("../../package.json", import.meta.url), "utf8")
|
|
5806
5896
|
).version;
|
|
5807
5897
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
5808
|
-
const localPkgPath =
|
|
5809
|
-
const hasLocalSource =
|
|
5898
|
+
const localPkgPath = join30(sourceRoot, "packages", "cli", "package.json");
|
|
5899
|
+
const hasLocalSource = existsSync10(localPkgPath);
|
|
5810
5900
|
if (input.check) {
|
|
5811
5901
|
let availableVersion = null;
|
|
5812
5902
|
let source;
|
|
@@ -5937,10 +6027,10 @@ async function runSelfUpdate(input) {
|
|
|
5937
6027
|
}
|
|
5938
6028
|
|
|
5939
6029
|
// src/commands/transcripts.ts
|
|
5940
|
-
import { readdir as readdir5, stat as stat7, readFile as
|
|
5941
|
-
import { join as
|
|
6030
|
+
import { readdir as readdir5, stat as stat7, readFile as readFile21 } from "fs/promises";
|
|
6031
|
+
import { join as join31 } from "path";
|
|
5942
6032
|
async function runTranscripts(input) {
|
|
5943
|
-
const dir =
|
|
6033
|
+
const dir = join31(input.vault, "raw", "transcripts");
|
|
5944
6034
|
let entries;
|
|
5945
6035
|
try {
|
|
5946
6036
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -5950,8 +6040,8 @@ async function runTranscripts(input) {
|
|
|
5950
6040
|
const transcripts = [];
|
|
5951
6041
|
for (const entry of entries) {
|
|
5952
6042
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5953
|
-
const filePath =
|
|
5954
|
-
const content = await
|
|
6043
|
+
const filePath = join31(dir, entry.name);
|
|
6044
|
+
const content = await readFile21(filePath, "utf8");
|
|
5955
6045
|
const fm = extractFrontmatter(content);
|
|
5956
6046
|
if (!fm.ok) continue;
|
|
5957
6047
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -5968,12 +6058,12 @@ async function runTranscripts(input) {
|
|
|
5968
6058
|
}
|
|
5969
6059
|
|
|
5970
6060
|
// src/commands/project-index.ts
|
|
5971
|
-
import { readdir as readdir6, readFile as
|
|
5972
|
-
import { join as
|
|
6061
|
+
import { readdir as readdir6, readFile as readFile22, writeFile as writeFile11, mkdir as mkdir10 } from "fs/promises";
|
|
6062
|
+
import { join as join32, dirname as dirname11 } from "path";
|
|
5973
6063
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
5974
6064
|
async function runProjectIndex(input) {
|
|
5975
6065
|
const slug = input.slug;
|
|
5976
|
-
const projectDir =
|
|
6066
|
+
const projectDir = join32(input.vault, "projects", slug);
|
|
5977
6067
|
try {
|
|
5978
6068
|
await readdir6(projectDir);
|
|
5979
6069
|
} catch {
|
|
@@ -5984,15 +6074,15 @@ async function runProjectIndex(input) {
|
|
|
5984
6074
|
}
|
|
5985
6075
|
const wikilinkPattern = `[[${slug}]]`;
|
|
5986
6076
|
const entries = [];
|
|
5987
|
-
const compoundDir =
|
|
6077
|
+
const compoundDir = join32(input.vault, "projects", slug, "compound");
|
|
5988
6078
|
try {
|
|
5989
6079
|
const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
|
|
5990
6080
|
for (const entry of compoundFiles) {
|
|
5991
6081
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5992
|
-
const filePath =
|
|
6082
|
+
const filePath = join32(compoundDir, entry.name);
|
|
5993
6083
|
let text;
|
|
5994
6084
|
try {
|
|
5995
|
-
text = await
|
|
6085
|
+
text = await readFile22(filePath, "utf8");
|
|
5996
6086
|
} catch {
|
|
5997
6087
|
continue;
|
|
5998
6088
|
}
|
|
@@ -6009,16 +6099,16 @@ async function runProjectIndex(input) {
|
|
|
6009
6099
|
for (const dir of LAYER2_DIRS) {
|
|
6010
6100
|
let files;
|
|
6011
6101
|
try {
|
|
6012
|
-
files = await readdir6(
|
|
6102
|
+
files = await readdir6(join32(input.vault, dir), { withFileTypes: true });
|
|
6013
6103
|
} catch {
|
|
6014
6104
|
continue;
|
|
6015
6105
|
}
|
|
6016
6106
|
for (const entry of files) {
|
|
6017
6107
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6018
|
-
const filePath =
|
|
6108
|
+
const filePath = join32(input.vault, dir, entry.name);
|
|
6019
6109
|
let text;
|
|
6020
6110
|
try {
|
|
6021
|
-
text = await
|
|
6111
|
+
text = await readFile22(filePath, "utf8");
|
|
6022
6112
|
} catch {
|
|
6023
6113
|
continue;
|
|
6024
6114
|
}
|
|
@@ -6039,11 +6129,11 @@ async function runProjectIndex(input) {
|
|
|
6039
6129
|
const tb = typeOrder[b.type] ?? 99;
|
|
6040
6130
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
6041
6131
|
});
|
|
6042
|
-
const indexPath =
|
|
6132
|
+
const indexPath = join32(projectDir, "knowledge.md");
|
|
6043
6133
|
let existing = false;
|
|
6044
6134
|
let stale = false;
|
|
6045
6135
|
try {
|
|
6046
|
-
const existingText = await
|
|
6136
|
+
const existingText = await readFile22(indexPath, "utf8");
|
|
6047
6137
|
existing = true;
|
|
6048
6138
|
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
6049
6139
|
const existingPages = new Set(existingEntries.map((l) => {
|
|
@@ -6083,7 +6173,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
6083
6173
|
}
|
|
6084
6174
|
if (input.apply) {
|
|
6085
6175
|
try {
|
|
6086
|
-
await
|
|
6176
|
+
await mkdir10(dirname11(indexPath), { recursive: true });
|
|
6087
6177
|
await writeFile11(indexPath, body, "utf8");
|
|
6088
6178
|
} catch (e) {
|
|
6089
6179
|
return {
|
|
@@ -6112,10 +6202,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
6112
6202
|
}
|
|
6113
6203
|
|
|
6114
6204
|
// src/commands/compound.ts
|
|
6115
|
-
import { writeFile as writeFile12, mkdir as
|
|
6116
|
-
import { join as
|
|
6117
|
-
import { existsSync as
|
|
6118
|
-
import { readFile as
|
|
6205
|
+
import { writeFile as writeFile12, mkdir as mkdir11, readdir as readdir7, unlink as unlink4 } from "fs/promises";
|
|
6206
|
+
import { join as join33 } from "path";
|
|
6207
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6208
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
6119
6209
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
6120
6210
|
var FIELD_RE = {
|
|
6121
6211
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -6213,17 +6303,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
6213
6303
|
};
|
|
6214
6304
|
}
|
|
6215
6305
|
async function runCompound(input) {
|
|
6216
|
-
const logPath =
|
|
6306
|
+
const logPath = join33(input.vault, "log.md");
|
|
6217
6307
|
let logText;
|
|
6218
6308
|
try {
|
|
6219
|
-
logText = await
|
|
6309
|
+
logText = await readFile23(logPath, "utf8");
|
|
6220
6310
|
} catch {
|
|
6221
6311
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
6222
6312
|
}
|
|
6223
6313
|
const entries = parseRetroEntries(logText);
|
|
6224
6314
|
const promoted = [];
|
|
6225
6315
|
const skipped = [];
|
|
6226
|
-
const compoundDir =
|
|
6316
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6227
6317
|
for (const entry of entries) {
|
|
6228
6318
|
const generalizeValue = entry.generalize.trim();
|
|
6229
6319
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -6231,8 +6321,8 @@ async function runCompound(input) {
|
|
|
6231
6321
|
continue;
|
|
6232
6322
|
}
|
|
6233
6323
|
const slug = slugify(entry.cycleName);
|
|
6234
|
-
const compoundPath =
|
|
6235
|
-
if (
|
|
6324
|
+
const compoundPath = join33(compoundDir, `${slug}.md`);
|
|
6325
|
+
if (existsSync11(compoundPath)) {
|
|
6236
6326
|
skipped.push(entry.date);
|
|
6237
6327
|
continue;
|
|
6238
6328
|
}
|
|
@@ -6270,8 +6360,8 @@ async function runCompound(input) {
|
|
|
6270
6360
|
].join("\n");
|
|
6271
6361
|
const content = frontmatter + "\n" + body;
|
|
6272
6362
|
if (!input.dryRun) {
|
|
6273
|
-
if (!
|
|
6274
|
-
await
|
|
6363
|
+
if (!existsSync11(compoundDir)) {
|
|
6364
|
+
await mkdir11(compoundDir, { recursive: true });
|
|
6275
6365
|
}
|
|
6276
6366
|
await writeFile12(compoundPath, content, "utf8");
|
|
6277
6367
|
}
|
|
@@ -6292,23 +6382,23 @@ async function runCompound(input) {
|
|
|
6292
6382
|
};
|
|
6293
6383
|
}
|
|
6294
6384
|
async function runCompoundDelete(input) {
|
|
6295
|
-
const projectDir =
|
|
6296
|
-
if (!
|
|
6385
|
+
const projectDir = join33(input.vault, "projects", input.project);
|
|
6386
|
+
if (!existsSync11(projectDir)) {
|
|
6297
6387
|
return {
|
|
6298
6388
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
6299
6389
|
result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
|
|
6300
6390
|
};
|
|
6301
6391
|
}
|
|
6302
6392
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
6303
|
-
const compoundPath =
|
|
6304
|
-
if (!
|
|
6393
|
+
const compoundPath = join33(projectDir, "compound", `${entryName}.md`);
|
|
6394
|
+
if (!existsSync11(compoundPath)) {
|
|
6305
6395
|
return {
|
|
6306
6396
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
6307
6397
|
result: err("FILE_NOT_FOUND", { path: compoundPath })
|
|
6308
6398
|
};
|
|
6309
6399
|
}
|
|
6310
6400
|
try {
|
|
6311
|
-
await
|
|
6401
|
+
await unlink4(compoundPath);
|
|
6312
6402
|
} catch (e) {
|
|
6313
6403
|
return {
|
|
6314
6404
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6334,8 +6424,8 @@ knowledge.md regenerated`
|
|
|
6334
6424
|
};
|
|
6335
6425
|
}
|
|
6336
6426
|
async function runCompoundList(input) {
|
|
6337
|
-
const compoundDir =
|
|
6338
|
-
if (!
|
|
6427
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6428
|
+
if (!existsSync11(compoundDir)) {
|
|
6339
6429
|
return {
|
|
6340
6430
|
exitCode: ExitCode.OK,
|
|
6341
6431
|
result: ok({
|
|
@@ -6365,10 +6455,10 @@ could not read compound directory`
|
|
|
6365
6455
|
const entries = [];
|
|
6366
6456
|
for (const dirent of dirents) {
|
|
6367
6457
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
6368
|
-
const filePath =
|
|
6458
|
+
const filePath = join33(compoundDir, dirent.name);
|
|
6369
6459
|
let text;
|
|
6370
6460
|
try {
|
|
6371
|
-
text = await
|
|
6461
|
+
text = await readFile23(filePath, "utf8");
|
|
6372
6462
|
} catch {
|
|
6373
6463
|
continue;
|
|
6374
6464
|
}
|
|
@@ -6397,9 +6487,9 @@ no compound entries found`;
|
|
|
6397
6487
|
}
|
|
6398
6488
|
|
|
6399
6489
|
// src/commands/observe.ts
|
|
6400
|
-
import { mkdir as
|
|
6401
|
-
import { existsSync as
|
|
6402
|
-
import { join as
|
|
6490
|
+
import { mkdir as mkdir12, writeFile as writeFile13 } from "fs/promises";
|
|
6491
|
+
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
6492
|
+
import { join as join34 } from "path";
|
|
6403
6493
|
import { createHash as createHash4 } from "crypto";
|
|
6404
6494
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6405
6495
|
function slugify2(text) {
|
|
@@ -6422,15 +6512,15 @@ async function runObserve(input) {
|
|
|
6422
6512
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
6423
6513
|
};
|
|
6424
6514
|
}
|
|
6425
|
-
if (!
|
|
6515
|
+
if (!existsSync12(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
6426
6516
|
return {
|
|
6427
6517
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6428
6518
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6429
6519
|
};
|
|
6430
6520
|
}
|
|
6431
|
-
const transcriptsDir =
|
|
6521
|
+
const transcriptsDir = join34(input.vault, "raw", "transcripts");
|
|
6432
6522
|
try {
|
|
6433
|
-
await
|
|
6523
|
+
await mkdir12(transcriptsDir, { recursive: true });
|
|
6434
6524
|
} catch {
|
|
6435
6525
|
return {
|
|
6436
6526
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
@@ -6440,7 +6530,7 @@ async function runObserve(input) {
|
|
|
6440
6530
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6441
6531
|
const slug = slugify2(input.text);
|
|
6442
6532
|
const fileName = `${today}-observation-${slug}.md`;
|
|
6443
|
-
const filePath =
|
|
6533
|
+
const filePath = join34(transcriptsDir, fileName);
|
|
6444
6534
|
const body = `
|
|
6445
6535
|
${input.text.trim()}
|
|
6446
6536
|
`;
|
|
@@ -6480,8 +6570,8 @@ ${input.text.trim()}
|
|
|
6480
6570
|
}
|
|
6481
6571
|
|
|
6482
6572
|
// src/commands/ingest.ts
|
|
6483
|
-
import { readFile as
|
|
6484
|
-
import { join as
|
|
6573
|
+
import { readFile as readFile24, writeFile as writeFile14, mkdir as mkdir13 } from "fs/promises";
|
|
6574
|
+
import { join as join35 } from "path";
|
|
6485
6575
|
import { createHash as createHash5 } from "crypto";
|
|
6486
6576
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
6487
6577
|
var TYPE_DIR = {
|
|
@@ -6640,7 +6730,7 @@ async function runIngest(input) {
|
|
|
6640
6730
|
sourceContent = fetchResult.data.body;
|
|
6641
6731
|
} else {
|
|
6642
6732
|
try {
|
|
6643
|
-
sourceContent = await
|
|
6733
|
+
sourceContent = await readFile24(input.source, "utf8");
|
|
6644
6734
|
} catch {
|
|
6645
6735
|
return {
|
|
6646
6736
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6655,8 +6745,8 @@ async function runIngest(input) {
|
|
|
6655
6745
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
6656
6746
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
6657
6747
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
6658
|
-
const rawAbsPath =
|
|
6659
|
-
const typedAbsPath =
|
|
6748
|
+
const rawAbsPath = join35(input.vault, rawRelPath);
|
|
6749
|
+
const typedAbsPath = join35(input.vault, typedRelPath);
|
|
6660
6750
|
const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
|
|
6661
6751
|
const typedContent = buildTypedContent(
|
|
6662
6752
|
input.title,
|
|
@@ -6719,7 +6809,7 @@ async function runIngest(input) {
|
|
|
6719
6809
|
};
|
|
6720
6810
|
}
|
|
6721
6811
|
try {
|
|
6722
|
-
await
|
|
6812
|
+
await mkdir13(join35(input.vault, "raw", "articles"), { recursive: true });
|
|
6723
6813
|
await writeFile14(rawAbsPath, rawContent, "utf8");
|
|
6724
6814
|
} catch (e) {
|
|
6725
6815
|
return {
|
|
@@ -6728,7 +6818,7 @@ async function runIngest(input) {
|
|
|
6728
6818
|
};
|
|
6729
6819
|
}
|
|
6730
6820
|
try {
|
|
6731
|
-
await
|
|
6821
|
+
await mkdir13(join35(input.vault, typedDir), { recursive: true });
|
|
6732
6822
|
await writeFile14(typedAbsPath, typedContent, "utf8");
|
|
6733
6823
|
} catch (e) {
|
|
6734
6824
|
return {
|
|
@@ -6907,12 +6997,12 @@ ${body}`;
|
|
|
6907
6997
|
}
|
|
6908
6998
|
|
|
6909
6999
|
// src/commands/sync.ts
|
|
6910
|
-
import { existsSync as
|
|
6911
|
-
import { join as
|
|
7000
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7001
|
+
import { join as join37 } from "path";
|
|
6912
7002
|
|
|
6913
7003
|
// src/utils/sync-lock.ts
|
|
6914
|
-
import { existsSync as
|
|
6915
|
-
import { join as
|
|
7004
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, renameSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
7005
|
+
import { join as join36 } from "path";
|
|
6916
7006
|
import { createHash as createHash6 } from "crypto";
|
|
6917
7007
|
function getSessionId() {
|
|
6918
7008
|
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
@@ -6920,11 +7010,11 @@ function getSessionId() {
|
|
|
6920
7010
|
return process.pid.toString();
|
|
6921
7011
|
}
|
|
6922
7012
|
function lockPath(vault) {
|
|
6923
|
-
return
|
|
7013
|
+
return join36(vault, ".skillwiki", "sync.lock");
|
|
6924
7014
|
}
|
|
6925
7015
|
function readLock(vault) {
|
|
6926
7016
|
const path = lockPath(vault);
|
|
6927
|
-
if (!
|
|
7017
|
+
if (!existsSync13(path)) return null;
|
|
6928
7018
|
try {
|
|
6929
7019
|
const raw = readFileSync10(path, "utf8");
|
|
6930
7020
|
return JSON.parse(raw);
|
|
@@ -6939,8 +7029,8 @@ function isStale(lock, now) {
|
|
|
6939
7029
|
}
|
|
6940
7030
|
function acquireLock(vault, opts = {}) {
|
|
6941
7031
|
const path = lockPath(vault);
|
|
6942
|
-
const dir =
|
|
6943
|
-
if (!
|
|
7032
|
+
const dir = join36(vault, ".skillwiki");
|
|
7033
|
+
if (!existsSync13(dir)) {
|
|
6944
7034
|
mkdirSync4(dir, { recursive: true });
|
|
6945
7035
|
}
|
|
6946
7036
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -6985,7 +7075,7 @@ function writeLockedFile(path, lock) {
|
|
|
6985
7075
|
}
|
|
6986
7076
|
function releaseLock(vault, opts = {}) {
|
|
6987
7077
|
const path = lockPath(vault);
|
|
6988
|
-
if (!
|
|
7078
|
+
if (!existsSync13(path)) {
|
|
6989
7079
|
return { released: false };
|
|
6990
7080
|
}
|
|
6991
7081
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -7014,7 +7104,7 @@ function releaseLock(vault, opts = {}) {
|
|
|
7014
7104
|
function runSyncStatus(input) {
|
|
7015
7105
|
const vault = input.vault;
|
|
7016
7106
|
const includeStashes = input.includeStashes ?? false;
|
|
7017
|
-
if (!
|
|
7107
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7018
7108
|
return {
|
|
7019
7109
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7020
7110
|
result: ok({
|
|
@@ -7091,12 +7181,23 @@ function runSyncStatus(input) {
|
|
|
7091
7181
|
}
|
|
7092
7182
|
async function runSyncPush(input) {
|
|
7093
7183
|
const vault = input.vault;
|
|
7094
|
-
if (!
|
|
7184
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7095
7185
|
return {
|
|
7096
7186
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7097
7187
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
7098
7188
|
};
|
|
7099
7189
|
}
|
|
7190
|
+
let pathFixes = 0;
|
|
7191
|
+
const pathFix = await fixPathTooLong({ vault });
|
|
7192
|
+
if (pathFix.result.ok && pathFix.result.data.fixed.length > 0) {
|
|
7193
|
+
pathFixes = pathFix.result.data.fixed.length;
|
|
7194
|
+
appendLastOp(vault, {
|
|
7195
|
+
operation: "lint-fix",
|
|
7196
|
+
summary: `fixed ${pathFixes} long path(s)`,
|
|
7197
|
+
files: pathFix.result.data.fixed.flatMap((f) => [f.from, f.to]),
|
|
7198
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7199
|
+
});
|
|
7200
|
+
}
|
|
7100
7201
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7101
7202
|
const dirtyFiles = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0) : [];
|
|
7102
7203
|
if (dirtyFiles.length === 0) {
|
|
@@ -7106,6 +7207,7 @@ async function runSyncPush(input) {
|
|
|
7106
7207
|
files_committed: 0,
|
|
7107
7208
|
commit_message: "",
|
|
7108
7209
|
pushed: false,
|
|
7210
|
+
path_fixes: pathFixes,
|
|
7109
7211
|
humanHint: "nothing to commit, working tree clean"
|
|
7110
7212
|
})
|
|
7111
7213
|
};
|
|
@@ -7160,7 +7262,8 @@ async function runSyncPush(input) {
|
|
|
7160
7262
|
files_committed: dirtyFiles.length,
|
|
7161
7263
|
commit_message: commitMessage,
|
|
7162
7264
|
pushed: false,
|
|
7163
|
-
|
|
7265
|
+
path_fixes: pathFixes,
|
|
7266
|
+
humanHint: `committed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""} but push failed: ${String(e)}`
|
|
7164
7267
|
})
|
|
7165
7268
|
};
|
|
7166
7269
|
}
|
|
@@ -7170,7 +7273,8 @@ async function runSyncPush(input) {
|
|
|
7170
7273
|
files_committed: dirtyFiles.length,
|
|
7171
7274
|
commit_message: commitMessage,
|
|
7172
7275
|
pushed,
|
|
7173
|
-
|
|
7276
|
+
path_fixes: pathFixes,
|
|
7277
|
+
humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}`
|
|
7174
7278
|
})
|
|
7175
7279
|
};
|
|
7176
7280
|
}
|
|
@@ -7195,7 +7299,7 @@ function enumerateStashes(vault) {
|
|
|
7195
7299
|
}
|
|
7196
7300
|
async function runSyncPull(input) {
|
|
7197
7301
|
const vault = input.vault;
|
|
7198
|
-
if (!
|
|
7302
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7199
7303
|
return {
|
|
7200
7304
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7201
7305
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -7289,6 +7393,8 @@ async function runSyncPull(input) {
|
|
|
7289
7393
|
};
|
|
7290
7394
|
}
|
|
7291
7395
|
}
|
|
7396
|
+
const pathFix = await fixPathTooLong({ vault });
|
|
7397
|
+
const pathFixCount = pathFix.result.ok ? pathFix.result.data.fixed.length : 0;
|
|
7292
7398
|
let lintErrors = 0;
|
|
7293
7399
|
let lintWarnings = 0;
|
|
7294
7400
|
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
@@ -7300,6 +7406,7 @@ async function runSyncPull(input) {
|
|
|
7300
7406
|
if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
|
|
7301
7407
|
else hintParts.push("already up to date");
|
|
7302
7408
|
if (autoResolved > 0) hintParts.push(`${autoResolved} conflict(s) auto-resolved`);
|
|
7409
|
+
if (pathFixCount > 0) hintParts.push(`${pathFixCount} long path(s) fixed`);
|
|
7303
7410
|
if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
|
|
7304
7411
|
if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
|
|
7305
7412
|
const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
@@ -7363,7 +7470,7 @@ function runSyncPeers(input) {
|
|
|
7363
7470
|
}
|
|
7364
7471
|
function runSyncLock(input) {
|
|
7365
7472
|
const vault = input.vault;
|
|
7366
|
-
if (!
|
|
7473
|
+
if (!existsSync14(vault)) {
|
|
7367
7474
|
return {
|
|
7368
7475
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7369
7476
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7398,7 +7505,7 @@ function runSyncLock(input) {
|
|
|
7398
7505
|
}
|
|
7399
7506
|
function runSyncUnlock(input) {
|
|
7400
7507
|
const vault = input.vault;
|
|
7401
|
-
if (!
|
|
7508
|
+
if (!existsSync14(vault)) {
|
|
7402
7509
|
return {
|
|
7403
7510
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7404
7511
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7432,7 +7539,7 @@ function runSyncUnlock(input) {
|
|
|
7432
7539
|
|
|
7433
7540
|
// src/commands/backup.ts
|
|
7434
7541
|
import { statSync as statSync5, readdirSync as readdirSync2, readFileSync as readFileSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
7435
|
-
import { join as
|
|
7542
|
+
import { join as join38, relative as relative3, dirname as dirname12 } from "path";
|
|
7436
7543
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
7437
7544
|
|
|
7438
7545
|
// src/utils/s3-client.ts
|
|
@@ -7452,11 +7559,11 @@ function createS3Client(config) {
|
|
|
7452
7559
|
}
|
|
7453
7560
|
|
|
7454
7561
|
// src/commands/backup.ts
|
|
7455
|
-
var
|
|
7562
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_modules", ".skillwiki"]);
|
|
7456
7563
|
function* walkMarkdown(dir, base) {
|
|
7457
7564
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
7458
|
-
if (
|
|
7459
|
-
const full =
|
|
7565
|
+
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
7566
|
+
const full = join38(dir, entry.name);
|
|
7460
7567
|
if (entry.isDirectory()) {
|
|
7461
7568
|
yield* walkMarkdown(full, base);
|
|
7462
7569
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7479,7 +7586,7 @@ async function runBackupSync(input) {
|
|
|
7479
7586
|
let failed = 0;
|
|
7480
7587
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
7481
7588
|
for (const relPath of files) {
|
|
7482
|
-
const absPath =
|
|
7589
|
+
const absPath = join38(input.vault, relPath);
|
|
7483
7590
|
const localStat = statSync5(absPath);
|
|
7484
7591
|
let needsUpload = true;
|
|
7485
7592
|
try {
|
|
@@ -7555,7 +7662,7 @@ async function runBackupRestore(input) {
|
|
|
7555
7662
|
const objects = list.Contents ?? [];
|
|
7556
7663
|
for (const obj of objects) {
|
|
7557
7664
|
if (!obj.Key) continue;
|
|
7558
|
-
const localPath =
|
|
7665
|
+
const localPath = join38(target, obj.Key);
|
|
7559
7666
|
try {
|
|
7560
7667
|
const localStat = statSync5(localPath);
|
|
7561
7668
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -7568,7 +7675,7 @@ async function runBackupRestore(input) {
|
|
|
7568
7675
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
7569
7676
|
const body = await resp.Body?.transformToByteArray();
|
|
7570
7677
|
if (body) {
|
|
7571
|
-
mkdirSync5(
|
|
7678
|
+
mkdirSync5(dirname12(localPath), { recursive: true });
|
|
7572
7679
|
writeFileSync7(localPath, Buffer.from(body));
|
|
7573
7680
|
downloaded++;
|
|
7574
7681
|
}
|
|
@@ -7601,11 +7708,11 @@ async function runBackupRestore(input) {
|
|
|
7601
7708
|
}
|
|
7602
7709
|
|
|
7603
7710
|
// src/commands/status.ts
|
|
7604
|
-
import { existsSync as
|
|
7605
|
-
import { readFile as
|
|
7606
|
-
import { join as
|
|
7711
|
+
import { existsSync as existsSync15, statSync as statSync6 } from "fs";
|
|
7712
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
7713
|
+
import { join as join39 } from "path";
|
|
7607
7714
|
async function runStatus(input) {
|
|
7608
|
-
if (!
|
|
7715
|
+
if (!existsSync15(input.vault)) {
|
|
7609
7716
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
7610
7717
|
}
|
|
7611
7718
|
const scan = await scanVault(input.vault);
|
|
@@ -7630,7 +7737,7 @@ async function runStatus(input) {
|
|
|
7630
7737
|
const compound = scan.data.compound.length;
|
|
7631
7738
|
let schemaVersion = "v1";
|
|
7632
7739
|
try {
|
|
7633
|
-
const schemaContent = await
|
|
7740
|
+
const schemaContent = await readFile25(join39(input.vault, "SCHEMA.md"), "utf8");
|
|
7634
7741
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
7635
7742
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
7636
7743
|
} catch {
|
|
@@ -7690,8 +7797,8 @@ async function runStatus(input) {
|
|
|
7690
7797
|
}
|
|
7691
7798
|
|
|
7692
7799
|
// src/commands/seed.ts
|
|
7693
|
-
import { mkdir as
|
|
7694
|
-
import { join as
|
|
7800
|
+
import { mkdir as mkdir14, writeFile as writeFile15, stat as stat8 } from "fs/promises";
|
|
7801
|
+
import { join as join40 } from "path";
|
|
7695
7802
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7696
7803
|
var EXAMPLE_PAGES = {
|
|
7697
7804
|
"entities/example-project.md": `---
|
|
@@ -7760,29 +7867,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
7760
7867
|
`;
|
|
7761
7868
|
async function runSeed(input) {
|
|
7762
7869
|
try {
|
|
7763
|
-
await stat8(
|
|
7870
|
+
await stat8(join40(input.vault, "SCHEMA.md"));
|
|
7764
7871
|
} catch {
|
|
7765
7872
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
7766
7873
|
}
|
|
7767
7874
|
const created = [];
|
|
7768
7875
|
const skipped = [];
|
|
7769
7876
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
7770
|
-
const absPath =
|
|
7877
|
+
const absPath = join40(input.vault, relPath);
|
|
7771
7878
|
try {
|
|
7772
7879
|
await stat8(absPath);
|
|
7773
7880
|
skipped.push(relPath);
|
|
7774
7881
|
} catch {
|
|
7775
|
-
await
|
|
7882
|
+
await mkdir14(join40(absPath, ".."), { recursive: true });
|
|
7776
7883
|
await writeFile15(absPath, content, "utf8");
|
|
7777
7884
|
created.push(relPath);
|
|
7778
7885
|
}
|
|
7779
7886
|
}
|
|
7780
|
-
const rawPath =
|
|
7887
|
+
const rawPath = join40(input.vault, "raw", "articles", "example-source.md");
|
|
7781
7888
|
try {
|
|
7782
7889
|
await stat8(rawPath);
|
|
7783
7890
|
skipped.push("raw/articles/example-source.md");
|
|
7784
7891
|
} catch {
|
|
7785
|
-
await
|
|
7892
|
+
await mkdir14(join40(rawPath, ".."), { recursive: true });
|
|
7786
7893
|
await writeFile15(rawPath, EXAMPLE_RAW, "utf8");
|
|
7787
7894
|
created.push("raw/articles/example-source.md");
|
|
7788
7895
|
}
|
|
@@ -7805,9 +7912,9 @@ async function runSeed(input) {
|
|
|
7805
7912
|
}
|
|
7806
7913
|
|
|
7807
7914
|
// src/commands/canvas.ts
|
|
7808
|
-
import { readFile as
|
|
7809
|
-
import { existsSync as
|
|
7810
|
-
import { join as
|
|
7915
|
+
import { readFile as readFile26, writeFile as writeFile16 } from "fs/promises";
|
|
7916
|
+
import { existsSync as existsSync16 } from "fs";
|
|
7917
|
+
import { join as join41 } from "path";
|
|
7811
7918
|
var NODE_WIDTH = 240;
|
|
7812
7919
|
var NODE_HEIGHT = 60;
|
|
7813
7920
|
var COLUMN_SPACING = 400;
|
|
@@ -7885,8 +7992,8 @@ function buildCanvasEdges(adjacency) {
|
|
|
7885
7992
|
return edges;
|
|
7886
7993
|
}
|
|
7887
7994
|
async function runCanvasGenerate(input) {
|
|
7888
|
-
const graphPath = input.graphPath ??
|
|
7889
|
-
if (!
|
|
7995
|
+
const graphPath = input.graphPath ?? join41(input.vault, ".skillwiki", "graph.json");
|
|
7996
|
+
if (!existsSync16(graphPath)) {
|
|
7890
7997
|
return {
|
|
7891
7998
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
7892
7999
|
result: err("FILE_NOT_FOUND", {
|
|
@@ -7897,7 +8004,7 @@ async function runCanvasGenerate(input) {
|
|
|
7897
8004
|
}
|
|
7898
8005
|
let raw;
|
|
7899
8006
|
try {
|
|
7900
|
-
raw = await
|
|
8007
|
+
raw = await readFile26(graphPath, "utf8");
|
|
7901
8008
|
} catch (e) {
|
|
7902
8009
|
return {
|
|
7903
8010
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -7923,7 +8030,7 @@ async function runCanvasGenerate(input) {
|
|
|
7923
8030
|
const nodes = buildCanvasNodes(paths);
|
|
7924
8031
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
7925
8032
|
const canvas = { nodes, edges };
|
|
7926
|
-
const outPath =
|
|
8033
|
+
const outPath = join41(input.vault, "vault-graph.canvas");
|
|
7927
8034
|
try {
|
|
7928
8035
|
await writeFile16(outPath, JSON.stringify(canvas, null, 2));
|
|
7929
8036
|
} catch (e) {
|
|
@@ -7945,8 +8052,8 @@ written: ${outPath}`
|
|
|
7945
8052
|
}
|
|
7946
8053
|
|
|
7947
8054
|
// src/commands/query.ts
|
|
7948
|
-
import { readFile as
|
|
7949
|
-
import { join as
|
|
8055
|
+
import { readFile as readFile27, stat as stat9 } from "fs/promises";
|
|
8056
|
+
import { join as join42 } from "path";
|
|
7950
8057
|
var W_KEYWORD = 2;
|
|
7951
8058
|
var W_SOURCE_OVERLAP = 4;
|
|
7952
8059
|
var W_WIKILINK = 3;
|
|
@@ -8067,7 +8174,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8067
8174
|
return score;
|
|
8068
8175
|
}
|
|
8069
8176
|
async function loadOrBuildGraph(vault) {
|
|
8070
|
-
const graphPath =
|
|
8177
|
+
const graphPath = join42(vault, ".skillwiki", "graph.json");
|
|
8071
8178
|
let needsBuild = false;
|
|
8072
8179
|
try {
|
|
8073
8180
|
const fileStat = await stat9(graphPath);
|
|
@@ -8081,7 +8188,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
8081
8188
|
if (buildResult.exitCode !== 0) return null;
|
|
8082
8189
|
}
|
|
8083
8190
|
try {
|
|
8084
|
-
const raw = await
|
|
8191
|
+
const raw = await readFile27(graphPath, "utf8");
|
|
8085
8192
|
return JSON.parse(raw);
|
|
8086
8193
|
} catch {
|
|
8087
8194
|
return null;
|
|
@@ -8089,14 +8196,14 @@ async function loadOrBuildGraph(vault) {
|
|
|
8089
8196
|
}
|
|
8090
8197
|
|
|
8091
8198
|
// src/utils/auto-commit.ts
|
|
8092
|
-
import { existsSync as
|
|
8093
|
-
import { join as
|
|
8199
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8200
|
+
import { join as join43 } from "path";
|
|
8094
8201
|
async function postCommit(vault, exitCode) {
|
|
8095
8202
|
if (exitCode !== 0) return;
|
|
8096
8203
|
const home = process.env.HOME ?? "";
|
|
8097
8204
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
8098
8205
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
8099
|
-
if (!
|
|
8206
|
+
if (!existsSync17(join43(vault, ".git"))) return;
|
|
8100
8207
|
const lastOps = readLastOp(vault);
|
|
8101
8208
|
if (lastOps.length === 0) return;
|
|
8102
8209
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -8147,7 +8254,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
8147
8254
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
8148
8255
|
});
|
|
8149
8256
|
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) => {
|
|
8150
|
-
const out = opts.out ??
|
|
8257
|
+
const out = opts.out ?? join44(vault, ".skillwiki", "graph.json");
|
|
8151
8258
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
8152
8259
|
});
|
|
8153
8260
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|