skillwiki 0.8.1-beta.6 → 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 +367 -256
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +2 -2
- package/skills/README.md +3 -1
- package/skills/package.json +2 -1
- package/skills/skills/proj-decide/SKILL.md +25 -0
- package/skills/skills/proj-distill/SKILL.md +55 -0
- package/skills/skills/proj-init/SKILL.md +30 -0
- package/skills/skills/proj-work/SKILL.md +69 -0
- package/skills/skills/using-skillwiki/SKILL.md +157 -0
- package/skills/skills/wiki-adapter-prd/SKILL.md +88 -0
- package/skills/skills/wiki-add-task/SKILL.md +102 -0
- package/skills/skills/wiki-archive/SKILL.md +46 -0
- package/skills/skills/wiki-audit/SKILL.md +34 -0
- package/skills/skills/wiki-canvas/SKILL.md +57 -0
- package/skills/skills/wiki-crystallize/SKILL.md +29 -0
- package/skills/skills/wiki-gate-plan-mode/SKILL.md +80 -0
- package/skills/skills/wiki-ingest/SKILL.md +55 -0
- package/skills/skills/wiki-init/SKILL.md +37 -0
- package/skills/skills/wiki-lint/SKILL.md +25 -0
- package/skills/skills/wiki-query/SKILL.md +36 -0
- package/skills/skills/wiki-reingest/SKILL.md +55 -0
- package/skills/skills/wiki-sync/SKILL.md +240 -0
- package/skills/wiki-query/SKILL.md +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,23 +4356,23 @@ 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 &&
|
|
4271
|
-
const found =
|
|
4359
|
+
const srcDir = cwd ? join27(cwd, "packages", "skills") : void 0;
|
|
4360
|
+
if (srcDir && existsSync9(srcDir)) {
|
|
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)`);
|
|
4274
4364
|
}
|
|
4275
4365
|
}
|
|
4276
4366
|
const plugin = findPlugin(home);
|
|
4277
4367
|
if (plugin) {
|
|
4278
|
-
const found =
|
|
4368
|
+
const found = findInstalledSkillMd(plugin.installPath);
|
|
4279
4369
|
if (found.length > 0) {
|
|
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 (
|
|
4285
|
-
const found =
|
|
4373
|
+
const skillsDir = join27(home, ".claude", "skills");
|
|
4374
|
+
if (existsSync9(skillsDir)) {
|
|
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)`);
|
|
4288
4378
|
}
|
|
@@ -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,13 +5149,17 @@ 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
|
}
|
|
5159
|
+
function findInstalledSkillMd(dir) {
|
|
5160
|
+
const directSkills = findSkillNames(dir).map((name) => join27(dir, name, "SKILL.md"));
|
|
5161
|
+
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
5162
|
+
}
|
|
5069
5163
|
function findSkillNames(dir) {
|
|
5070
5164
|
const results = [];
|
|
5071
5165
|
let entries;
|
|
@@ -5075,7 +5169,7 @@ function findSkillNames(dir) {
|
|
|
5075
5169
|
return results;
|
|
5076
5170
|
}
|
|
5077
5171
|
for (const entry of entries) {
|
|
5078
|
-
if (entry.isDirectory() &&
|
|
5172
|
+
if (entry.isDirectory() && existsSync9(join27(dir, entry.name, "SKILL.md"))) {
|
|
5079
5173
|
results.push(entry.name);
|
|
5080
5174
|
}
|
|
5081
5175
|
}
|
|
@@ -5119,7 +5213,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
5119
5213
|
}
|
|
5120
5214
|
let logLines = 0;
|
|
5121
5215
|
try {
|
|
5122
|
-
logLines = readFileSync7(
|
|
5216
|
+
logLines = readFileSync7(join27(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
5123
5217
|
} catch {
|
|
5124
5218
|
}
|
|
5125
5219
|
return [
|
|
@@ -5190,8 +5284,8 @@ async function runDoctor(input) {
|
|
|
5190
5284
|
}
|
|
5191
5285
|
|
|
5192
5286
|
// src/commands/archive.ts
|
|
5193
|
-
import { rename as rename7, mkdir as
|
|
5194
|
-
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";
|
|
5195
5289
|
function countWikilinks(body, slug) {
|
|
5196
5290
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5197
5291
|
const re = new RegExp(`\\[\\[${escaped}(?:[|#][^\\]]*)?\\]\\]`, "g");
|
|
@@ -5219,7 +5313,7 @@ async function runArchive(input) {
|
|
|
5219
5313
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
5220
5314
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
5221
5315
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
5222
|
-
const archivePath =
|
|
5316
|
+
const archivePath = join28("_archive", relPath).replace(/\\/g, "/");
|
|
5223
5317
|
let cascade;
|
|
5224
5318
|
if (input.cascade) {
|
|
5225
5319
|
const wikilinkRefs = [];
|
|
@@ -5243,7 +5337,7 @@ async function runArchive(input) {
|
|
|
5243
5337
|
const indexRefs = [];
|
|
5244
5338
|
if (!isRaw) {
|
|
5245
5339
|
try {
|
|
5246
|
-
const idx = await
|
|
5340
|
+
const idx = await readFile20(join28(input.vault, "index.md"), "utf8");
|
|
5247
5341
|
idx.split("\n").forEach((line, i) => {
|
|
5248
5342
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
5249
5343
|
});
|
|
@@ -5269,8 +5363,8 @@ async function runArchive(input) {
|
|
|
5269
5363
|
}
|
|
5270
5364
|
if (input.cascade && input.apply && cascade) {
|
|
5271
5365
|
for (const ref of cascade.source_array_refs) {
|
|
5272
|
-
const absPath =
|
|
5273
|
-
const text = await
|
|
5366
|
+
const absPath = join28(input.vault, ref.page);
|
|
5367
|
+
const text = await readFile20(absPath, "utf8");
|
|
5274
5368
|
const split = splitFrontmatter(text);
|
|
5275
5369
|
if (!split.ok) continue;
|
|
5276
5370
|
const before = split.data.rawFrontmatter;
|
|
@@ -5287,12 +5381,12 @@ ${fmRewritten}
|
|
|
5287
5381
|
}
|
|
5288
5382
|
}
|
|
5289
5383
|
}
|
|
5290
|
-
await
|
|
5384
|
+
await mkdir9(dirname10(join28(input.vault, archivePath)), { recursive: true });
|
|
5291
5385
|
let indexUpdated = false;
|
|
5292
5386
|
if (!isRaw) {
|
|
5293
|
-
const indexPath =
|
|
5387
|
+
const indexPath = join28(input.vault, "index.md");
|
|
5294
5388
|
try {
|
|
5295
|
-
const idx = await
|
|
5389
|
+
const idx = await readFile20(indexPath, "utf8");
|
|
5296
5390
|
const originalLines = idx.split("\n");
|
|
5297
5391
|
const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
|
|
5298
5392
|
if (filtered.length !== originalLines.length) {
|
|
@@ -5303,7 +5397,7 @@ ${fmRewritten}
|
|
|
5303
5397
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
5304
5398
|
}
|
|
5305
5399
|
}
|
|
5306
|
-
await rename7(
|
|
5400
|
+
await rename7(join28(input.vault, relPath), join28(input.vault, archivePath));
|
|
5307
5401
|
appendLastOp(input.vault, {
|
|
5308
5402
|
operation: input.cascade ? "archive-cascade" : "archive",
|
|
5309
5403
|
summary: `moved ${relPath} to ${archivePath}${input.cascade ? ` (cascade: ${cascade?.source_array_refs.length ?? 0} source arrays updated)` : ""}`,
|
|
@@ -5690,14 +5784,14 @@ ${newBody}`;
|
|
|
5690
5784
|
// src/commands/update.ts
|
|
5691
5785
|
import { execSync as execSync3 } from "child_process";
|
|
5692
5786
|
import { readFileSync as readFileSync8 } from "fs";
|
|
5693
|
-
import { join as
|
|
5787
|
+
import { join as join29 } from "path";
|
|
5694
5788
|
function resolveGlobalSkillsRoot() {
|
|
5695
5789
|
try {
|
|
5696
5790
|
const globalRoot = execSync3("npm root -g", {
|
|
5697
5791
|
encoding: "utf8",
|
|
5698
5792
|
timeout: 5e3
|
|
5699
5793
|
}).trim();
|
|
5700
|
-
return
|
|
5794
|
+
return join29(globalRoot, "skillwiki", "skills");
|
|
5701
5795
|
} catch {
|
|
5702
5796
|
return null;
|
|
5703
5797
|
}
|
|
@@ -5723,7 +5817,7 @@ async function runUpdate(input) {
|
|
|
5723
5817
|
);
|
|
5724
5818
|
const currentVersion = pkg2.version;
|
|
5725
5819
|
const tag = input.distTag ?? "latest";
|
|
5726
|
-
const target =
|
|
5820
|
+
const target = join29(input.home, ".claude", "skills");
|
|
5727
5821
|
let latest;
|
|
5728
5822
|
try {
|
|
5729
5823
|
latest = execSync3(`npm view skillwiki@${tag} version`, {
|
|
@@ -5793,16 +5887,16 @@ async function runUpdate(input) {
|
|
|
5793
5887
|
|
|
5794
5888
|
// src/commands/self-update.ts
|
|
5795
5889
|
import { execSync as execSync4 } from "child_process";
|
|
5796
|
-
import { existsSync as
|
|
5797
|
-
import { join as
|
|
5890
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5891
|
+
import { join as join30 } from "path";
|
|
5798
5892
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
5799
5893
|
async function runSelfUpdate(input) {
|
|
5800
5894
|
const currentVersion = JSON.parse(
|
|
5801
5895
|
readFileSync9(new URL("../../package.json", import.meta.url), "utf8")
|
|
5802
5896
|
).version;
|
|
5803
5897
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
5804
|
-
const localPkgPath =
|
|
5805
|
-
const hasLocalSource =
|
|
5898
|
+
const localPkgPath = join30(sourceRoot, "packages", "cli", "package.json");
|
|
5899
|
+
const hasLocalSource = existsSync10(localPkgPath);
|
|
5806
5900
|
if (input.check) {
|
|
5807
5901
|
let availableVersion = null;
|
|
5808
5902
|
let source;
|
|
@@ -5933,10 +6027,10 @@ async function runSelfUpdate(input) {
|
|
|
5933
6027
|
}
|
|
5934
6028
|
|
|
5935
6029
|
// src/commands/transcripts.ts
|
|
5936
|
-
import { readdir as readdir5, stat as stat7, readFile as
|
|
5937
|
-
import { join as
|
|
6030
|
+
import { readdir as readdir5, stat as stat7, readFile as readFile21 } from "fs/promises";
|
|
6031
|
+
import { join as join31 } from "path";
|
|
5938
6032
|
async function runTranscripts(input) {
|
|
5939
|
-
const dir =
|
|
6033
|
+
const dir = join31(input.vault, "raw", "transcripts");
|
|
5940
6034
|
let entries;
|
|
5941
6035
|
try {
|
|
5942
6036
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -5946,8 +6040,8 @@ async function runTranscripts(input) {
|
|
|
5946
6040
|
const transcripts = [];
|
|
5947
6041
|
for (const entry of entries) {
|
|
5948
6042
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5949
|
-
const filePath =
|
|
5950
|
-
const content = await
|
|
6043
|
+
const filePath = join31(dir, entry.name);
|
|
6044
|
+
const content = await readFile21(filePath, "utf8");
|
|
5951
6045
|
const fm = extractFrontmatter(content);
|
|
5952
6046
|
if (!fm.ok) continue;
|
|
5953
6047
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -5964,12 +6058,12 @@ async function runTranscripts(input) {
|
|
|
5964
6058
|
}
|
|
5965
6059
|
|
|
5966
6060
|
// src/commands/project-index.ts
|
|
5967
|
-
import { readdir as readdir6, readFile as
|
|
5968
|
-
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";
|
|
5969
6063
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
5970
6064
|
async function runProjectIndex(input) {
|
|
5971
6065
|
const slug = input.slug;
|
|
5972
|
-
const projectDir =
|
|
6066
|
+
const projectDir = join32(input.vault, "projects", slug);
|
|
5973
6067
|
try {
|
|
5974
6068
|
await readdir6(projectDir);
|
|
5975
6069
|
} catch {
|
|
@@ -5980,15 +6074,15 @@ async function runProjectIndex(input) {
|
|
|
5980
6074
|
}
|
|
5981
6075
|
const wikilinkPattern = `[[${slug}]]`;
|
|
5982
6076
|
const entries = [];
|
|
5983
|
-
const compoundDir =
|
|
6077
|
+
const compoundDir = join32(input.vault, "projects", slug, "compound");
|
|
5984
6078
|
try {
|
|
5985
6079
|
const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
|
|
5986
6080
|
for (const entry of compoundFiles) {
|
|
5987
6081
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
5988
|
-
const filePath =
|
|
6082
|
+
const filePath = join32(compoundDir, entry.name);
|
|
5989
6083
|
let text;
|
|
5990
6084
|
try {
|
|
5991
|
-
text = await
|
|
6085
|
+
text = await readFile22(filePath, "utf8");
|
|
5992
6086
|
} catch {
|
|
5993
6087
|
continue;
|
|
5994
6088
|
}
|
|
@@ -6005,16 +6099,16 @@ async function runProjectIndex(input) {
|
|
|
6005
6099
|
for (const dir of LAYER2_DIRS) {
|
|
6006
6100
|
let files;
|
|
6007
6101
|
try {
|
|
6008
|
-
files = await readdir6(
|
|
6102
|
+
files = await readdir6(join32(input.vault, dir), { withFileTypes: true });
|
|
6009
6103
|
} catch {
|
|
6010
6104
|
continue;
|
|
6011
6105
|
}
|
|
6012
6106
|
for (const entry of files) {
|
|
6013
6107
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6014
|
-
const filePath =
|
|
6108
|
+
const filePath = join32(input.vault, dir, entry.name);
|
|
6015
6109
|
let text;
|
|
6016
6110
|
try {
|
|
6017
|
-
text = await
|
|
6111
|
+
text = await readFile22(filePath, "utf8");
|
|
6018
6112
|
} catch {
|
|
6019
6113
|
continue;
|
|
6020
6114
|
}
|
|
@@ -6035,11 +6129,11 @@ async function runProjectIndex(input) {
|
|
|
6035
6129
|
const tb = typeOrder[b.type] ?? 99;
|
|
6036
6130
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
6037
6131
|
});
|
|
6038
|
-
const indexPath =
|
|
6132
|
+
const indexPath = join32(projectDir, "knowledge.md");
|
|
6039
6133
|
let existing = false;
|
|
6040
6134
|
let stale = false;
|
|
6041
6135
|
try {
|
|
6042
|
-
const existingText = await
|
|
6136
|
+
const existingText = await readFile22(indexPath, "utf8");
|
|
6043
6137
|
existing = true;
|
|
6044
6138
|
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
6045
6139
|
const existingPages = new Set(existingEntries.map((l) => {
|
|
@@ -6079,7 +6173,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
6079
6173
|
}
|
|
6080
6174
|
if (input.apply) {
|
|
6081
6175
|
try {
|
|
6082
|
-
await
|
|
6176
|
+
await mkdir10(dirname11(indexPath), { recursive: true });
|
|
6083
6177
|
await writeFile11(indexPath, body, "utf8");
|
|
6084
6178
|
} catch (e) {
|
|
6085
6179
|
return {
|
|
@@ -6108,10 +6202,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
6108
6202
|
}
|
|
6109
6203
|
|
|
6110
6204
|
// src/commands/compound.ts
|
|
6111
|
-
import { writeFile as writeFile12, mkdir as
|
|
6112
|
-
import { join as
|
|
6113
|
-
import { existsSync as
|
|
6114
|
-
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";
|
|
6115
6209
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
6116
6210
|
var FIELD_RE = {
|
|
6117
6211
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -6209,17 +6303,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
6209
6303
|
};
|
|
6210
6304
|
}
|
|
6211
6305
|
async function runCompound(input) {
|
|
6212
|
-
const logPath =
|
|
6306
|
+
const logPath = join33(input.vault, "log.md");
|
|
6213
6307
|
let logText;
|
|
6214
6308
|
try {
|
|
6215
|
-
logText = await
|
|
6309
|
+
logText = await readFile23(logPath, "utf8");
|
|
6216
6310
|
} catch {
|
|
6217
6311
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
6218
6312
|
}
|
|
6219
6313
|
const entries = parseRetroEntries(logText);
|
|
6220
6314
|
const promoted = [];
|
|
6221
6315
|
const skipped = [];
|
|
6222
|
-
const compoundDir =
|
|
6316
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6223
6317
|
for (const entry of entries) {
|
|
6224
6318
|
const generalizeValue = entry.generalize.trim();
|
|
6225
6319
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -6227,8 +6321,8 @@ async function runCompound(input) {
|
|
|
6227
6321
|
continue;
|
|
6228
6322
|
}
|
|
6229
6323
|
const slug = slugify(entry.cycleName);
|
|
6230
|
-
const compoundPath =
|
|
6231
|
-
if (
|
|
6324
|
+
const compoundPath = join33(compoundDir, `${slug}.md`);
|
|
6325
|
+
if (existsSync11(compoundPath)) {
|
|
6232
6326
|
skipped.push(entry.date);
|
|
6233
6327
|
continue;
|
|
6234
6328
|
}
|
|
@@ -6266,8 +6360,8 @@ async function runCompound(input) {
|
|
|
6266
6360
|
].join("\n");
|
|
6267
6361
|
const content = frontmatter + "\n" + body;
|
|
6268
6362
|
if (!input.dryRun) {
|
|
6269
|
-
if (!
|
|
6270
|
-
await
|
|
6363
|
+
if (!existsSync11(compoundDir)) {
|
|
6364
|
+
await mkdir11(compoundDir, { recursive: true });
|
|
6271
6365
|
}
|
|
6272
6366
|
await writeFile12(compoundPath, content, "utf8");
|
|
6273
6367
|
}
|
|
@@ -6288,23 +6382,23 @@ async function runCompound(input) {
|
|
|
6288
6382
|
};
|
|
6289
6383
|
}
|
|
6290
6384
|
async function runCompoundDelete(input) {
|
|
6291
|
-
const projectDir =
|
|
6292
|
-
if (!
|
|
6385
|
+
const projectDir = join33(input.vault, "projects", input.project);
|
|
6386
|
+
if (!existsSync11(projectDir)) {
|
|
6293
6387
|
return {
|
|
6294
6388
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
6295
6389
|
result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
|
|
6296
6390
|
};
|
|
6297
6391
|
}
|
|
6298
6392
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
6299
|
-
const compoundPath =
|
|
6300
|
-
if (!
|
|
6393
|
+
const compoundPath = join33(projectDir, "compound", `${entryName}.md`);
|
|
6394
|
+
if (!existsSync11(compoundPath)) {
|
|
6301
6395
|
return {
|
|
6302
6396
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
6303
6397
|
result: err("FILE_NOT_FOUND", { path: compoundPath })
|
|
6304
6398
|
};
|
|
6305
6399
|
}
|
|
6306
6400
|
try {
|
|
6307
|
-
await
|
|
6401
|
+
await unlink4(compoundPath);
|
|
6308
6402
|
} catch (e) {
|
|
6309
6403
|
return {
|
|
6310
6404
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6330,8 +6424,8 @@ knowledge.md regenerated`
|
|
|
6330
6424
|
};
|
|
6331
6425
|
}
|
|
6332
6426
|
async function runCompoundList(input) {
|
|
6333
|
-
const compoundDir =
|
|
6334
|
-
if (!
|
|
6427
|
+
const compoundDir = join33(input.vault, "projects", input.project, "compound");
|
|
6428
|
+
if (!existsSync11(compoundDir)) {
|
|
6335
6429
|
return {
|
|
6336
6430
|
exitCode: ExitCode.OK,
|
|
6337
6431
|
result: ok({
|
|
@@ -6361,10 +6455,10 @@ could not read compound directory`
|
|
|
6361
6455
|
const entries = [];
|
|
6362
6456
|
for (const dirent of dirents) {
|
|
6363
6457
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
6364
|
-
const filePath =
|
|
6458
|
+
const filePath = join33(compoundDir, dirent.name);
|
|
6365
6459
|
let text;
|
|
6366
6460
|
try {
|
|
6367
|
-
text = await
|
|
6461
|
+
text = await readFile23(filePath, "utf8");
|
|
6368
6462
|
} catch {
|
|
6369
6463
|
continue;
|
|
6370
6464
|
}
|
|
@@ -6393,9 +6487,9 @@ no compound entries found`;
|
|
|
6393
6487
|
}
|
|
6394
6488
|
|
|
6395
6489
|
// src/commands/observe.ts
|
|
6396
|
-
import { mkdir as
|
|
6397
|
-
import { existsSync as
|
|
6398
|
-
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";
|
|
6399
6493
|
import { createHash as createHash4 } from "crypto";
|
|
6400
6494
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
6401
6495
|
function slugify2(text) {
|
|
@@ -6418,15 +6512,15 @@ async function runObserve(input) {
|
|
|
6418
6512
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
6419
6513
|
};
|
|
6420
6514
|
}
|
|
6421
|
-
if (!
|
|
6515
|
+
if (!existsSync12(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
6422
6516
|
return {
|
|
6423
6517
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
6424
6518
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
6425
6519
|
};
|
|
6426
6520
|
}
|
|
6427
|
-
const transcriptsDir =
|
|
6521
|
+
const transcriptsDir = join34(input.vault, "raw", "transcripts");
|
|
6428
6522
|
try {
|
|
6429
|
-
await
|
|
6523
|
+
await mkdir12(transcriptsDir, { recursive: true });
|
|
6430
6524
|
} catch {
|
|
6431
6525
|
return {
|
|
6432
6526
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
@@ -6436,7 +6530,7 @@ async function runObserve(input) {
|
|
|
6436
6530
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6437
6531
|
const slug = slugify2(input.text);
|
|
6438
6532
|
const fileName = `${today}-observation-${slug}.md`;
|
|
6439
|
-
const filePath =
|
|
6533
|
+
const filePath = join34(transcriptsDir, fileName);
|
|
6440
6534
|
const body = `
|
|
6441
6535
|
${input.text.trim()}
|
|
6442
6536
|
`;
|
|
@@ -6476,8 +6570,8 @@ ${input.text.trim()}
|
|
|
6476
6570
|
}
|
|
6477
6571
|
|
|
6478
6572
|
// src/commands/ingest.ts
|
|
6479
|
-
import { readFile as
|
|
6480
|
-
import { join as
|
|
6573
|
+
import { readFile as readFile24, writeFile as writeFile14, mkdir as mkdir13 } from "fs/promises";
|
|
6574
|
+
import { join as join35 } from "path";
|
|
6481
6575
|
import { createHash as createHash5 } from "crypto";
|
|
6482
6576
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
6483
6577
|
var TYPE_DIR = {
|
|
@@ -6636,7 +6730,7 @@ async function runIngest(input) {
|
|
|
6636
6730
|
sourceContent = fetchResult.data.body;
|
|
6637
6731
|
} else {
|
|
6638
6732
|
try {
|
|
6639
|
-
sourceContent = await
|
|
6733
|
+
sourceContent = await readFile24(input.source, "utf8");
|
|
6640
6734
|
} catch {
|
|
6641
6735
|
return {
|
|
6642
6736
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -6651,8 +6745,8 @@ async function runIngest(input) {
|
|
|
6651
6745
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
6652
6746
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
6653
6747
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
6654
|
-
const rawAbsPath =
|
|
6655
|
-
const typedAbsPath =
|
|
6748
|
+
const rawAbsPath = join35(input.vault, rawRelPath);
|
|
6749
|
+
const typedAbsPath = join35(input.vault, typedRelPath);
|
|
6656
6750
|
const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
|
|
6657
6751
|
const typedContent = buildTypedContent(
|
|
6658
6752
|
input.title,
|
|
@@ -6715,7 +6809,7 @@ async function runIngest(input) {
|
|
|
6715
6809
|
};
|
|
6716
6810
|
}
|
|
6717
6811
|
try {
|
|
6718
|
-
await
|
|
6812
|
+
await mkdir13(join35(input.vault, "raw", "articles"), { recursive: true });
|
|
6719
6813
|
await writeFile14(rawAbsPath, rawContent, "utf8");
|
|
6720
6814
|
} catch (e) {
|
|
6721
6815
|
return {
|
|
@@ -6724,7 +6818,7 @@ async function runIngest(input) {
|
|
|
6724
6818
|
};
|
|
6725
6819
|
}
|
|
6726
6820
|
try {
|
|
6727
|
-
await
|
|
6821
|
+
await mkdir13(join35(input.vault, typedDir), { recursive: true });
|
|
6728
6822
|
await writeFile14(typedAbsPath, typedContent, "utf8");
|
|
6729
6823
|
} catch (e) {
|
|
6730
6824
|
return {
|
|
@@ -6903,12 +6997,12 @@ ${body}`;
|
|
|
6903
6997
|
}
|
|
6904
6998
|
|
|
6905
6999
|
// src/commands/sync.ts
|
|
6906
|
-
import { existsSync as
|
|
6907
|
-
import { join as
|
|
7000
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7001
|
+
import { join as join37 } from "path";
|
|
6908
7002
|
|
|
6909
7003
|
// src/utils/sync-lock.ts
|
|
6910
|
-
import { existsSync as
|
|
6911
|
-
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";
|
|
6912
7006
|
import { createHash as createHash6 } from "crypto";
|
|
6913
7007
|
function getSessionId() {
|
|
6914
7008
|
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
@@ -6916,11 +7010,11 @@ function getSessionId() {
|
|
|
6916
7010
|
return process.pid.toString();
|
|
6917
7011
|
}
|
|
6918
7012
|
function lockPath(vault) {
|
|
6919
|
-
return
|
|
7013
|
+
return join36(vault, ".skillwiki", "sync.lock");
|
|
6920
7014
|
}
|
|
6921
7015
|
function readLock(vault) {
|
|
6922
7016
|
const path = lockPath(vault);
|
|
6923
|
-
if (!
|
|
7017
|
+
if (!existsSync13(path)) return null;
|
|
6924
7018
|
try {
|
|
6925
7019
|
const raw = readFileSync10(path, "utf8");
|
|
6926
7020
|
return JSON.parse(raw);
|
|
@@ -6935,8 +7029,8 @@ function isStale(lock, now) {
|
|
|
6935
7029
|
}
|
|
6936
7030
|
function acquireLock(vault, opts = {}) {
|
|
6937
7031
|
const path = lockPath(vault);
|
|
6938
|
-
const dir =
|
|
6939
|
-
if (!
|
|
7032
|
+
const dir = join36(vault, ".skillwiki");
|
|
7033
|
+
if (!existsSync13(dir)) {
|
|
6940
7034
|
mkdirSync4(dir, { recursive: true });
|
|
6941
7035
|
}
|
|
6942
7036
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -6981,7 +7075,7 @@ function writeLockedFile(path, lock) {
|
|
|
6981
7075
|
}
|
|
6982
7076
|
function releaseLock(vault, opts = {}) {
|
|
6983
7077
|
const path = lockPath(vault);
|
|
6984
|
-
if (!
|
|
7078
|
+
if (!existsSync13(path)) {
|
|
6985
7079
|
return { released: false };
|
|
6986
7080
|
}
|
|
6987
7081
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -7010,7 +7104,7 @@ function releaseLock(vault, opts = {}) {
|
|
|
7010
7104
|
function runSyncStatus(input) {
|
|
7011
7105
|
const vault = input.vault;
|
|
7012
7106
|
const includeStashes = input.includeStashes ?? false;
|
|
7013
|
-
if (!
|
|
7107
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7014
7108
|
return {
|
|
7015
7109
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7016
7110
|
result: ok({
|
|
@@ -7087,12 +7181,23 @@ function runSyncStatus(input) {
|
|
|
7087
7181
|
}
|
|
7088
7182
|
async function runSyncPush(input) {
|
|
7089
7183
|
const vault = input.vault;
|
|
7090
|
-
if (!
|
|
7184
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7091
7185
|
return {
|
|
7092
7186
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7093
7187
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
7094
7188
|
};
|
|
7095
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
|
+
}
|
|
7096
7201
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
7097
7202
|
const dirtyFiles = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0) : [];
|
|
7098
7203
|
if (dirtyFiles.length === 0) {
|
|
@@ -7102,6 +7207,7 @@ async function runSyncPush(input) {
|
|
|
7102
7207
|
files_committed: 0,
|
|
7103
7208
|
commit_message: "",
|
|
7104
7209
|
pushed: false,
|
|
7210
|
+
path_fixes: pathFixes,
|
|
7105
7211
|
humanHint: "nothing to commit, working tree clean"
|
|
7106
7212
|
})
|
|
7107
7213
|
};
|
|
@@ -7156,7 +7262,8 @@ async function runSyncPush(input) {
|
|
|
7156
7262
|
files_committed: dirtyFiles.length,
|
|
7157
7263
|
commit_message: commitMessage,
|
|
7158
7264
|
pushed: false,
|
|
7159
|
-
|
|
7265
|
+
path_fixes: pathFixes,
|
|
7266
|
+
humanHint: `committed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""} but push failed: ${String(e)}`
|
|
7160
7267
|
})
|
|
7161
7268
|
};
|
|
7162
7269
|
}
|
|
@@ -7166,7 +7273,8 @@ async function runSyncPush(input) {
|
|
|
7166
7273
|
files_committed: dirtyFiles.length,
|
|
7167
7274
|
commit_message: commitMessage,
|
|
7168
7275
|
pushed,
|
|
7169
|
-
|
|
7276
|
+
path_fixes: pathFixes,
|
|
7277
|
+
humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}`
|
|
7170
7278
|
})
|
|
7171
7279
|
};
|
|
7172
7280
|
}
|
|
@@ -7191,7 +7299,7 @@ function enumerateStashes(vault) {
|
|
|
7191
7299
|
}
|
|
7192
7300
|
async function runSyncPull(input) {
|
|
7193
7301
|
const vault = input.vault;
|
|
7194
|
-
if (!
|
|
7302
|
+
if (!existsSync14(join37(vault, ".git"))) {
|
|
7195
7303
|
return {
|
|
7196
7304
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7197
7305
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -7285,6 +7393,8 @@ async function runSyncPull(input) {
|
|
|
7285
7393
|
};
|
|
7286
7394
|
}
|
|
7287
7395
|
}
|
|
7396
|
+
const pathFix = await fixPathTooLong({ vault });
|
|
7397
|
+
const pathFixCount = pathFix.result.ok ? pathFix.result.data.fixed.length : 0;
|
|
7288
7398
|
let lintErrors = 0;
|
|
7289
7399
|
let lintWarnings = 0;
|
|
7290
7400
|
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
@@ -7296,6 +7406,7 @@ async function runSyncPull(input) {
|
|
|
7296
7406
|
if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
|
|
7297
7407
|
else hintParts.push("already up to date");
|
|
7298
7408
|
if (autoResolved > 0) hintParts.push(`${autoResolved} conflict(s) auto-resolved`);
|
|
7409
|
+
if (pathFixCount > 0) hintParts.push(`${pathFixCount} long path(s) fixed`);
|
|
7299
7410
|
if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
|
|
7300
7411
|
if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
|
|
7301
7412
|
const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
@@ -7359,7 +7470,7 @@ function runSyncPeers(input) {
|
|
|
7359
7470
|
}
|
|
7360
7471
|
function runSyncLock(input) {
|
|
7361
7472
|
const vault = input.vault;
|
|
7362
|
-
if (!
|
|
7473
|
+
if (!existsSync14(vault)) {
|
|
7363
7474
|
return {
|
|
7364
7475
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7365
7476
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7394,7 +7505,7 @@ function runSyncLock(input) {
|
|
|
7394
7505
|
}
|
|
7395
7506
|
function runSyncUnlock(input) {
|
|
7396
7507
|
const vault = input.vault;
|
|
7397
|
-
if (!
|
|
7508
|
+
if (!existsSync14(vault)) {
|
|
7398
7509
|
return {
|
|
7399
7510
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7400
7511
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -7428,7 +7539,7 @@ function runSyncUnlock(input) {
|
|
|
7428
7539
|
|
|
7429
7540
|
// src/commands/backup.ts
|
|
7430
7541
|
import { statSync as statSync5, readdirSync as readdirSync2, readFileSync as readFileSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
7431
|
-
import { join as
|
|
7542
|
+
import { join as join38, relative as relative3, dirname as dirname12 } from "path";
|
|
7432
7543
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
7433
7544
|
|
|
7434
7545
|
// src/utils/s3-client.ts
|
|
@@ -7448,11 +7559,11 @@ function createS3Client(config) {
|
|
|
7448
7559
|
}
|
|
7449
7560
|
|
|
7450
7561
|
// src/commands/backup.ts
|
|
7451
|
-
var
|
|
7562
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_modules", ".skillwiki"]);
|
|
7452
7563
|
function* walkMarkdown(dir, base) {
|
|
7453
7564
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
7454
|
-
if (
|
|
7455
|
-
const full =
|
|
7565
|
+
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
7566
|
+
const full = join38(dir, entry.name);
|
|
7456
7567
|
if (entry.isDirectory()) {
|
|
7457
7568
|
yield* walkMarkdown(full, base);
|
|
7458
7569
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7475,7 +7586,7 @@ async function runBackupSync(input) {
|
|
|
7475
7586
|
let failed = 0;
|
|
7476
7587
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
7477
7588
|
for (const relPath of files) {
|
|
7478
|
-
const absPath =
|
|
7589
|
+
const absPath = join38(input.vault, relPath);
|
|
7479
7590
|
const localStat = statSync5(absPath);
|
|
7480
7591
|
let needsUpload = true;
|
|
7481
7592
|
try {
|
|
@@ -7551,7 +7662,7 @@ async function runBackupRestore(input) {
|
|
|
7551
7662
|
const objects = list.Contents ?? [];
|
|
7552
7663
|
for (const obj of objects) {
|
|
7553
7664
|
if (!obj.Key) continue;
|
|
7554
|
-
const localPath =
|
|
7665
|
+
const localPath = join38(target, obj.Key);
|
|
7555
7666
|
try {
|
|
7556
7667
|
const localStat = statSync5(localPath);
|
|
7557
7668
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -7564,7 +7675,7 @@ async function runBackupRestore(input) {
|
|
|
7564
7675
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
7565
7676
|
const body = await resp.Body?.transformToByteArray();
|
|
7566
7677
|
if (body) {
|
|
7567
|
-
mkdirSync5(
|
|
7678
|
+
mkdirSync5(dirname12(localPath), { recursive: true });
|
|
7568
7679
|
writeFileSync7(localPath, Buffer.from(body));
|
|
7569
7680
|
downloaded++;
|
|
7570
7681
|
}
|
|
@@ -7597,11 +7708,11 @@ async function runBackupRestore(input) {
|
|
|
7597
7708
|
}
|
|
7598
7709
|
|
|
7599
7710
|
// src/commands/status.ts
|
|
7600
|
-
import { existsSync as
|
|
7601
|
-
import { readFile as
|
|
7602
|
-
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";
|
|
7603
7714
|
async function runStatus(input) {
|
|
7604
|
-
if (!
|
|
7715
|
+
if (!existsSync15(input.vault)) {
|
|
7605
7716
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
7606
7717
|
}
|
|
7607
7718
|
const scan = await scanVault(input.vault);
|
|
@@ -7626,7 +7737,7 @@ async function runStatus(input) {
|
|
|
7626
7737
|
const compound = scan.data.compound.length;
|
|
7627
7738
|
let schemaVersion = "v1";
|
|
7628
7739
|
try {
|
|
7629
|
-
const schemaContent = await
|
|
7740
|
+
const schemaContent = await readFile25(join39(input.vault, "SCHEMA.md"), "utf8");
|
|
7630
7741
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
7631
7742
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
7632
7743
|
} catch {
|
|
@@ -7686,8 +7797,8 @@ async function runStatus(input) {
|
|
|
7686
7797
|
}
|
|
7687
7798
|
|
|
7688
7799
|
// src/commands/seed.ts
|
|
7689
|
-
import { mkdir as
|
|
7690
|
-
import { join as
|
|
7800
|
+
import { mkdir as mkdir14, writeFile as writeFile15, stat as stat8 } from "fs/promises";
|
|
7801
|
+
import { join as join40 } from "path";
|
|
7691
7802
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7692
7803
|
var EXAMPLE_PAGES = {
|
|
7693
7804
|
"entities/example-project.md": `---
|
|
@@ -7756,29 +7867,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
7756
7867
|
`;
|
|
7757
7868
|
async function runSeed(input) {
|
|
7758
7869
|
try {
|
|
7759
|
-
await stat8(
|
|
7870
|
+
await stat8(join40(input.vault, "SCHEMA.md"));
|
|
7760
7871
|
} catch {
|
|
7761
7872
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
7762
7873
|
}
|
|
7763
7874
|
const created = [];
|
|
7764
7875
|
const skipped = [];
|
|
7765
7876
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
7766
|
-
const absPath =
|
|
7877
|
+
const absPath = join40(input.vault, relPath);
|
|
7767
7878
|
try {
|
|
7768
7879
|
await stat8(absPath);
|
|
7769
7880
|
skipped.push(relPath);
|
|
7770
7881
|
} catch {
|
|
7771
|
-
await
|
|
7882
|
+
await mkdir14(join40(absPath, ".."), { recursive: true });
|
|
7772
7883
|
await writeFile15(absPath, content, "utf8");
|
|
7773
7884
|
created.push(relPath);
|
|
7774
7885
|
}
|
|
7775
7886
|
}
|
|
7776
|
-
const rawPath =
|
|
7887
|
+
const rawPath = join40(input.vault, "raw", "articles", "example-source.md");
|
|
7777
7888
|
try {
|
|
7778
7889
|
await stat8(rawPath);
|
|
7779
7890
|
skipped.push("raw/articles/example-source.md");
|
|
7780
7891
|
} catch {
|
|
7781
|
-
await
|
|
7892
|
+
await mkdir14(join40(rawPath, ".."), { recursive: true });
|
|
7782
7893
|
await writeFile15(rawPath, EXAMPLE_RAW, "utf8");
|
|
7783
7894
|
created.push("raw/articles/example-source.md");
|
|
7784
7895
|
}
|
|
@@ -7801,9 +7912,9 @@ async function runSeed(input) {
|
|
|
7801
7912
|
}
|
|
7802
7913
|
|
|
7803
7914
|
// src/commands/canvas.ts
|
|
7804
|
-
import { readFile as
|
|
7805
|
-
import { existsSync as
|
|
7806
|
-
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";
|
|
7807
7918
|
var NODE_WIDTH = 240;
|
|
7808
7919
|
var NODE_HEIGHT = 60;
|
|
7809
7920
|
var COLUMN_SPACING = 400;
|
|
@@ -7881,8 +7992,8 @@ function buildCanvasEdges(adjacency) {
|
|
|
7881
7992
|
return edges;
|
|
7882
7993
|
}
|
|
7883
7994
|
async function runCanvasGenerate(input) {
|
|
7884
|
-
const graphPath = input.graphPath ??
|
|
7885
|
-
if (!
|
|
7995
|
+
const graphPath = input.graphPath ?? join41(input.vault, ".skillwiki", "graph.json");
|
|
7996
|
+
if (!existsSync16(graphPath)) {
|
|
7886
7997
|
return {
|
|
7887
7998
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
7888
7999
|
result: err("FILE_NOT_FOUND", {
|
|
@@ -7893,7 +8004,7 @@ async function runCanvasGenerate(input) {
|
|
|
7893
8004
|
}
|
|
7894
8005
|
let raw;
|
|
7895
8006
|
try {
|
|
7896
|
-
raw = await
|
|
8007
|
+
raw = await readFile26(graphPath, "utf8");
|
|
7897
8008
|
} catch (e) {
|
|
7898
8009
|
return {
|
|
7899
8010
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -7919,7 +8030,7 @@ async function runCanvasGenerate(input) {
|
|
|
7919
8030
|
const nodes = buildCanvasNodes(paths);
|
|
7920
8031
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
7921
8032
|
const canvas = { nodes, edges };
|
|
7922
|
-
const outPath =
|
|
8033
|
+
const outPath = join41(input.vault, "vault-graph.canvas");
|
|
7923
8034
|
try {
|
|
7924
8035
|
await writeFile16(outPath, JSON.stringify(canvas, null, 2));
|
|
7925
8036
|
} catch (e) {
|
|
@@ -7941,8 +8052,8 @@ written: ${outPath}`
|
|
|
7941
8052
|
}
|
|
7942
8053
|
|
|
7943
8054
|
// src/commands/query.ts
|
|
7944
|
-
import { readFile as
|
|
7945
|
-
import { join as
|
|
8055
|
+
import { readFile as readFile27, stat as stat9 } from "fs/promises";
|
|
8056
|
+
import { join as join42 } from "path";
|
|
7946
8057
|
var W_KEYWORD = 2;
|
|
7947
8058
|
var W_SOURCE_OVERLAP = 4;
|
|
7948
8059
|
var W_WIKILINK = 3;
|
|
@@ -8063,7 +8174,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
8063
8174
|
return score;
|
|
8064
8175
|
}
|
|
8065
8176
|
async function loadOrBuildGraph(vault) {
|
|
8066
|
-
const graphPath =
|
|
8177
|
+
const graphPath = join42(vault, ".skillwiki", "graph.json");
|
|
8067
8178
|
let needsBuild = false;
|
|
8068
8179
|
try {
|
|
8069
8180
|
const fileStat = await stat9(graphPath);
|
|
@@ -8077,7 +8188,7 @@ async function loadOrBuildGraph(vault) {
|
|
|
8077
8188
|
if (buildResult.exitCode !== 0) return null;
|
|
8078
8189
|
}
|
|
8079
8190
|
try {
|
|
8080
|
-
const raw = await
|
|
8191
|
+
const raw = await readFile27(graphPath, "utf8");
|
|
8081
8192
|
return JSON.parse(raw);
|
|
8082
8193
|
} catch {
|
|
8083
8194
|
return null;
|
|
@@ -8085,14 +8196,14 @@ async function loadOrBuildGraph(vault) {
|
|
|
8085
8196
|
}
|
|
8086
8197
|
|
|
8087
8198
|
// src/utils/auto-commit.ts
|
|
8088
|
-
import { existsSync as
|
|
8089
|
-
import { join as
|
|
8199
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8200
|
+
import { join as join43 } from "path";
|
|
8090
8201
|
async function postCommit(vault, exitCode) {
|
|
8091
8202
|
if (exitCode !== 0) return;
|
|
8092
8203
|
const home = process.env.HOME ?? "";
|
|
8093
8204
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
8094
8205
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
8095
|
-
if (!
|
|
8206
|
+
if (!existsSync17(join43(vault, ".git"))) return;
|
|
8096
8207
|
const lastOps = readLastOp(vault);
|
|
8097
8208
|
if (lastOps.length === 0) return;
|
|
8098
8209
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -8143,7 +8254,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
8143
8254
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
8144
8255
|
});
|
|
8145
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) => {
|
|
8146
|
-
const out = opts.out ??
|
|
8257
|
+
const out = opts.out ?? join44(vault, ".skillwiki", "graph.json");
|
|
8147
8258
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
8148
8259
|
});
|
|
8149
8260
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|