skillwiki 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -2402,6 +2402,46 @@ async function runDedup(input) {
|
|
|
2402
2402
|
};
|
|
2403
2403
|
}
|
|
2404
2404
|
|
|
2405
|
+
// src/commands/raw-body-dedup.ts
|
|
2406
|
+
import { createHash as createHash2 } from "crypto";
|
|
2407
|
+
async function runRawBodyDedup(vault) {
|
|
2408
|
+
const scan = await scanVault(vault);
|
|
2409
|
+
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2410
|
+
const bodyHashMap = /* @__PURE__ */ new Map();
|
|
2411
|
+
let totalFiles = 0;
|
|
2412
|
+
for (const raw of scan.data.raw) {
|
|
2413
|
+
const text = await readPage(raw);
|
|
2414
|
+
const split = splitFrontmatter(text);
|
|
2415
|
+
if (!split.ok) continue;
|
|
2416
|
+
totalFiles++;
|
|
2417
|
+
const bodyHash = createHash2("sha256").update(split.data.body).digest("hex");
|
|
2418
|
+
const fm = extractFrontmatter(text);
|
|
2419
|
+
let fmSha256 = null;
|
|
2420
|
+
if (fm.ok && typeof fm.data.sha256 === "string" && fm.data.sha256.length === 64) {
|
|
2421
|
+
fmSha256 = fm.data.sha256;
|
|
2422
|
+
}
|
|
2423
|
+
const existing = bodyHashMap.get(bodyHash);
|
|
2424
|
+
if (existing) {
|
|
2425
|
+
existing.push({ relPath: raw.relPath, sha256: fmSha256 });
|
|
2426
|
+
} else {
|
|
2427
|
+
bodyHashMap.set(bodyHash, [{ relPath: raw.relPath, sha256: fmSha256 }]);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
const duplicates = [];
|
|
2431
|
+
for (const [bodyHash, files] of bodyHashMap) {
|
|
2432
|
+
if (files.length < 2) continue;
|
|
2433
|
+
const uniqueShas = new Set(files.map((f) => f.sha256));
|
|
2434
|
+
const allHaveSameValidSha = uniqueShas.size === 1 && files.every((f) => f.sha256 !== null);
|
|
2435
|
+
if (!allHaveSameValidSha) {
|
|
2436
|
+
duplicates.push({ bodyHash, files });
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
return {
|
|
2440
|
+
exitCode: 0,
|
|
2441
|
+
result: ok({ scanned: totalFiles, duplicates })
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2405
2445
|
// src/commands/lint.ts
|
|
2406
2446
|
var STRUCT_MIN_BODY_LINES = 60;
|
|
2407
2447
|
var STRUCT_MIN_SECTIONS = 3;
|
|
@@ -2434,7 +2474,7 @@ function extractSourceEntries(rawFm) {
|
|
|
2434
2474
|
return entries;
|
|
2435
2475
|
}
|
|
2436
2476
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "broken_sources", "tag_not_in_taxonomy"];
|
|
2437
|
-
var WARNING_ORDER = ["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"];
|
|
2477
|
+
var WARNING_ORDER = ["raw_body_duplicate", "raw_subdirectory_duplicate", "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"];
|
|
2438
2478
|
var INFO_ORDER = ["bridges", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "missing_diagram"];
|
|
2439
2479
|
async function runLint(input) {
|
|
2440
2480
|
const buckets = {};
|
|
@@ -2484,12 +2524,41 @@ async function runLint(input) {
|
|
|
2484
2524
|
}
|
|
2485
2525
|
const dedup = await runDedup({ vault: input.vault });
|
|
2486
2526
|
if (dedup.result.ok && dedup.result.data.duplicates.length > 0) buckets.raw_dedup = dedup.result.data.duplicates;
|
|
2527
|
+
const bodyDedup = await runRawBodyDedup(input.vault);
|
|
2528
|
+
if (bodyDedup.result.ok && bodyDedup.result.data.duplicates.length > 0) {
|
|
2529
|
+
buckets.raw_body_duplicate = bodyDedup.result.data.duplicates.map((d) => ({
|
|
2530
|
+
body_hash: d.bodyHash.slice(0, 12),
|
|
2531
|
+
files: d.files.map((f) => `${f.relPath} (sha256: ${f.sha256 ?? "none"})`)
|
|
2532
|
+
}));
|
|
2533
|
+
}
|
|
2487
2534
|
const compoundRefs = await validateCompoundReferences(input.vault);
|
|
2488
2535
|
if (compoundRefs.ok && compoundRefs.data.length > 0) buckets.compound_refs = compoundRefs.data;
|
|
2489
2536
|
const scan = await scanVault(input.vault);
|
|
2490
2537
|
const allPages = scan.ok ? [...scan.data.typedKnowledge, ...scan.data.raw, ...scan.data.workItems, ...scan.data.compound] : [];
|
|
2491
2538
|
const slugs = scan.ok ? buildSlugMap(allPages) : /* @__PURE__ */ new Map();
|
|
2492
2539
|
if (scan.ok) {
|
|
2540
|
+
const subDirDupes = [];
|
|
2541
|
+
const flatStems = /* @__PURE__ */ new Map();
|
|
2542
|
+
const deepFiles = [];
|
|
2543
|
+
for (const raw of scan.data.raw) {
|
|
2544
|
+
const parts = raw.relPath.split("/");
|
|
2545
|
+
if (parts.length === 3) {
|
|
2546
|
+
const stem = parts[2].replace(/\.md$/, "");
|
|
2547
|
+
flatStems.set(`${parts[1]}/${stem}`, raw.relPath);
|
|
2548
|
+
} else if (parts.length > 3) {
|
|
2549
|
+
const stem = parts[parts.length - 1].replace(/\.md$/, "");
|
|
2550
|
+
deepFiles.push({ relPath: raw.relPath, stem, parentType: parts[1] });
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
for (const df of deepFiles) {
|
|
2554
|
+
const flatPath = flatStems.get(`${df.parentType}/${df.stem}`);
|
|
2555
|
+
if (flatPath) {
|
|
2556
|
+
subDirDupes.push(`${df.relPath} -> duplicate of ${flatPath}`);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
if (subDirDupes.length > 0) {
|
|
2560
|
+
buckets.raw_subdirectory_duplicate = subDirDupes;
|
|
2561
|
+
}
|
|
2493
2562
|
const legacyPages = [];
|
|
2494
2563
|
const orphanedPages = [];
|
|
2495
2564
|
const structFlags = [];
|
|
@@ -3444,18 +3513,6 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
3444
3513
|
if (!existsSync6(conceptsDir)) {
|
|
3445
3514
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
3446
3515
|
}
|
|
3447
|
-
let rgAvailable = false;
|
|
3448
|
-
try {
|
|
3449
|
-
execSync("which rg", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
3450
|
-
rgAvailable = true;
|
|
3451
|
-
} catch {
|
|
3452
|
-
return check(
|
|
3453
|
-
"info",
|
|
3454
|
-
"s3_mount_perf",
|
|
3455
|
-
"S3 mount performance",
|
|
3456
|
-
`S3 FUSE mount (${mountPoint}) \u2014 ripgrep not found, benchmark skipped`
|
|
3457
|
-
);
|
|
3458
|
-
}
|
|
3459
3516
|
const start = Date.now();
|
|
3460
3517
|
let timedOut = false;
|
|
3461
3518
|
try {
|
|
@@ -3618,7 +3675,7 @@ async function runArchive(input) {
|
|
|
3618
3675
|
}
|
|
3619
3676
|
|
|
3620
3677
|
// src/commands/drift.ts
|
|
3621
|
-
import { createHash as
|
|
3678
|
+
import { createHash as createHash3 } from "crypto";
|
|
3622
3679
|
import { writeFile as writeFile10 } from "fs/promises";
|
|
3623
3680
|
|
|
3624
3681
|
// src/utils/fetch.ts
|
|
@@ -3698,7 +3755,7 @@ async function runDrift(input) {
|
|
|
3698
3755
|
});
|
|
3699
3756
|
continue;
|
|
3700
3757
|
}
|
|
3701
|
-
const currentHash =
|
|
3758
|
+
const currentHash = createHash3("sha256").update(Buffer.from(resp.data.body, "utf8")).digest("hex");
|
|
3702
3759
|
const drifted2 = currentHash !== storedHash;
|
|
3703
3760
|
if (drifted2 && input.apply) {
|
|
3704
3761
|
const newFm = rawFrontmatter.replace(/^sha256:\s*[a-f0-9]+$/m, `sha256: ${currentHash}`);
|
|
@@ -4683,7 +4740,7 @@ no compound entries found`;
|
|
|
4683
4740
|
import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
|
|
4684
4741
|
import { existsSync as existsSync9, statSync as statSync3 } from "fs";
|
|
4685
4742
|
import { join as join29 } from "path";
|
|
4686
|
-
import { createHash as
|
|
4743
|
+
import { createHash as createHash4 } from "crypto";
|
|
4687
4744
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
4688
4745
|
function slugify2(text) {
|
|
4689
4746
|
const words = text.trim().split(/\s+/).slice(0, 6).join("-").toLowerCase().replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -4727,7 +4784,7 @@ async function runObserve(input) {
|
|
|
4727
4784
|
const body = `
|
|
4728
4785
|
${input.text.trim()}
|
|
4729
4786
|
`;
|
|
4730
|
-
const sha256 =
|
|
4787
|
+
const sha256 = createHash4("sha256").update(Buffer.from(body, "utf8")).digest("hex");
|
|
4731
4788
|
const frontmatterLines = [
|
|
4732
4789
|
"---",
|
|
4733
4790
|
"source_url:",
|
|
@@ -4765,7 +4822,7 @@ ${input.text.trim()}
|
|
|
4765
4822
|
// src/commands/ingest.ts
|
|
4766
4823
|
import { readFile as readFile20, writeFile as writeFile16, mkdir as mkdir12 } from "fs/promises";
|
|
4767
4824
|
import { join as join30 } from "path";
|
|
4768
|
-
import { createHash as
|
|
4825
|
+
import { createHash as createHash5 } from "crypto";
|
|
4769
4826
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
4770
4827
|
var TYPE_DIR = {
|
|
4771
4828
|
entity: "entities",
|
|
@@ -4931,7 +4988,7 @@ async function runIngest(input) {
|
|
|
4931
4988
|
};
|
|
4932
4989
|
}
|
|
4933
4990
|
}
|
|
4934
|
-
const sha256 =
|
|
4991
|
+
const sha256 = createHash5("sha256").update(Buffer.from(sourceContent, "utf8")).digest("hex");
|
|
4935
4992
|
const today = todayIso();
|
|
4936
4993
|
const slug = slugify3(input.title);
|
|
4937
4994
|
const tags = input.tags && input.tags.length > 0 ? input.tags : [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|