skillwiki 0.8.1-beta.8 → 0.8.1
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 +380 -257
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +2 -1
- package/skills/README.md +10 -0
- package/skills/hooks/hooks-codex.json +16 -0
- package/skills/hooks/session-context +112 -0
- package/skills/hooks/session-start +6 -20
- package/skills/hooks/session-start-codex +15 -0
- 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,100 @@ 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, resolve as resolve4 } from "path";
|
|
2840
2847
|
var MAX_PATH_LENGTH = 240;
|
|
2848
|
+
var WINDOWS_ABSOLUTE_PATH_LIMIT = 259;
|
|
2841
2849
|
async function runPathTooLong(input) {
|
|
2842
2850
|
const scan = await scanVault(input.vault);
|
|
2843
2851
|
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
|
-
}
|
|
2852
|
+
const violations = findPathTooLongViolations(scan.data.allMarkdown, MAX_PATH_LENGTH);
|
|
2851
2853
|
if (violations.length > 0) {
|
|
2852
2854
|
return {
|
|
2853
2855
|
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
2854
2856
|
result: ok({
|
|
2855
2857
|
violations,
|
|
2856
|
-
humanHint: violations.map((v) => `${v.relPath}: ${v.length} chars (max ${MAX_PATH_LENGTH})`).join("\n")
|
|
2858
|
+
humanHint: violations.map((v) => `${v.relPath}: ${v.length} chars (max ${MAX_PATH_LENGTH}) -> ${v.suggestedRelPath}`).join("\n")
|
|
2857
2859
|
})
|
|
2858
2860
|
};
|
|
2859
2861
|
}
|
|
2860
2862
|
return { exitCode: ExitCode.OK, result: ok({ violations, humanHint: "all paths within length limit" }) };
|
|
2861
2863
|
}
|
|
2864
|
+
async function fixPathTooLong(input) {
|
|
2865
|
+
const scan = await scanVault(input.vault);
|
|
2866
|
+
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2867
|
+
const maxFixLength = maxFixPathLength(input.vault);
|
|
2868
|
+
const violations = findPathTooLongViolations(scan.data.allMarkdown, maxFixLength);
|
|
2869
|
+
const fixed = [];
|
|
2870
|
+
const unresolved = [];
|
|
2871
|
+
for (const violation of violations) {
|
|
2872
|
+
const target = await resolveFixTarget(input.vault, violation.relPath, violation.suggestedRelPath, maxFixLength);
|
|
2873
|
+
if (!target || target.relPath === violation.relPath || target.relPath.length > maxFixLength) {
|
|
2874
|
+
unresolved.push(violation.relPath);
|
|
2875
|
+
continue;
|
|
2876
|
+
}
|
|
2877
|
+
try {
|
|
2878
|
+
if (target.mode === "dedupe") {
|
|
2879
|
+
await unlink3(join21(input.vault, violation.relPath));
|
|
2880
|
+
} else {
|
|
2881
|
+
await mkdir8(dirname8(join21(input.vault, target.relPath)), { recursive: true });
|
|
2882
|
+
await rename6(join21(input.vault, violation.relPath), join21(input.vault, target.relPath));
|
|
2883
|
+
}
|
|
2884
|
+
fixed.push({ from: violation.relPath, to: target.relPath });
|
|
2885
|
+
} catch {
|
|
2886
|
+
unresolved.push(violation.relPath);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
const rewired = [];
|
|
2890
|
+
if (fixed.length > 0) {
|
|
2891
|
+
const afterScan = await scanVault(input.vault);
|
|
2892
|
+
if (afterScan.ok) {
|
|
2893
|
+
for (const page of afterScan.data.allMarkdown) {
|
|
2894
|
+
if (!shouldRewriteReferences(page.relPath)) continue;
|
|
2895
|
+
try {
|
|
2896
|
+
const original = await readFile16(page.absPath, "utf8");
|
|
2897
|
+
let updated = original;
|
|
2898
|
+
for (const fix of fixed) {
|
|
2899
|
+
updated = replacePathReferences(updated, fix.from, fix.to);
|
|
2900
|
+
}
|
|
2901
|
+
if (updated !== original) {
|
|
2902
|
+
const write = await safeWritePage(page.absPath, updated);
|
|
2903
|
+
if (write.ok) rewired.push(page.relPath);
|
|
2904
|
+
else unresolved.push(`${page.relPath} (rewire)`);
|
|
2905
|
+
}
|
|
2906
|
+
} catch {
|
|
2907
|
+
unresolved.push(`${page.relPath} (rewire)`);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
const hintLines = [
|
|
2913
|
+
`fixed: ${fixed.length}`,
|
|
2914
|
+
`rewired: ${rewired.length}`,
|
|
2915
|
+
`unresolved: ${unresolved.length}`
|
|
2916
|
+
];
|
|
2917
|
+
for (const f of fixed) hintLines.push(` ${f.from} -> ${f.to}`);
|
|
2918
|
+
for (const u of unresolved) hintLines.push(` unresolved: ${u}`);
|
|
2919
|
+
return {
|
|
2920
|
+
exitCode: unresolved.length > 0 ? ExitCode.LINT_HAS_ERRORS : ExitCode.OK,
|
|
2921
|
+
result: ok({ fixed, unresolved, rewired, humanHint: hintLines.join("\n") })
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
function findPathTooLongViolations(pages, maxLength) {
|
|
2925
|
+
return pages.filter((page) => page.relPath.length > maxLength).map((page) => ({
|
|
2926
|
+
relPath: page.relPath,
|
|
2927
|
+
length: page.relPath.length,
|
|
2928
|
+
suggestedRelPath: truncateFilename(page.relPath, maxLength)
|
|
2929
|
+
}));
|
|
2930
|
+
}
|
|
2931
|
+
function maxFixPathLength(vault) {
|
|
2932
|
+
if (process.platform !== "win32") return MAX_PATH_LENGTH;
|
|
2933
|
+
const root = resolve4(vault);
|
|
2934
|
+
const separatorBudget = root.endsWith("\\") || root.endsWith("/") ? 0 : 1;
|
|
2935
|
+
const absoluteSafeRelLength = WINDOWS_ABSOLUTE_PATH_LIMIT - root.length - separatorBudget;
|
|
2936
|
+
return Math.max(1, Math.min(MAX_PATH_LENGTH, absoluteSafeRelLength));
|
|
2937
|
+
}
|
|
2862
2938
|
function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
2863
2939
|
if (relPath.length <= maxLength) return relPath;
|
|
2864
2940
|
const lastSlash = relPath.lastIndexOf("/");
|
|
@@ -2872,15 +2948,62 @@ function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
|
2872
2948
|
const maxPrefixLen = maxLength - dirPrefix.length - suffix.length;
|
|
2873
2949
|
if (maxPrefixLen <= 0) {
|
|
2874
2950
|
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;
|
|
2951
|
+
return fallback.length <= maxLength ? fallback : relPath;
|
|
2880
2952
|
}
|
|
2881
2953
|
const prefix = base.slice(0, maxPrefixLen).replace(/[-_\s]+$/, "");
|
|
2882
2954
|
return dirPrefix + prefix + suffix;
|
|
2883
2955
|
}
|
|
2956
|
+
async function resolveFixTarget(vault, original, preferred, maxLength) {
|
|
2957
|
+
for (const candidate of candidateRelPaths(preferred, maxLength)) {
|
|
2958
|
+
if (candidate === original || candidate.length > maxLength) continue;
|
|
2959
|
+
const candidatePath = join21(vault, candidate);
|
|
2960
|
+
if (!existsSync4(candidatePath)) return { relPath: candidate, mode: "rename" };
|
|
2961
|
+
if (await hasSameContent(join21(vault, original), candidatePath)) {
|
|
2962
|
+
return { relPath: candidate, mode: "dedupe" };
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return null;
|
|
2966
|
+
}
|
|
2967
|
+
function candidateRelPaths(preferred, maxLength) {
|
|
2968
|
+
const candidates = [preferred];
|
|
2969
|
+
if (preferred.length > maxLength) return candidates;
|
|
2970
|
+
const dir = posix.dirname(preferred) === "." ? "" : posix.dirname(preferred);
|
|
2971
|
+
const filename = posix.basename(preferred);
|
|
2972
|
+
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
2973
|
+
const base = ext ? filename.slice(0, -3) : filename;
|
|
2974
|
+
const dirPrefix = dir ? `${dir}/` : "";
|
|
2975
|
+
for (let i = 2; i < 100; i++) {
|
|
2976
|
+
const suffix = `-${i}${ext}`;
|
|
2977
|
+
const prefixBudget = maxLength - dirPrefix.length - suffix.length;
|
|
2978
|
+
if (prefixBudget <= 0) break;
|
|
2979
|
+
candidates.push(`${dirPrefix}${base.slice(0, prefixBudget).replace(/[-_\s]+$/, "")}${suffix}`);
|
|
2980
|
+
}
|
|
2981
|
+
return candidates;
|
|
2982
|
+
}
|
|
2983
|
+
async function hasSameContent(a, b) {
|
|
2984
|
+
try {
|
|
2985
|
+
const [left, right] = await Promise.all([readFile16(a), readFile16(b)]);
|
|
2986
|
+
return left.equals(right);
|
|
2987
|
+
} catch {
|
|
2988
|
+
return false;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
function shouldRewriteReferences(relPath) {
|
|
2992
|
+
if (relPath.startsWith("raw/")) return false;
|
|
2993
|
+
if (relPath.startsWith("_archive/")) return false;
|
|
2994
|
+
return true;
|
|
2995
|
+
}
|
|
2996
|
+
function replacePathReferences(content, oldRelPath, newRelPath) {
|
|
2997
|
+
let updated = content.replaceAll(oldRelPath, newRelPath);
|
|
2998
|
+
const oldStem = posix.basename(oldRelPath).replace(/\.md$/, "");
|
|
2999
|
+
const newStem = posix.basename(newRelPath).replace(/\.md$/, "");
|
|
3000
|
+
if (oldStem !== newStem) {
|
|
3001
|
+
const oldStemEscaped = oldStem.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3002
|
+
const stemWikilinkRe = new RegExp(`\\[\\[${oldStemEscaped}(\\|[^\\]]*)?\\]\\]`, "g");
|
|
3003
|
+
updated = updated.replace(stemWikilinkRe, (_match, alias) => `[[${newStem}${alias ?? ""}]]`);
|
|
3004
|
+
}
|
|
3005
|
+
return updated;
|
|
3006
|
+
}
|
|
2884
3007
|
function computeShortHash(input) {
|
|
2885
3008
|
let hash = 2166136261;
|
|
2886
3009
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -3069,7 +3192,15 @@ function extractSourceEntries(rawFm) {
|
|
|
3069
3192
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
|
|
3070
3193
|
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
3194
|
var INFO_ORDER = ["bridges", "sparse_community", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
|
|
3195
|
+
var KNOWN_BUCKETS = [...ERROR_ORDER, ...WARNING_ORDER, ...INFO_ORDER];
|
|
3072
3196
|
async function runLint(input) {
|
|
3197
|
+
if (input.only && !KNOWN_BUCKETS.includes(input.only)) {
|
|
3198
|
+
return {
|
|
3199
|
+
exitCode: ExitCode.USAGE,
|
|
3200
|
+
result: { ok: false, error: "UNKNOWN_BUCKET", detail: `Unknown bucket "${input.only}". Valid: ${KNOWN_BUCKETS.join(", ")}` }
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
const shouldFix = (bucket) => !!input.fix && (!input.only || input.only === bucket);
|
|
3073
3204
|
const buckets = {};
|
|
3074
3205
|
const fixed = [];
|
|
3075
3206
|
const unresolved = [];
|
|
@@ -3193,7 +3324,7 @@ async function runLint(input) {
|
|
|
3193
3324
|
let rawPath = entry.replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
3194
3325
|
rawPath = rawPath.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
3195
3326
|
if (!rawPath.startsWith("raw/") && !rawPath.startsWith("_archive/raw/")) continue;
|
|
3196
|
-
if (!
|
|
3327
|
+
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
3328
|
brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
|
|
3198
3329
|
}
|
|
3199
3330
|
}
|
|
@@ -3284,11 +3415,11 @@ async function runLint(input) {
|
|
|
3284
3415
|
const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
|
|
3285
3416
|
if (!slugMatch) continue;
|
|
3286
3417
|
const slug = slugMatch[1];
|
|
3287
|
-
const knowledgePath =
|
|
3288
|
-
if (!
|
|
3418
|
+
const knowledgePath = join22(input.vault, "projects", slug, "knowledge.md");
|
|
3419
|
+
if (!existsSync5(knowledgePath)) continue;
|
|
3289
3420
|
const pageRef = page.relPath.replace(/\.md$/, "");
|
|
3290
3421
|
try {
|
|
3291
|
-
const knowledgeContent = await
|
|
3422
|
+
const knowledgeContent = await readFile17(knowledgePath, "utf8");
|
|
3292
3423
|
if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
|
|
3293
3424
|
orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
|
|
3294
3425
|
}
|
|
@@ -3329,13 +3460,13 @@ async function runLint(input) {
|
|
|
3329
3460
|
}
|
|
3330
3461
|
}
|
|
3331
3462
|
if (staleSectionFlags.length > 0) buckets.stale_sections = staleSectionFlags;
|
|
3332
|
-
if (
|
|
3463
|
+
if (shouldFix("legacy_citation_style") && legacyPages.length > 0) {
|
|
3333
3464
|
const FENCE_RE2 = /```[\s\S]*?```/g;
|
|
3334
3465
|
const INLINE_MARKER = /\^\[raw\/[^\]]+\]/g;
|
|
3335
3466
|
for (const relPath of legacyPages) {
|
|
3336
3467
|
try {
|
|
3337
3468
|
const absPath = `${input.vault}/${relPath}`;
|
|
3338
|
-
const raw = await
|
|
3469
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3339
3470
|
const split = splitFrontmatter(raw);
|
|
3340
3471
|
if (!split.ok) {
|
|
3341
3472
|
unresolved.push(relPath);
|
|
@@ -3430,11 +3561,11 @@ ${newBody}`;
|
|
|
3430
3561
|
else delete buckets.legacy_citation_style;
|
|
3431
3562
|
}
|
|
3432
3563
|
}
|
|
3433
|
-
if (
|
|
3564
|
+
if (shouldFix("missing_overview") && noOverview.length > 0) {
|
|
3434
3565
|
for (const relPath of noOverview) {
|
|
3435
3566
|
try {
|
|
3436
3567
|
const absPath = `${input.vault}/${relPath}`;
|
|
3437
|
-
const raw = await
|
|
3568
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3438
3569
|
const split = splitFrontmatter(raw);
|
|
3439
3570
|
if (!split.ok) {
|
|
3440
3571
|
unresolved.push(relPath);
|
|
@@ -3471,11 +3602,11 @@ ${trimmedBody}`;
|
|
|
3471
3602
|
if (remaining.length > 0) buckets.missing_overview = remaining;
|
|
3472
3603
|
else delete buckets.missing_overview;
|
|
3473
3604
|
}
|
|
3474
|
-
if (
|
|
3605
|
+
if (shouldFix("missing_tldr") && missingTldrFlags.length > 0) {
|
|
3475
3606
|
for (const relPath of missingTldrFlags) {
|
|
3476
3607
|
try {
|
|
3477
3608
|
const absPath = `${input.vault}/${relPath}`;
|
|
3478
|
-
const raw = await
|
|
3609
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3479
3610
|
const split = splitFrontmatter(raw);
|
|
3480
3611
|
if (!split.ok) {
|
|
3481
3612
|
unresolved.push(relPath);
|
|
@@ -3518,14 +3649,14 @@ ${lines.join("\n")}`;
|
|
|
3518
3649
|
if (remaining.length > 0) buckets.missing_tldr = remaining;
|
|
3519
3650
|
else delete buckets.missing_tldr;
|
|
3520
3651
|
}
|
|
3521
|
-
if (
|
|
3652
|
+
if (shouldFix("wikilink_citation") && wikilinkCitationFlags.length > 0) {
|
|
3522
3653
|
const WIKILINK_RE = /\[\[raw\/([^\]|]+)(?:\|[^\]]*)?\]\]/g;
|
|
3523
3654
|
const FENCE_RE2 = /```[\s\S]*?```/g;
|
|
3524
3655
|
const wikilinkFixed = [];
|
|
3525
3656
|
for (const relPath of wikilinkCitationFlags) {
|
|
3526
3657
|
try {
|
|
3527
3658
|
const absPath = `${input.vault}/${relPath}`;
|
|
3528
|
-
const raw = await
|
|
3659
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3529
3660
|
const split = splitFrontmatter(raw);
|
|
3530
3661
|
if (!split.ok) {
|
|
3531
3662
|
unresolved.push(relPath);
|
|
@@ -3607,12 +3738,12 @@ ${newBody}`;
|
|
|
3607
3738
|
else delete buckets.wikilink_citation;
|
|
3608
3739
|
}
|
|
3609
3740
|
}
|
|
3610
|
-
if (
|
|
3741
|
+
if (shouldFix("file_source_url") && fileSourceUrlFlags.length > 0) {
|
|
3611
3742
|
const FILE_FIXED = [];
|
|
3612
3743
|
for (const relPath of fileSourceUrlFlags) {
|
|
3613
3744
|
try {
|
|
3614
3745
|
const absPath = `${input.vault}/${relPath}`;
|
|
3615
|
-
const raw = await
|
|
3746
|
+
const raw = await readFile17(absPath, "utf8");
|
|
3616
3747
|
const parts = raw.split("---", 3);
|
|
3617
3748
|
if (parts.length < 3) {
|
|
3618
3749
|
unresolved.push(relPath);
|
|
@@ -3647,36 +3778,11 @@ ${newBody}`;
|
|
|
3647
3778
|
}
|
|
3648
3779
|
}
|
|
3649
3780
|
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
|
-
}
|
|
3781
|
+
if (shouldFix("path_too_long") && pathViolations && pathViolations.length > 0) {
|
|
3782
|
+
const pathFix = await fixPathTooLong({ vault: input.vault });
|
|
3783
|
+
const pathFixed = pathFix.result.ok ? pathFix.result.data.fixed.map((f) => f.from) : [];
|
|
3784
|
+
if (pathFix.result.ok) unresolved.push(...pathFix.result.data.unresolved);
|
|
3785
|
+
else unresolved.push(...pathViolations.map((v) => v.relPath));
|
|
3680
3786
|
fixed.push(...pathFixed);
|
|
3681
3787
|
if (pathFixed.length > 0) {
|
|
3682
3788
|
const fixedSet = new Set(pathFixed);
|
|
@@ -3690,13 +3796,6 @@ ${newBody}`;
|
|
|
3690
3796
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
3691
3797
|
const infoOut = INFO_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
3692
3798
|
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
3799
|
const match = [...errorOut, ...warningOut, ...infoOut].filter((b) => b.kind === input.only);
|
|
3701
3800
|
const severity = ERROR_ORDER.includes(input.only) ? "error" : WARNING_ORDER.includes(input.only) ? "warning" : "info";
|
|
3702
3801
|
const filtered = severity === "error" ? { error: match, warning: [], info: [] } : severity === "warning" ? { error: [], warning: match, info: [] } : { error: [], warning: [], info: match };
|
|
@@ -3760,14 +3859,14 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
|
|
|
3760
3859
|
}
|
|
3761
3860
|
|
|
3762
3861
|
// src/commands/config.ts
|
|
3763
|
-
import { readFile as
|
|
3764
|
-
import { existsSync as
|
|
3765
|
-
import { join as
|
|
3862
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
3863
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3864
|
+
import { join as join23 } from "path";
|
|
3766
3865
|
function validateKey(key) {
|
|
3767
3866
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
3768
3867
|
}
|
|
3769
3868
|
function configPath(home) {
|
|
3770
|
-
return
|
|
3869
|
+
return join23(home, ".skillwiki", ".env");
|
|
3771
3870
|
}
|
|
3772
3871
|
async function runConfigGet(input) {
|
|
3773
3872
|
if (!validateKey(input.key)) {
|
|
@@ -3785,7 +3884,7 @@ async function runConfigSet(input) {
|
|
|
3785
3884
|
try {
|
|
3786
3885
|
let originalContent;
|
|
3787
3886
|
try {
|
|
3788
|
-
originalContent = await
|
|
3887
|
+
originalContent = await readFile18(filePath, "utf8");
|
|
3789
3888
|
} catch {
|
|
3790
3889
|
}
|
|
3791
3890
|
const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
|
|
@@ -3817,18 +3916,18 @@ async function runConfigList(input) {
|
|
|
3817
3916
|
}
|
|
3818
3917
|
async function runConfigPath(input) {
|
|
3819
3918
|
const filePath = configPath(input.home);
|
|
3820
|
-
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists:
|
|
3919
|
+
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync6(filePath), humanHint: filePath }) };
|
|
3821
3920
|
}
|
|
3822
3921
|
|
|
3823
3922
|
// src/commands/doctor.ts
|
|
3824
|
-
import { existsSync as
|
|
3825
|
-
import { join as
|
|
3923
|
+
import { existsSync as existsSync9, lstatSync, readlinkSync, readdirSync, statSync as statSync3, readFileSync as readFileSync7 } from "fs";
|
|
3924
|
+
import { join as join27, resolve as resolve5 } from "path";
|
|
3826
3925
|
import { execSync as execSync2 } from "child_process";
|
|
3827
3926
|
import { platform as platform2 } from "os";
|
|
3828
3927
|
|
|
3829
3928
|
// src/utils/auto-update.ts
|
|
3830
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as
|
|
3831
|
-
import { join as
|
|
3929
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
3930
|
+
import { join as join24, dirname as dirname9 } from "path";
|
|
3832
3931
|
import { spawn } from "child_process";
|
|
3833
3932
|
|
|
3834
3933
|
// src/utils/update-consts.ts
|
|
@@ -3839,7 +3938,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
|
|
|
3839
3938
|
|
|
3840
3939
|
// src/utils/auto-update.ts
|
|
3841
3940
|
function cachePath(home) {
|
|
3842
|
-
return
|
|
3941
|
+
return join24(home, ".skillwiki", CACHE_FILENAME);
|
|
3843
3942
|
}
|
|
3844
3943
|
function readCacheRaw(home) {
|
|
3845
3944
|
try {
|
|
@@ -3858,7 +3957,7 @@ function readCache(home) {
|
|
|
3858
3957
|
}
|
|
3859
3958
|
function writeCache(home, cache) {
|
|
3860
3959
|
const p = cachePath(home);
|
|
3861
|
-
mkdirSync3(
|
|
3960
|
+
mkdirSync3(dirname9(p), { recursive: true });
|
|
3862
3961
|
writeFileSync4(p, JSON.stringify(cache, null, 2));
|
|
3863
3962
|
}
|
|
3864
3963
|
function latestFromCache(home, currentVersion) {
|
|
@@ -3877,7 +3976,7 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
3877
3976
|
const { isStale: isStale2 } = readCache(home);
|
|
3878
3977
|
if (!isStale2) return;
|
|
3879
3978
|
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
3880
|
-
if (!
|
|
3979
|
+
if (!existsSync7(bgScript)) return;
|
|
3881
3980
|
const child = spawn(process.execPath, [bgScript, home, currentVersion], {
|
|
3882
3981
|
detached: true,
|
|
3883
3982
|
stdio: "ignore"
|
|
@@ -3889,12 +3988,12 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
3889
3988
|
|
|
3890
3989
|
// src/utils/plugin-registry.ts
|
|
3891
3990
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3892
|
-
import { join as
|
|
3893
|
-
var REGISTRY_PATH =
|
|
3991
|
+
import { join as join25 } from "path";
|
|
3992
|
+
var REGISTRY_PATH = join25(".claude", "plugins", "installed_plugins.json");
|
|
3894
3993
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
3895
3994
|
function readInstalledPlugins(home) {
|
|
3896
3995
|
try {
|
|
3897
|
-
const raw = readFileSync5(
|
|
3996
|
+
const raw = readFileSync5(join25(home, REGISTRY_PATH), "utf8");
|
|
3898
3997
|
return JSON.parse(raw);
|
|
3899
3998
|
} catch {
|
|
3900
3999
|
return null;
|
|
@@ -3911,8 +4010,8 @@ function findPlugin(home, key = PLUGIN_KEY) {
|
|
|
3911
4010
|
// src/utils/s3-mount-health.ts
|
|
3912
4011
|
import { execSync } from "child_process";
|
|
3913
4012
|
import { platform } from "os";
|
|
3914
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, readFileSync as
|
|
3915
|
-
import { join as
|
|
4013
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, readFileSync as readFile19 } from "fs";
|
|
4014
|
+
import { join as join26 } from "path";
|
|
3916
4015
|
var OS = platform();
|
|
3917
4016
|
function findRcloneMountPid() {
|
|
3918
4017
|
try {
|
|
@@ -4070,7 +4169,7 @@ function detectFuseMount(vaultPath) {
|
|
|
4070
4169
|
return null;
|
|
4071
4170
|
}
|
|
4072
4171
|
function writeTest(dir) {
|
|
4073
|
-
const testFile =
|
|
4172
|
+
const testFile = join26(dir, `.doctor-write-test-${process.pid}.tmp`);
|
|
4074
4173
|
const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
|
|
4075
4174
|
const start = Date.now();
|
|
4076
4175
|
try {
|
|
@@ -4081,7 +4180,7 @@ function writeTest(dir) {
|
|
|
4081
4180
|
const writeMs = Date.now() - start;
|
|
4082
4181
|
const readStart = Date.now();
|
|
4083
4182
|
try {
|
|
4084
|
-
const back =
|
|
4183
|
+
const back = readFile19(testFile, "utf8");
|
|
4085
4184
|
const readMs = Date.now() - readStart;
|
|
4086
4185
|
if (back !== payload) {
|
|
4087
4186
|
try {
|
|
@@ -4155,14 +4254,14 @@ function checkNodeVersion() {
|
|
|
4155
4254
|
function detectCliChannels(argv, home) {
|
|
4156
4255
|
const channels = [];
|
|
4157
4256
|
if (argv.length >= 2 && argv[1].endsWith("cli.js")) {
|
|
4158
|
-
const devPath =
|
|
4257
|
+
const devPath = resolve5(argv[1]);
|
|
4159
4258
|
channels.push({ name: "dev", path: devPath, isDevLink: true });
|
|
4160
4259
|
}
|
|
4161
4260
|
try {
|
|
4162
4261
|
const whichOut = execSync2("which skillwiki 2>/dev/null", { encoding: "utf8" }).trim();
|
|
4163
4262
|
if (whichOut) {
|
|
4164
4263
|
const isDev = isDevSymlink(whichOut);
|
|
4165
|
-
if (!channels.some((c) => c.path ===
|
|
4264
|
+
if (!channels.some((c) => c.path === resolve5(whichOut))) {
|
|
4166
4265
|
channels.push({ name: "npm", path: whichOut, isDevLink: isDev });
|
|
4167
4266
|
}
|
|
4168
4267
|
}
|
|
@@ -4170,13 +4269,13 @@ function detectCliChannels(argv, home) {
|
|
|
4170
4269
|
}
|
|
4171
4270
|
const plugin = findPlugin(home);
|
|
4172
4271
|
if (plugin) {
|
|
4173
|
-
const pluginBin =
|
|
4174
|
-
if (
|
|
4272
|
+
const pluginBin = join27(plugin.installPath, "bin", "skillwiki");
|
|
4273
|
+
if (existsSync9(pluginBin)) {
|
|
4175
4274
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
4176
4275
|
}
|
|
4177
4276
|
}
|
|
4178
|
-
const installBin =
|
|
4179
|
-
if (
|
|
4277
|
+
const installBin = join27(home, ".claude", "skills", "bin", "skillwiki");
|
|
4278
|
+
if (existsSync9(installBin)) {
|
|
4180
4279
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
4181
4280
|
}
|
|
4182
4281
|
return channels;
|
|
@@ -4185,7 +4284,7 @@ function isDevSymlink(binPath) {
|
|
|
4185
4284
|
try {
|
|
4186
4285
|
const st = lstatSync(binPath);
|
|
4187
4286
|
if (st.isSymbolicLink()) {
|
|
4188
|
-
const target =
|
|
4287
|
+
const target = resolve5(binPath, "..", readlinkSync(binPath));
|
|
4189
4288
|
return target.includes("packages/cli") || target.includes("packages\\cli");
|
|
4190
4289
|
}
|
|
4191
4290
|
} catch {
|
|
@@ -4228,7 +4327,7 @@ function checkCliChannels(argv, home) {
|
|
|
4228
4327
|
}
|
|
4229
4328
|
async function checkConfigFile(home) {
|
|
4230
4329
|
const cfgPath = configPath(home);
|
|
4231
|
-
if (!
|
|
4330
|
+
if (!existsSync9(cfgPath)) {
|
|
4232
4331
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
4233
4332
|
}
|
|
4234
4333
|
try {
|
|
@@ -4243,7 +4342,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
4243
4342
|
if (resolvedPath === void 0) {
|
|
4244
4343
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4245
4344
|
}
|
|
4246
|
-
if (
|
|
4345
|
+
if (existsSync9(resolvedPath) && statSync3(resolvedPath).isDirectory()) {
|
|
4247
4346
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
4248
4347
|
}
|
|
4249
4348
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -4252,13 +4351,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4252
4351
|
if (resolvedPath === void 0) {
|
|
4253
4352
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4254
4353
|
}
|
|
4255
|
-
if (!
|
|
4354
|
+
if (!existsSync9(resolvedPath)) {
|
|
4256
4355
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
4257
4356
|
}
|
|
4258
4357
|
const missing = [];
|
|
4259
|
-
if (!
|
|
4358
|
+
if (!existsSync9(join27(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
4260
4359
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
4261
|
-
if (!
|
|
4360
|
+
if (!existsSync9(join27(resolvedPath, dir))) missing.push(dir + "/");
|
|
4262
4361
|
}
|
|
4263
4362
|
if (missing.length === 0) {
|
|
4264
4363
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -4266,8 +4365,8 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4266
4365
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
4267
4366
|
}
|
|
4268
4367
|
function checkSkillsInstalled(home, cwd) {
|
|
4269
|
-
const srcDir = cwd ?
|
|
4270
|
-
if (srcDir &&
|
|
4368
|
+
const srcDir = cwd ? join27(cwd, "packages", "skills") : void 0;
|
|
4369
|
+
if (srcDir && existsSync9(srcDir)) {
|
|
4271
4370
|
const found = findInstalledSkillMd(srcDir);
|
|
4272
4371
|
if (found.length > 0) {
|
|
4273
4372
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
|
|
@@ -4280,8 +4379,8 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4280
4379
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
4281
4380
|
}
|
|
4282
4381
|
}
|
|
4283
|
-
const skillsDir =
|
|
4284
|
-
if (
|
|
4382
|
+
const skillsDir = join27(home, ".claude", "skills");
|
|
4383
|
+
if (existsSync9(skillsDir)) {
|
|
4285
4384
|
const found = findInstalledSkillMd(skillsDir);
|
|
4286
4385
|
if (found.length > 0) {
|
|
4287
4386
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
|
|
@@ -4291,10 +4390,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4291
4390
|
}
|
|
4292
4391
|
function checkDuplicateSkills(home) {
|
|
4293
4392
|
const plugin = findPlugin(home);
|
|
4294
|
-
const skillsDir =
|
|
4393
|
+
const skillsDir = join27(home, ".claude", "skills");
|
|
4295
4394
|
const agentSkillDirs = [
|
|
4296
|
-
{ label: "~/.codex/skills/", path:
|
|
4297
|
-
{ label: "~/.agents/skills/", path:
|
|
4395
|
+
{ label: "~/.codex/skills/", path: join27(home, ".codex", "skills") },
|
|
4396
|
+
{ label: "~/.agents/skills/", path: join27(home, ".agents", "skills") }
|
|
4298
4397
|
];
|
|
4299
4398
|
if (!plugin) {
|
|
4300
4399
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -4371,8 +4470,8 @@ async function checkProfiles(home) {
|
|
|
4371
4470
|
}
|
|
4372
4471
|
async function checkProjectLocalOverride(cwd) {
|
|
4373
4472
|
const dir = cwd ?? process.cwd();
|
|
4374
|
-
const envPath =
|
|
4375
|
-
if (
|
|
4473
|
+
const envPath = join27(dir, ".skillwiki", ".env");
|
|
4474
|
+
if (existsSync9(envPath)) {
|
|
4376
4475
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
4377
4476
|
}
|
|
4378
4477
|
return check("pass", "project_local", "Project-local config", "None");
|
|
@@ -4381,7 +4480,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
4381
4480
|
if (resolvedPath === void 0) {
|
|
4382
4481
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4383
4482
|
}
|
|
4384
|
-
if (!
|
|
4483
|
+
if (!existsSync9(join27(resolvedPath, ".git"))) {
|
|
4385
4484
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
4386
4485
|
}
|
|
4387
4486
|
try {
|
|
@@ -4404,9 +4503,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
4404
4503
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4405
4504
|
}
|
|
4406
4505
|
const missing = [];
|
|
4407
|
-
if (!
|
|
4408
|
-
if (!
|
|
4409
|
-
if (!
|
|
4506
|
+
if (!existsSync9(join27(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
4507
|
+
if (!existsSync9(join27(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
4508
|
+
if (!existsSync9(join27(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
4410
4509
|
if (missing.length === 0) {
|
|
4411
4510
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
4412
4511
|
}
|
|
@@ -4416,8 +4515,8 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4416
4515
|
if (resolvedPath === void 0) {
|
|
4417
4516
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4418
4517
|
}
|
|
4419
|
-
const rawDir =
|
|
4420
|
-
if (!
|
|
4518
|
+
const rawDir = join27(resolvedPath, "raw");
|
|
4519
|
+
if (!existsSync9(rawDir)) {
|
|
4421
4520
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
4422
4521
|
}
|
|
4423
4522
|
const found = [];
|
|
@@ -4432,7 +4531,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4432
4531
|
if (entry.name === ".DS_Store") {
|
|
4433
4532
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
4434
4533
|
} else if (entry.isDirectory()) {
|
|
4435
|
-
walk2(
|
|
4534
|
+
walk2(join27(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
4436
4535
|
}
|
|
4437
4536
|
}
|
|
4438
4537
|
})(rawDir, "");
|
|
@@ -4445,7 +4544,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
4445
4544
|
if (resolvedPath === void 0) {
|
|
4446
4545
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4447
4546
|
}
|
|
4448
|
-
if (!
|
|
4547
|
+
if (!existsSync9(join27(resolvedPath, ".git"))) {
|
|
4449
4548
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
4450
4549
|
}
|
|
4451
4550
|
let timestamp;
|
|
@@ -4486,8 +4585,8 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
4486
4585
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
4487
4586
|
}
|
|
4488
4587
|
const mountPoint = fuse.mountPoint;
|
|
4489
|
-
const conceptsDir =
|
|
4490
|
-
if (!
|
|
4588
|
+
const conceptsDir = join27(resolvedPath, "concepts");
|
|
4589
|
+
if (!existsSync9(conceptsDir)) {
|
|
4491
4590
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
4492
4591
|
}
|
|
4493
4592
|
const start = Date.now();
|
|
@@ -4669,8 +4768,8 @@ function checkWriteTest(resolvedPath) {
|
|
|
4669
4768
|
if (!fuse) {
|
|
4670
4769
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
4671
4770
|
}
|
|
4672
|
-
const conceptsDir =
|
|
4673
|
-
if (!
|
|
4771
|
+
const conceptsDir = join27(resolvedPath, "concepts");
|
|
4772
|
+
if (!existsSync9(conceptsDir)) {
|
|
4674
4773
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
4675
4774
|
}
|
|
4676
4775
|
const result = writeTest(conceptsDir);
|
|
@@ -4756,7 +4855,7 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
4756
4855
|
}
|
|
4757
4856
|
function readVaultSyncConfig(home) {
|
|
4758
4857
|
try {
|
|
4759
|
-
const content = readFileSync7(
|
|
4858
|
+
const content = readFileSync7(join27(home, ".skillwiki", ".env"), "utf8");
|
|
4760
4859
|
let installed = false;
|
|
4761
4860
|
let role;
|
|
4762
4861
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -4790,12 +4889,12 @@ function vaultSyncChecks(input) {
|
|
|
4790
4889
|
];
|
|
4791
4890
|
}
|
|
4792
4891
|
const isMac = os === "darwin";
|
|
4793
|
-
const logDir = input.logDir ?? (isMac ?
|
|
4794
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
4795
|
-
const filterPath = input.filterPath ??
|
|
4892
|
+
const logDir = input.logDir ?? (isMac ? join27(home, "Library", "Logs") : join27(home, ".local", "state", "vault-sync", "log"));
|
|
4893
|
+
const shareDir = input.shareDir ?? (isMac ? join27(home, "Library", "Application Support", "vault-sync", "bin") : join27(home, ".local", "share", "vault-sync", "bin"));
|
|
4894
|
+
const filterPath = input.filterPath ?? join27(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
4796
4895
|
const snapshotPath = input.snapshotScriptPath ?? "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
4797
|
-
const pushScriptPath =
|
|
4798
|
-
const c1 =
|
|
4896
|
+
const pushScriptPath = join27(shareDir, "wiki-push.sh");
|
|
4897
|
+
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
4898
|
let c2;
|
|
4800
4899
|
try {
|
|
4801
4900
|
if (isMac) {
|
|
@@ -4846,7 +4945,7 @@ function vaultSyncChecks(input) {
|
|
|
4846
4945
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
4847
4946
|
);
|
|
4848
4947
|
}
|
|
4849
|
-
const logFile =
|
|
4948
|
+
const logFile = join27(logDir, "wiki-push.log");
|
|
4850
4949
|
let c3;
|
|
4851
4950
|
try {
|
|
4852
4951
|
const logContent = readFileSync7(logFile, "utf8");
|
|
@@ -4905,7 +5004,7 @@ function vaultSyncChecks(input) {
|
|
|
4905
5004
|
}
|
|
4906
5005
|
}
|
|
4907
5006
|
} catch {
|
|
4908
|
-
c3 =
|
|
5007
|
+
c3 = existsSync9(logDir) ? check(
|
|
4909
5008
|
"warn",
|
|
4910
5009
|
"vault_sync_last_push_age",
|
|
4911
5010
|
"Vault sync last push recency",
|
|
@@ -4917,7 +5016,7 @@ function vaultSyncChecks(input) {
|
|
|
4917
5016
|
`Log directory not found at ${logDir}`
|
|
4918
5017
|
);
|
|
4919
5018
|
}
|
|
4920
|
-
const fetchLogFile =
|
|
5019
|
+
const fetchLogFile = join27(logDir, "wiki-fetch.log");
|
|
4921
5020
|
let cFetch;
|
|
4922
5021
|
try {
|
|
4923
5022
|
const logContent = readFileSync7(fetchLogFile, "utf8");
|
|
@@ -4964,7 +5063,7 @@ function vaultSyncChecks(input) {
|
|
|
4964
5063
|
}
|
|
4965
5064
|
let c4;
|
|
4966
5065
|
try {
|
|
4967
|
-
if (!
|
|
5066
|
+
if (!existsSync9(filterPath)) {
|
|
4968
5067
|
c4 = check(
|
|
4969
5068
|
"error",
|
|
4970
5069
|
"vault_sync_filter_present",
|
|
@@ -5013,7 +5112,7 @@ function vaultSyncChecks(input) {
|
|
|
5013
5112
|
);
|
|
5014
5113
|
} else {
|
|
5015
5114
|
try {
|
|
5016
|
-
if (!
|
|
5115
|
+
if (!existsSync9(snapshotPath)) {
|
|
5017
5116
|
c5 = check(
|
|
5018
5117
|
"error",
|
|
5019
5118
|
"vault_sync_snapshot_guard",
|
|
@@ -5059,15 +5158,15 @@ function findSkillMd(dir) {
|
|
|
5059
5158
|
}
|
|
5060
5159
|
for (const entry of entries) {
|
|
5061
5160
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
5062
|
-
results.push(
|
|
5161
|
+
results.push(join27(dir, entry.name));
|
|
5063
5162
|
} else if (entry.isDirectory()) {
|
|
5064
|
-
results.push(...findSkillMd(
|
|
5163
|
+
results.push(...findSkillMd(join27(dir, entry.name)));
|
|
5065
5164
|
}
|
|
5066
5165
|
}
|
|
5067
5166
|
return results;
|
|
5068
5167
|
}
|
|
5069
5168
|
function findInstalledSkillMd(dir) {
|
|
5070
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
5169
|
+
const directSkills = findSkillNames(dir).map((name) => join27(dir, name, "SKILL.md"));
|
|
5071
5170
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
5072
5171
|
}
|
|
5073
5172
|
function findSkillNames(dir) {
|
|
@@ -5079,7 +5178,7 @@ function findSkillNames(dir) {
|
|
|
5079
5178
|
return results;
|
|
5080
5179
|
}
|
|
5081
5180
|
for (const entry of entries) {
|
|
5082
|
-
if (entry.isDirectory() &&
|
|
5181
|
+
if (entry.isDirectory() && existsSync9(join27(dir, entry.name, "SKILL.md"))) {
|
|
5083
5182
|
results.push(entry.name);
|
|
5084
5183
|
}
|
|
5085
5184
|
}
|
|
@@ -5123,7 +5222,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
5123
5222
|
}
|
|
5124
5223
|
let logLines = 0;
|
|
5125
5224
|
try {
|
|
5126
|
-
logLines = readFileSync7(
|
|
5225
|
+
logLines = readFileSync7(join27(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
5127
5226
|
} catch {
|
|
5128
5227
|
}
|
|
5129
5228
|
return [
|
|
@@ -5194,8 +5293,8 @@ async function runDoctor(input) {
|
|
|
5194
5293
|
}
|
|
5195
5294
|
|
|
5196
5295
|
// src/commands/archive.ts
|
|
5197
|
-
import { rename as rename7, mkdir as
|
|
5198
|
-
import { join as
|
|
5296
|
+
import { rename as rename7, mkdir as mkdir9, readFile as readFile20, writeFile as writeFile10 } from "fs/promises";
|
|
5297
|
+
import { join as join28, dirname as dirname10 } from "path";
|
|
5199
5298
|
function countWikilinks(body, slug) {
|
|
5200
5299
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5201
5300
|
const re = new RegExp(`\\[\\[${escaped}(?:[|#][^\\]]*)?\\]\\]`, "g");
|
|
@@ -5223,7 +5322,7 @@ async function runArchive(input) {
|
|
|
5223
5322
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
5224
5323
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
5225
5324
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
5226
|
-
const archivePath =
|
|
5325
|
+
const archivePath = join28("_archive", relPath).replace(/\\/g, "/");
|
|
5227
5326
|
let cascade;
|
|
5228
5327
|
if (input.cascade) {
|
|
5229
5328
|
const wikilinkRefs = [];
|
|
@@ -5247,7 +5346,7 @@ async function runArchive(input) {
|
|
|
5247
5346
|
const indexRefs = [];
|
|
5248
5347
|
if (!isRaw) {
|
|
5249
5348
|
try {
|
|
5250
|
-
const idx = await
|
|
5349
|
+
const idx = await readFile20(join28(input.vault, "index.md"), "utf8");
|
|
5251
5350
|
idx.split("\n").forEach((line, i) => {
|
|
5252
5351
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
5253
5352
|
});
|
|
@@ -5273,8 +5372,8 @@ async function runArchive(input) {
|
|
|
5273
5372
|
}
|
|
5274
5373
|
if (input.cascade && input.apply && cascade) {
|
|
5275
5374
|
for (const ref of cascade.source_array_refs) {
|
|
5276
|
-
const absPath =
|
|
5277
|
-
const text = await
|
|
5375
|
+
const absPath = join28(input.vault, ref.page);
|
|
5376
|
+
const text = await readFile20(absPath, "utf8");
|
|
5278
5377
|
const split = splitFrontmatter(text);
|
|
5279
5378
|
if (!split.ok) continue;
|
|
5280
5379
|
const before = split.data.rawFrontmatter;
|
|
@@ -5291,12 +5390,12 @@ ${fmRewritten}
|
|
|
5291
5390
|
}
|
|
5292
5391
|
}
|
|
5293
5392
|
}
|
|
5294
|
-
await
|
|
5393
|
+
await mkdir9(dirname10(join28(input.vault, archivePath)), { recursive: true });
|
|
5295
5394
|
let indexUpdated = false;
|
|
5296
5395
|
if (!isRaw) {
|
|
5297
|
-
const indexPath =
|
|
5396
|
+
const indexPath = join28(input.vault, "index.md");
|
|
5298
5397
|
try {
|
|
5299
|
-
const idx = await
|
|
5398
|
+
const idx = await readFile20(indexPath, "utf8");
|
|
5300
5399
|
const originalLines = idx.split("\n");
|
|
5301
5400
|
const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
|
|
5302
5401
|
if (filtered.length !== originalLines.length) {
|
|
@@ -5307,7 +5406,7 @@ ${fmRewritten}
|
|
|
5307
5406
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
5308
5407
|
}
|
|
5309
5408
|
}
|
|
5310
|
-
await rename7(
|
|
5409
|
+
await rename7(join28(input.vault, relPath), join28(input.vault, archivePath));
|
|
5311
5410
|
appendLastOp(input.vault, {
|
|
5312
5411
|
operation: input.cascade ? "archive-cascade" : "archive",
|
|
5313
5412
|
summary: `moved ${relPath} to ${archivePath}${input.cascade ? ` (cascade: ${cascade?.source_array_refs.length ?? 0} source arrays updated)` : ""}`,
|
|
@@ -5694,14 +5793,14 @@ ${newBody}`;
|
|
|
5694
5793
|
// src/commands/update.ts
|
|
5695
5794
|
import { execSync as execSync3 } from "child_process";
|
|
5696
5795
|
import { readFileSync as readFileSync8 } from "fs";
|
|
5697
|
-
import { join as
|
|
5796
|
+
import { join as join29 } from "path";
|
|
5698
5797
|
function resolveGlobalSkillsRoot() {
|
|
5699
5798
|
try {
|
|
5700
5799
|
const globalRoot = execSync3("npm root -g", {
|
|
5701
5800
|
encoding: "utf8",
|
|
5702
5801
|
timeout: 5e3
|
|
5703
5802
|
}).trim();
|
|
5704
|
-
return
|
|
5803
|
+
return join29(globalRoot, "skillwiki", "skills");
|
|
5705
5804
|
} catch {
|
|
5706
5805
|
return null;
|
|
5707
5806
|
}
|
|
@@ -5727,7 +5826,7 @@ async function runUpdate(input) {
|
|
|
5727
5826
|
);
|
|
5728
5827
|
const currentVersion = pkg2.version;
|
|
5729
5828
|
const tag = input.distTag ?? "latest";
|
|
5730
|
-
const target =
|
|
5829
|
+
const target = join29(input.home, ".claude", "skills");
|
|
5731
5830
|
let latest;
|
|
5732
5831
|
try {
|
|
5733
5832
|
latest = execSync3(`npm view skillwiki@${tag} version`, {
|
|
@@ -5797,16 +5896,16 @@ async function runUpdate(input) {
|
|
|
5797
5896
|
|
|
5798
5897
|
// src/commands/self-update.ts
|
|
5799
5898
|
import { execSync as execSync4 } from "child_process";
|
|
5800
|
-
import { existsSync as
|
|
5801
|
-
import { join as
|
|
5899
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5900
|
+
import { join as join30 } from "path";
|
|
5802
5901
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
5803
5902
|
async function runSelfUpdate(input) {
|
|
5804
5903
|
const currentVersion = JSON.parse(
|
|
5805
5904
|
readFileSync9(new URL("../../package.json", import.meta.url), "utf8")
|
|
5806
5905
|
).version;
|
|
5807
5906
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
5808
|
-
const localPkgPath =
|
|
5809
|
-
const hasLocalSource =
|
|
5907
|
+
const localPkgPath = join30(sourceRoot, "packages", "cli", "package.json");
|
|
5908
|
+
const hasLocalSource = existsSync10(localPkgPath);
|
|
5810
5909
|
if (input.check) {
|
|
5811
5910
|
let availableVersion = null;
|
|
5812
5911
|
let source;
|
|
@@ -5937,10 +6036,10 @@ async function runSelfUpdate(input) {
|
|
|
5937
6036
|
}
|
|
5938
6037
|
|
|
5939
6038
|
// src/commands/transcripts.ts
|
|
5940
|
-
import { readdir as readdir5, stat as stat7, readFile as
|
|
5941
|
-
import { join as
|
|
6039
|
+
import { readdir as readdir5, stat as stat7, readFile as readFile21 } from "fs/promises";
|
|
6040
|
+
import { join as join31 } from "path";
|
|
5942
6041
|
async function runTranscripts(input) {
|
|
5943
|
-
const dir =
|
|
6042
|
+
const dir = join31(input.vault, "raw", "transcripts");
|
|
5944
6043
|
let entries;
|
|
5945
6044
|
try {
|
|
5946
6045
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -5950,8 +6049,8 @@ async function runTranscripts(input) {
|
|
|
5950
6049
|
const transcripts = [];
|
|
5951
6050
|
for (const entry of entries) {
|
|
5952
6051
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5953
|
-
const filePath =
|
|
5954
|
-
const content = await
|
|
6052
|
+
const filePath = join31(dir, entry.name);
|
|
6053
|
+
const content = await readFile21(filePath, "utf8");
|
|
5955
6054
|
const fm = extractFrontmatter(content);
|
|
5956
6055
|
if (!fm.ok) continue;
|
|
5957
6056
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -5968,12 +6067,12 @@ async function runTranscripts(input) {
|
|
|
5968
6067
|
}
|
|
5969
6068
|
|
|
5970
6069
|
// src/commands/project-index.ts
|
|
5971
|
-
import { readdir as readdir6, readFile as
|
|
5972
|
-
import { join as
|
|
6070
|
+
import { readdir as readdir6, readFile as readFile22, writeFile as writeFile11, mkdir as mkdir10 } from "fs/promises";
|
|
6071
|
+
import { join as join32, dirname as dirname11 } from "path";
|
|
5973
6072
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
5974
6073
|
async function runProjectIndex(input) {
|
|
5975
6074
|
const slug = input.slug;
|
|
5976
|
-
const projectDir =
|
|
6075
|
+
const projectDir = join32(input.vault, "projects", slug);
|
|
5977
6076
|
try {
|
|
5978
6077
|
await readdir6(projectDir);
|
|
5979
6078
|
} catch {
|
|
@@ -5984,15 +6083,15 @@ async function runProjectIndex(input) {
|
|
|
5984
6083
|
}
|
|
5985
6084
|
const wikilinkPattern = `[[${slug}]]`;
|
|
5986
6085
|
const entries = [];
|
|
5987
|
-
const compoundDir =
|
|
6086
|
+
const compoundDir = join32(input.vault, "projects", slug, "compound");
|
|
5988
6087
|
try {
|
|
5989
6088
|
const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
|
|
5990
6089
|
for (const entry of compoundFiles) {
|
|
5991
6090
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5992
|
-
const filePath =
|
|
6091
|
+
const filePath = join32(compoundDir, entry.name);
|
|
5993
6092
|
let text;
|
|
5994
6093
|
try {
|
|
5995
|
-
text = await
|
|
6094
|
+
text = await readFile22(filePath, "utf8");
|
|
5996
6095
|
} catch {
|
|
5997
6096
|
continue;
|
|
5998
6097
|
}
|
|
@@ -6009,16 +6108,16 @@ async function runProjectIndex(input) {
|
|
|
6009
6108
|
for (const dir of LAYER2_DIRS) {
|
|
6010
6109
|
let files;
|
|
6011
6110
|
try {
|
|
6012
|
-
files = await readdir6(
|
|
6111
|
+
files = await readdir6(join32(input.vault, dir), { withFileTypes: true });
|
|
6013
6112
|
} catch {
|
|
6014
6113
|
continue;
|
|
6015
6114
|
}
|
|
6016
6115
|
for (const entry of files) {
|
|
6017
6116
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6018
|
-
const filePath =
|
|
6117
|
+
const filePath = join32(input.vault, dir, entry.name);
|
|
6019
6118
|
let text;
|
|
6020
6119
|
try {
|
|
6021
|
-
text = await
|
|
6120
|
+
text = await readFile22(filePath, "utf8");
|
|
6022
6121
|
} catch {
|
|
6023
6122
|
continue;
|
|
6024
6123
|
}
|
|
@@ -6039,11 +6138,11 @@ async function runProjectIndex(input) {
|
|
|
6039
6138
|
const tb = typeOrder[b.type] ?? 99;
|
|
6040
6139
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
6041
6140
|
});
|
|
6042
|
-
const indexPath =
|
|
6141
|
+
const indexPath = join32(projectDir, "knowledge.md");
|
|
6043
6142
|
let existing = false;
|
|
6044
6143
|
let stale = false;
|
|
6045
6144
|
try {
|
|
6046
|
-
const existingText = await
|
|
6145
|
+
const existingText = await readFile22(indexPath, "utf8");
|
|
6047
6146
|
existing = true;
|
|
6048
6147
|
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
6049
6148
|
const existingPages = new Set(existingEntries.map((l) => {
|
|
@@ -6083,7 +6182,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
6083
6182
|
}
|
|
6084
6183
|
if (input.apply) {
|
|
6085
6184
|
try {
|
|
6086
|
-
await
|
|
6185
|
+
await mkdir10(dirname11(indexPath), { recursive: true });
|
|
6087
6186
|
await writeFile11(indexPath, body, "utf8");
|
|
6088
6187
|
} catch (e) {
|
|
6089
6188
|
return {
|
|
@@ -6112,10 +6211,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
6112
6211
|
}
|
|
6113
6212
|
|
|
6114
6213
|
// src/commands/compound.ts
|
|
6115
|
-
import { writeFile as writeFile12, mkdir as
|
|
6116
|
-
import { join as
|
|
6117
|
-
import { existsSync as
|
|
6118
|
-
import { readFile as
|
|
6214
|
+
import { writeFile as writeFile12, mkdir as mkdir11, readdir as readdir7, unlink as unlink4 } from "fs/promises";
|
|
6215
|
+
import { join as join33 } from "path";
|
|
6216
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6217
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
6119
6218
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
6120
6219
|
var FIELD_RE = {
|
|
6121
6220
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -6213,17 +6312,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
6213
6312
|
};
|
|
6214
6313
|
}
|
|
6215
6314
|
async function runCompound(input) {
|
|
6216
|
-
const logPath =
|
|
6315
|
+
const logPath = join33(input.vault, "log.md");
|
|
6217
6316
|
let logText;
|
|
6218
6317
|
try {
|
|
6219
|
-
logText = await
|
|
6318
|
+
logText = await readFile23(logPath, "utf8");
|
|
6220
6319
|
} catch {
|
|
6221
6320
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
6222
6321
|
}
|
|
6223
6322
|
const entries = parseRetroEntries(logText);
|
|
6224
6323
|
const promoted = [];
|
|
6225
6324
|
const skipped = [];
|
|
6226
|
-
const compoundDir =
|
|
6325
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6227
6326
|
for (const entry of entries) {
|
|
6228
6327
|
const generalizeValue = entry.generalize.trim();
|
|
6229
6328
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -6231,8 +6330,8 @@ async function runCompound(input) {
|
|
|
6231
6330
|
continue;
|
|
6232
6331
|
}
|
|
6233
6332
|
const slug = slugify(entry.cycleName);
|
|
6234
|
-
const compoundPath =
|
|
6235
|
-
if (
|
|
6333
|
+
const compoundPath = join33(compoundDir, `${slug}.md`);
|
|
6334
|
+
if (existsSync11(compoundPath)) {
|
|
6236
6335
|
skipped.push(entry.date);
|
|
6237
6336
|
continue;
|
|
6238
6337
|
}
|
|
@@ -6270,8 +6369,8 @@ async function runCompound(input) {
|
|
|
6270
6369
|
].join("\n");
|
|
6271
6370
|
const content = frontmatter + "\n" + body;
|
|
6272
6371
|
if (!input.dryRun) {
|
|
6273
|
-
if (!
|
|
6274
|
-
await
|
|
6372
|
+
if (!existsSync11(compoundDir)) {
|
|
6373
|
+
await mkdir11(compoundDir, { recursive: true });
|
|
6275
6374
|
}
|
|
6276
6375
|
await writeFile12(compoundPath, content, "utf8");
|
|
6277
6376
|
}
|
|
@@ -6292,23 +6391,23 @@ async function runCompound(input) {
|
|
|
6292
6391
|
};
|
|
6293
6392
|
}
|
|
6294
6393
|
async function runCompoundDelete(input) {
|
|
6295
|
-
const projectDir =
|
|
6296
|
-
if (!
|
|
6394
|
+
const projectDir = join33(input.vault, "projects", input.project);
|
|
6395
|
+
if (!existsSync11(projectDir)) {
|
|
6297
6396
|
return {
|
|
6298
6397
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
6299
6398
|
result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
|
|
6300
6399
|
};
|
|
6301
6400
|
}
|
|
6302
6401
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
6303
|
-
const compoundPath =
|
|
6304
|
-
if (!
|
|
6402
|
+
const compoundPath = join33(projectDir, "compound", `${entryName}.md`);
|
|
6403
|
+
if (!existsSync11(compoundPath)) {
|
|
6305
6404
|
return {
|
|
6306
6405
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
6307
6406
|
result: err("FILE_NOT_FOUND", { path: compoundPath })
|
|
6308
6407
|
};
|
|
6309
6408
|
}
|
|
6310
6409
|
try {
|
|
6311
|
-
await
|
|
6410
|
+
await unlink4(compoundPath);
|
|
6312
6411
|
} catch (e) {
|
|
6313
6412
|
return {
|
|
6314
6413
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6334,8 +6433,8 @@ knowledge.md regenerated`
|
|
|
6334
6433
|
};
|
|
6335
6434
|
}
|
|
6336
6435
|
async function runCompoundList(input) {
|
|
6337
|
-
const compoundDir =
|
|
6338
|
-
if (!
|
|
6436
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6437
|
+
if (!existsSync11(compoundDir)) {
|
|
6339
6438
|
return {
|
|
6340
6439
|
exitCode: ExitCode.OK,
|
|
6341
6440
|
result: ok({
|
|
@@ -6365,10 +6464,10 @@ could not read compound directory`
|
|
|
6365
6464
|
const entries = [];
|
|
6366
6465
|
for (const dirent of dirents) {
|
|
6367
6466
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
6368
|
-
const filePath =
|
|
6467
|
+
const filePath = join33(compoundDir, dirent.name);
|
|
6369
6468
|
let text;
|
|
6370
6469
|
try {
|
|
6371
|
-
text = await
|
|
6470
|
+
text = await readFile23(filePath, "utf8");
|
|
6372
6471
|
} catch {
|
|
6373
6472
|
continue;
|
|
6374
6473
|
}
|
|
@@ -6397,9 +6496,9 @@ no compound entries found`;
|
|
|
6397
6496
|
}
|
|
6398
6497
|
|
|
6399
6498
|
// src/commands/observe.ts
|
|
6400
|
-
import { mkdir as
|
|
6401
|
-
import { existsSync as
|
|
6402
|
-
import { join as
|
|
6499
|
+
import { mkdir as mkdir12, writeFile as writeFile13 } from "fs/promises";
|
|
6500
|
+
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
6501
|
+
import { join as join34 } from "path";
|
|
6403
6502
|
import { createHash as createHash4 } from "crypto";
|
|
6404
6503
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6405
6504
|
function slugify2(text) {
|
|
@@ -6422,15 +6521,15 @@ async function runObserve(input) {
|
|
|
6422
6521
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
6423
6522
|
};
|
|
6424
6523
|
}
|
|
6425
|
-
if (!
|
|
6524
|
+
if (!existsSync12(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
6426
6525
|
return {
|
|
6427
6526
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6428
6527
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6429
6528
|
};
|
|
6430
6529
|
}
|
|
6431
|
-
const transcriptsDir =
|
|
6530
|
+
const transcriptsDir = join34(input.vault, "raw", "transcripts");
|
|
6432
6531
|
try {
|
|
6433
|
-
await
|
|
6532
|
+
await mkdir12(transcriptsDir, { recursive: true });
|
|
6434
6533
|
} catch {
|
|
6435
6534
|
return {
|
|
6436
6535
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
@@ -6440,7 +6539,7 @@ async function runObserve(input) {
|
|
|
6440
6539
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6441
6540
|
const slug = slugify2(input.text);
|
|
6442
6541
|
const fileName = `${today}-observation-${slug}.md`;
|
|
6443
|
-
const filePath =
|
|
6542
|
+
const filePath = join34(transcriptsDir, fileName);
|
|
6444
6543
|
const body = `
|
|
6445
6544
|
${input.text.trim()}
|
|
6446
6545
|
`;
|
|
@@ -6480,8 +6579,8 @@ ${input.text.trim()}
|
|
|
6480
6579
|
}
|
|
6481
6580
|
|
|
6482
6581
|
// src/commands/ingest.ts
|
|
6483
|
-
import { readFile as
|
|
6484
|
-
import { join as
|
|
6582
|
+
import { readFile as readFile24, writeFile as writeFile14, mkdir as mkdir13 } from "fs/promises";
|
|
6583
|
+
import { join as join35 } from "path";
|
|
6485
6584
|
import { createHash as createHash5 } from "crypto";
|
|
6486
6585
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
6487
6586
|
var TYPE_DIR = {
|
|
@@ -6640,7 +6739,7 @@ async function runIngest(input) {
|
|
|
6640
6739
|
sourceContent = fetchResult.data.body;
|
|
6641
6740
|
} else {
|
|
6642
6741
|
try {
|
|
6643
|
-
sourceContent = await
|
|
6742
|
+
sourceContent = await readFile24(input.source, "utf8");
|
|
6644
6743
|
} catch {
|
|
6645
6744
|
return {
|
|
6646
6745
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6655,8 +6754,8 @@ async function runIngest(input) {
|
|
|
6655
6754
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
6656
6755
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
6657
6756
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
6658
|
-
const rawAbsPath =
|
|
6659
|
-
const typedAbsPath =
|
|
6757
|
+
const rawAbsPath = join35(input.vault, rawRelPath);
|
|
6758
|
+
const typedAbsPath = join35(input.vault, typedRelPath);
|
|
6660
6759
|
const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
|
|
6661
6760
|
const typedContent = buildTypedContent(
|
|
6662
6761
|
input.title,
|
|
@@ -6719,7 +6818,7 @@ async function runIngest(input) {
|
|
|
6719
6818
|
};
|
|
6720
6819
|
}
|
|
6721
6820
|
try {
|
|
6722
|
-
await
|
|
6821
|
+
await mkdir13(join35(input.vault, "raw", "articles"), { recursive: true });
|
|
6723
6822
|
await writeFile14(rawAbsPath, rawContent, "utf8");
|
|
6724
6823
|
} catch (e) {
|
|
6725
6824
|
return {
|
|
@@ -6728,7 +6827,7 @@ async function runIngest(input) {
|
|
|
6728
6827
|
};
|
|
6729
6828
|
}
|
|
6730
6829
|
try {
|
|
6731
|
-
await
|
|
6830
|
+
await mkdir13(join35(input.vault, typedDir), { recursive: true });
|
|
6732
6831
|
await writeFile14(typedAbsPath, typedContent, "utf8");
|
|
6733
6832
|
} catch (e) {
|
|
6734
6833
|
return {
|
|
@@ -6907,12 +7006,12 @@ ${body}`;
|
|
|
6907
7006
|
}
|
|
6908
7007
|
|
|
6909
7008
|
// src/commands/sync.ts
|
|
6910
|
-
import { existsSync as
|
|
6911
|
-
import { join as
|
|
7009
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7010
|
+
import { join as join37 } from "path";
|
|
6912
7011
|
|
|
6913
7012
|
// src/utils/sync-lock.ts
|
|
6914
|
-
import { existsSync as
|
|
6915
|
-
import { join as
|
|
7013
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, renameSync, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
7014
|
+
import { join as join36 } from "path";
|
|
6916
7015
|
import { createHash as createHash6 } from "crypto";
|
|
6917
7016
|
function getSessionId() {
|
|
6918
7017
|
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
@@ -6920,11 +7019,11 @@ function getSessionId() {
|
|
|
6920
7019
|
return process.pid.toString();
|
|
6921
7020
|
}
|
|
6922
7021
|
function lockPath(vault) {
|
|
6923
|
-
return
|
|
7022
|
+
return join36(vault, ".skillwiki", "sync.lock");
|
|
6924
7023
|
}
|
|
6925
7024
|
function readLock(vault) {
|
|
6926
7025
|
const path = lockPath(vault);
|
|
6927
|
-
if (!
|
|
7026
|
+
if (!existsSync13(path)) return null;
|
|
6928
7027
|
try {
|
|
6929
7028
|
const raw = readFileSync10(path, "utf8");
|
|
6930
7029
|
return JSON.parse(raw);
|
|
@@ -6939,8 +7038,8 @@ function isStale(lock, now) {
|
|
|
6939
7038
|
}
|
|
6940
7039
|
function acquireLock(vault, opts = {}) {
|
|
6941
7040
|
const path = lockPath(vault);
|
|
6942
|
-
const dir =
|
|
6943
|
-
if (!
|
|
7041
|
+
const dir = join36(vault, ".skillwiki");
|
|
7042
|
+
if (!existsSync13(dir)) {
|
|
6944
7043
|
mkdirSync4(dir, { recursive: true });
|
|
6945
7044
|
}
|
|
6946
7045
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -6985,7 +7084,7 @@ function writeLockedFile(path, lock) {
|
|
|
6985
7084
|
}
|
|
6986
7085
|
function releaseLock(vault, opts = {}) {
|
|
6987
7086
|
const path = lockPath(vault);
|
|
6988
|
-
if (!
|
|
7087
|
+
if (!existsSync13(path)) {
|
|
6989
7088
|
return { released: false };
|
|
6990
7089
|
}
|
|
6991
7090
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -7014,7 +7113,7 @@ function releaseLock(vault, opts = {}) {
|
|
|
7014
7113
|
function runSyncStatus(input) {
|
|
7015
7114
|
const vault = input.vault;
|
|
7016
7115
|
const includeStashes = input.includeStashes ?? false;
|
|
7017
|
-
if (!
|
|
7116
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7018
7117
|
return {
|
|
7019
7118
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7020
7119
|
result: ok({
|
|
@@ -7028,6 +7127,7 @@ function runSyncStatus(input) {
|
|
|
7028
7127
|
})
|
|
7029
7128
|
};
|
|
7030
7129
|
}
|
|
7130
|
+
enableGitLongPathsOnWindows(vault);
|
|
7031
7131
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7032
7132
|
const dirty = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0).length : 0;
|
|
7033
7133
|
const revOutput = git(vault, ["rev-list", "--left-right", "--count", "origin/HEAD...HEAD"]);
|
|
@@ -7091,12 +7191,24 @@ function runSyncStatus(input) {
|
|
|
7091
7191
|
}
|
|
7092
7192
|
async function runSyncPush(input) {
|
|
7093
7193
|
const vault = input.vault;
|
|
7094
|
-
if (!
|
|
7194
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7095
7195
|
return {
|
|
7096
7196
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7097
7197
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
7098
7198
|
};
|
|
7099
7199
|
}
|
|
7200
|
+
enableGitLongPathsOnWindows(vault);
|
|
7201
|
+
let pathFixes = 0;
|
|
7202
|
+
const pathFix = await fixPathTooLong({ vault });
|
|
7203
|
+
if (pathFix.result.ok && pathFix.result.data.fixed.length > 0) {
|
|
7204
|
+
pathFixes = pathFix.result.data.fixed.length;
|
|
7205
|
+
appendLastOp(vault, {
|
|
7206
|
+
operation: "lint-fix",
|
|
7207
|
+
summary: `fixed ${pathFixes} long path(s)`,
|
|
7208
|
+
files: pathFix.result.data.fixed.flatMap((f) => [f.from, f.to]),
|
|
7209
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7210
|
+
});
|
|
7211
|
+
}
|
|
7100
7212
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7101
7213
|
const dirtyFiles = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0) : [];
|
|
7102
7214
|
if (dirtyFiles.length === 0) {
|
|
@@ -7106,6 +7218,7 @@ async function runSyncPush(input) {
|
|
|
7106
7218
|
files_committed: 0,
|
|
7107
7219
|
commit_message: "",
|
|
7108
7220
|
pushed: false,
|
|
7221
|
+
path_fixes: pathFixes,
|
|
7109
7222
|
humanHint: "nothing to commit, working tree clean"
|
|
7110
7223
|
})
|
|
7111
7224
|
};
|
|
@@ -7160,7 +7273,8 @@ async function runSyncPush(input) {
|
|
|
7160
7273
|
files_committed: dirtyFiles.length,
|
|
7161
7274
|
commit_message: commitMessage,
|
|
7162
7275
|
pushed: false,
|
|
7163
|
-
|
|
7276
|
+
path_fixes: pathFixes,
|
|
7277
|
+
humanHint: `committed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""} but push failed: ${String(e)}`
|
|
7164
7278
|
})
|
|
7165
7279
|
};
|
|
7166
7280
|
}
|
|
@@ -7170,7 +7284,8 @@ async function runSyncPush(input) {
|
|
|
7170
7284
|
files_committed: dirtyFiles.length,
|
|
7171
7285
|
commit_message: commitMessage,
|
|
7172
7286
|
pushed,
|
|
7173
|
-
|
|
7287
|
+
path_fixes: pathFixes,
|
|
7288
|
+
humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}`
|
|
7174
7289
|
})
|
|
7175
7290
|
};
|
|
7176
7291
|
}
|
|
@@ -7193,14 +7308,19 @@ function enumerateStashes(vault) {
|
|
|
7193
7308
|
}
|
|
7194
7309
|
return stashes;
|
|
7195
7310
|
}
|
|
7311
|
+
function enableGitLongPathsOnWindows(vault) {
|
|
7312
|
+
if (process.platform !== "win32") return;
|
|
7313
|
+
git(vault, ["config", "core.longpaths", "true"]);
|
|
7314
|
+
}
|
|
7196
7315
|
async function runSyncPull(input) {
|
|
7197
7316
|
const vault = input.vault;
|
|
7198
|
-
if (!
|
|
7317
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7199
7318
|
return {
|
|
7200
7319
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7201
7320
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
7202
7321
|
};
|
|
7203
7322
|
}
|
|
7323
|
+
enableGitLongPathsOnWindows(vault);
|
|
7204
7324
|
let fetched = false;
|
|
7205
7325
|
try {
|
|
7206
7326
|
gitStrict(vault, ["fetch", "origin"]);
|
|
@@ -7289,6 +7409,8 @@ async function runSyncPull(input) {
|
|
|
7289
7409
|
};
|
|
7290
7410
|
}
|
|
7291
7411
|
}
|
|
7412
|
+
const pathFix = await fixPathTooLong({ vault });
|
|
7413
|
+
const pathFixCount = pathFix.result.ok ? pathFix.result.data.fixed.length : 0;
|
|
7292
7414
|
let lintErrors = 0;
|
|
7293
7415
|
let lintWarnings = 0;
|
|
7294
7416
|
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
@@ -7300,6 +7422,7 @@ async function runSyncPull(input) {
|
|
|
7300
7422
|
if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
|
|
7301
7423
|
else hintParts.push("already up to date");
|
|
7302
7424
|
if (autoResolved > 0) hintParts.push(`${autoResolved} conflict(s) auto-resolved`);
|
|
7425
|
+
if (pathFixCount > 0) hintParts.push(`${pathFixCount} long path(s) fixed`);
|
|
7303
7426
|
if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
|
|
7304
7427
|
if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
|
|
7305
7428
|
const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
@@ -7363,7 +7486,7 @@ function runSyncPeers(input) {
|
|
|
7363
7486
|
}
|
|
7364
7487
|
function runSyncLock(input) {
|
|
7365
7488
|
const vault = input.vault;
|
|
7366
|
-
if (!
|
|
7489
|
+
if (!existsSync14(vault)) {
|
|
7367
7490
|
return {
|
|
7368
7491
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7369
7492
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7398,7 +7521,7 @@ function runSyncLock(input) {
|
|
|
7398
7521
|
}
|
|
7399
7522
|
function runSyncUnlock(input) {
|
|
7400
7523
|
const vault = input.vault;
|
|
7401
|
-
if (!
|
|
7524
|
+
if (!existsSync14(vault)) {
|
|
7402
7525
|
return {
|
|
7403
7526
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7404
7527
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7432,7 +7555,7 @@ function runSyncUnlock(input) {
|
|
|
7432
7555
|
|
|
7433
7556
|
// src/commands/backup.ts
|
|
7434
7557
|
import { statSync as statSync5, readdirSync as readdirSync2, readFileSync as readFileSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
7435
|
-
import { join as
|
|
7558
|
+
import { join as join38, relative as relative3, dirname as dirname12 } from "path";
|
|
7436
7559
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
7437
7560
|
|
|
7438
7561
|
// src/utils/s3-client.ts
|
|
@@ -7452,11 +7575,11 @@ function createS3Client(config) {
|
|
|
7452
7575
|
}
|
|
7453
7576
|
|
|
7454
7577
|
// src/commands/backup.ts
|
|
7455
|
-
var
|
|
7578
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_modules", ".skillwiki"]);
|
|
7456
7579
|
function* walkMarkdown(dir, base) {
|
|
7457
7580
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
7458
|
-
if (
|
|
7459
|
-
const full =
|
|
7581
|
+
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
7582
|
+
const full = join38(dir, entry.name);
|
|
7460
7583
|
if (entry.isDirectory()) {
|
|
7461
7584
|
yield* walkMarkdown(full, base);
|
|
7462
7585
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7479,7 +7602,7 @@ async function runBackupSync(input) {
|
|
|
7479
7602
|
let failed = 0;
|
|
7480
7603
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
7481
7604
|
for (const relPath of files) {
|
|
7482
|
-
const absPath =
|
|
7605
|
+
const absPath = join38(input.vault, relPath);
|
|
7483
7606
|
const localStat = statSync5(absPath);
|
|
7484
7607
|
let needsUpload = true;
|
|
7485
7608
|
try {
|
|
@@ -7555,7 +7678,7 @@ async function runBackupRestore(input) {
|
|
|
7555
7678
|
const objects = list.Contents ?? [];
|
|
7556
7679
|
for (const obj of objects) {
|
|
7557
7680
|
if (!obj.Key) continue;
|
|
7558
|
-
const localPath =
|
|
7681
|
+
const localPath = join38(target, obj.Key);
|
|
7559
7682
|
try {
|
|
7560
7683
|
const localStat = statSync5(localPath);
|
|
7561
7684
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -7568,7 +7691,7 @@ async function runBackupRestore(input) {
|
|
|
7568
7691
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
7569
7692
|
const body = await resp.Body?.transformToByteArray();
|
|
7570
7693
|
if (body) {
|
|
7571
|
-
mkdirSync5(
|
|
7694
|
+
mkdirSync5(dirname12(localPath), { recursive: true });
|
|
7572
7695
|
writeFileSync7(localPath, Buffer.from(body));
|
|
7573
7696
|
downloaded++;
|
|
7574
7697
|
}
|
|
@@ -7601,11 +7724,11 @@ async function runBackupRestore(input) {
|
|
|
7601
7724
|
}
|
|
7602
7725
|
|
|
7603
7726
|
// src/commands/status.ts
|
|
7604
|
-
import { existsSync as
|
|
7605
|
-
import { readFile as
|
|
7606
|
-
import { join as
|
|
7727
|
+
import { existsSync as existsSync15, statSync as statSync6 } from "fs";
|
|
7728
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
7729
|
+
import { join as join39 } from "path";
|
|
7607
7730
|
async function runStatus(input) {
|
|
7608
|
-
if (!
|
|
7731
|
+
if (!existsSync15(input.vault)) {
|
|
7609
7732
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
7610
7733
|
}
|
|
7611
7734
|
const scan = await scanVault(input.vault);
|
|
@@ -7630,7 +7753,7 @@ async function runStatus(input) {
|
|
|
7630
7753
|
const compound = scan.data.compound.length;
|
|
7631
7754
|
let schemaVersion = "v1";
|
|
7632
7755
|
try {
|
|
7633
|
-
const schemaContent = await
|
|
7756
|
+
const schemaContent = await readFile25(join39(input.vault, "SCHEMA.md"), "utf8");
|
|
7634
7757
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
7635
7758
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
7636
7759
|
} catch {
|
|
@@ -7690,8 +7813,8 @@ async function runStatus(input) {
|
|
|
7690
7813
|
}
|
|
7691
7814
|
|
|
7692
7815
|
// src/commands/seed.ts
|
|
7693
|
-
import { mkdir as
|
|
7694
|
-
import { join as
|
|
7816
|
+
import { mkdir as mkdir14, writeFile as writeFile15, stat as stat8 } from "fs/promises";
|
|
7817
|
+
import { join as join40 } from "path";
|
|
7695
7818
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7696
7819
|
var EXAMPLE_PAGES = {
|
|
7697
7820
|
"entities/example-project.md": `---
|
|
@@ -7760,29 +7883,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
7760
7883
|
`;
|
|
7761
7884
|
async function runSeed(input) {
|
|
7762
7885
|
try {
|
|
7763
|
-
await stat8(
|
|
7886
|
+
await stat8(join40(input.vault, "SCHEMA.md"));
|
|
7764
7887
|
} catch {
|
|
7765
7888
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
7766
7889
|
}
|
|
7767
7890
|
const created = [];
|
|
7768
7891
|
const skipped = [];
|
|
7769
7892
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
7770
|
-
const absPath =
|
|
7893
|
+
const absPath = join40(input.vault, relPath);
|
|
7771
7894
|
try {
|
|
7772
7895
|
await stat8(absPath);
|
|
7773
7896
|
skipped.push(relPath);
|
|
7774
7897
|
} catch {
|
|
7775
|
-
await
|
|
7898
|
+
await mkdir14(join40(absPath, ".."), { recursive: true });
|
|
7776
7899
|
await writeFile15(absPath, content, "utf8");
|
|
7777
7900
|
created.push(relPath);
|
|
7778
7901
|
}
|
|
7779
7902
|
}
|
|
7780
|
-
const rawPath =
|
|
7903
|
+
const rawPath = join40(input.vault, "raw", "articles", "example-source.md");
|
|
7781
7904
|
try {
|
|
7782
7905
|
await stat8(rawPath);
|
|
7783
7906
|
skipped.push("raw/articles/example-source.md");
|
|
7784
7907
|
} catch {
|
|
7785
|
-
await
|
|
7908
|
+
await mkdir14(join40(rawPath, ".."), { recursive: true });
|
|
7786
7909
|
await writeFile15(rawPath, EXAMPLE_RAW, "utf8");
|
|
7787
7910
|
created.push("raw/articles/example-source.md");
|
|
7788
7911
|
}
|
|
@@ -7805,9 +7928,9 @@ async function runSeed(input) {
|
|
|
7805
7928
|
}
|
|
7806
7929
|
|
|
7807
7930
|
// src/commands/canvas.ts
|
|
7808
|
-
import { readFile as
|
|
7809
|
-
import { existsSync as
|
|
7810
|
-
import { join as
|
|
7931
|
+
import { readFile as readFile26, writeFile as writeFile16 } from "fs/promises";
|
|
7932
|
+
import { existsSync as existsSync16 } from "fs";
|
|
7933
|
+
import { join as join41 } from "path";
|
|
7811
7934
|
var NODE_WIDTH = 240;
|
|
7812
7935
|
var NODE_HEIGHT = 60;
|
|
7813
7936
|
var COLUMN_SPACING = 400;
|
|
@@ -7885,8 +8008,8 @@ function buildCanvasEdges(adjacency) {
|
|
|
7885
8008
|
return edges;
|
|
7886
8009
|
}
|
|
7887
8010
|
async function runCanvasGenerate(input) {
|
|
7888
|
-
const graphPath = input.graphPath ??
|
|
7889
|
-
if (!
|
|
8011
|
+
const graphPath = input.graphPath ?? join41(input.vault, ".skillwiki", "graph.json");
|
|
8012
|
+
if (!existsSync16(graphPath)) {
|
|
7890
8013
|
return {
|
|
7891
8014
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
7892
8015
|
result: err("FILE_NOT_FOUND", {
|
|
@@ -7897,7 +8020,7 @@ async function runCanvasGenerate(input) {
|
|
|
7897
8020
|
}
|
|
7898
8021
|
let raw;
|
|
7899
8022
|
try {
|
|
7900
|
-
raw = await
|
|
8023
|
+
raw = await readFile26(graphPath, "utf8");
|
|
7901
8024
|
} catch (e) {
|
|
7902
8025
|
return {
|
|
7903
8026
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -7923,7 +8046,7 @@ async function runCanvasGenerate(input) {
|
|
|
7923
8046
|
const nodes = buildCanvasNodes(paths);
|
|
7924
8047
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
7925
8048
|
const canvas = { nodes, edges };
|
|
7926
|
-
const outPath =
|
|
8049
|
+
const outPath = join41(input.vault, "vault-graph.canvas");
|
|
7927
8050
|
try {
|
|
7928
8051
|
await writeFile16(outPath, JSON.stringify(canvas, null, 2));
|
|
7929
8052
|
} catch (e) {
|
|
@@ -7945,8 +8068,8 @@ written: ${outPath}`
|
|
|
7945
8068
|
}
|
|
7946
8069
|
|
|
7947
8070
|
// src/commands/query.ts
|
|
7948
|
-
import { readFile as
|
|
7949
|
-
import { join as
|
|
8071
|
+
import { readFile as readFile27, stat as stat9 } from "fs/promises";
|
|
8072
|
+
import { join as join42 } from "path";
|
|
7950
8073
|
var W_KEYWORD = 2;
|
|
7951
8074
|
var W_SOURCE_OVERLAP = 4;
|
|
7952
8075
|
var W_WIKILINK = 3;
|
|
@@ -8067,7 +8190,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8067
8190
|
return score;
|
|
8068
8191
|
}
|
|
8069
8192
|
async function loadOrBuildGraph(vault) {
|
|
8070
|
-
const graphPath =
|
|
8193
|
+
const graphPath = join42(vault, ".skillwiki", "graph.json");
|
|
8071
8194
|
let needsBuild = false;
|
|
8072
8195
|
try {
|
|
8073
8196
|
const fileStat = await stat9(graphPath);
|
|
@@ -8081,7 +8204,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
8081
8204
|
if (buildResult.exitCode !== 0) return null;
|
|
8082
8205
|
}
|
|
8083
8206
|
try {
|
|
8084
|
-
const raw = await
|
|
8207
|
+
const raw = await readFile27(graphPath, "utf8");
|
|
8085
8208
|
return JSON.parse(raw);
|
|
8086
8209
|
} catch {
|
|
8087
8210
|
return null;
|
|
@@ -8089,14 +8212,14 @@ async function loadOrBuildGraph(vault) {
|
|
|
8089
8212
|
}
|
|
8090
8213
|
|
|
8091
8214
|
// src/utils/auto-commit.ts
|
|
8092
|
-
import { existsSync as
|
|
8093
|
-
import { join as
|
|
8215
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8216
|
+
import { join as join43 } from "path";
|
|
8094
8217
|
async function postCommit(vault, exitCode) {
|
|
8095
8218
|
if (exitCode !== 0) return;
|
|
8096
8219
|
const home = process.env.HOME ?? "";
|
|
8097
8220
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
8098
8221
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
8099
|
-
if (!
|
|
8222
|
+
if (!existsSync17(join43(vault, ".git"))) return;
|
|
8100
8223
|
const lastOps = readLastOp(vault);
|
|
8101
8224
|
if (lastOps.length === 0) return;
|
|
8102
8225
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -8147,7 +8270,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
8147
8270
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
8148
8271
|
});
|
|
8149
8272
|
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 ??
|
|
8273
|
+
const out = opts.out ?? join44(vault, ".skillwiki", "graph.json");
|
|
8151
8274
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
8152
8275
|
});
|
|
8153
8276
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|