skillwiki 0.2.0-beta.31 → 0.2.0-beta.33

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
@@ -1367,7 +1367,8 @@ function buildSlugMap(pages) {
1367
1367
  async function runLinks(input) {
1368
1368
  const scan = await scanVault(input.vault);
1369
1369
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
1370
- const slugs = buildSlugMap(scan.data.typedKnowledge);
1370
+ const allPages = [...scan.data.typedKnowledge, ...scan.data.raw, ...scan.data.workItems, ...scan.data.compound];
1371
+ const slugs = buildSlugMap(allPages);
1371
1372
  const broken = [];
1372
1373
  for (const p of scan.data.typedKnowledge) {
1373
1374
  const text = await readPage(p);
@@ -1375,7 +1376,7 @@ async function runLinks(input) {
1375
1376
  const body = split.ok ? split.data.body : text;
1376
1377
  const lines = body.split("\n");
1377
1378
  for (const slug of extractBodyWikilinks(body)) {
1378
- const tail = slug.split("/").pop();
1379
+ const tail = slug.split("/").pop().replace(/\.md$/, "");
1379
1380
  if (!slugs.has(tail.toLowerCase())) {
1380
1381
  const line = lines.findIndex((l) => l.includes(`[[${slug}`));
1381
1382
  broken.push({ page: p.relPath, slug, line: line >= 0 ? line + 1 : 0 });
@@ -1741,7 +1742,8 @@ async function runLint(input) {
1741
1742
  const dedup = await runDedup({ vault: input.vault });
1742
1743
  if (dedup.result.ok && dedup.result.data.duplicates.length > 0) buckets.raw_dedup = dedup.result.data.duplicates;
1743
1744
  const scan = await scanVault(input.vault);
1744
- const slugs = scan.ok ? buildSlugMap(scan.data.typedKnowledge) : /* @__PURE__ */ new Map();
1745
+ const allPages = scan.ok ? [...scan.data.typedKnowledge, ...scan.data.raw, ...scan.data.workItems, ...scan.data.compound] : [];
1746
+ const slugs = scan.ok ? buildSlugMap(allPages) : /* @__PURE__ */ new Map();
1745
1747
  if (scan.ok) {
1746
1748
  const legacyPages = [];
1747
1749
  const orphanedPages = [];
@@ -1761,7 +1763,7 @@ async function runLint(input) {
1761
1763
  const fmLinks = rawFm.match(/\[\[([^\[\]|]+)(?:\|[^\[\]]*)?\]\]/g) ?? [];
1762
1764
  for (const link of fmLinks) {
1763
1765
  const target = link.replace(/^\[\[/, "").replace(/(?:\|[^\[\]]*)?\]\]$/, "").trim();
1764
- const tail = target.split("/").pop();
1766
+ const tail = target.split("/").pop().replace(/\.md$/, "");
1765
1767
  if (!slugs.has(tail.toLowerCase())) {
1766
1768
  fmWikilinkFlags.push(`${page.relPath}: [[${target}]] does not resolve`);
1767
1769
  }
@@ -2174,6 +2176,7 @@ async function runArchive(input) {
2174
2176
 
2175
2177
  // src/commands/drift.ts
2176
2178
  import { createHash as createHash2 } from "crypto";
2179
+ import { writeFile as writeFile7 } from "fs/promises";
2177
2180
 
2178
2181
  // src/utils/fetch.ts
2179
2182
  async function controlledFetch(url, opts) {
@@ -2215,11 +2218,15 @@ async function runDrift(input) {
2215
2218
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
2216
2219
  const results = [];
2217
2220
  for (const raw of scan.data.raw) {
2218
- const fm = extractFrontmatter(await readPage(raw));
2219
- if (!fm.ok) continue;
2220
- const sourceUrl = typeof fm.data.source_url === "string" ? fm.data.source_url : null;
2221
- const storedHash = typeof fm.data.sha256 === "string" ? fm.data.sha256 : null;
2222
- if (!sourceUrl || !storedHash) continue;
2221
+ const text = await readPage(raw);
2222
+ const split = splitFrontmatter(text);
2223
+ if (!split.ok) continue;
2224
+ const { rawFrontmatter, body } = split.data;
2225
+ const sourceUrlMatch = rawFrontmatter.match(/^source_url:\s*(.+)$/m);
2226
+ const storedHashMatch = rawFrontmatter.match(/^sha256:\s*([a-f0-9]+)$/m);
2227
+ if (!sourceUrlMatch || !storedHashMatch) continue;
2228
+ const sourceUrl = sourceUrlMatch[1].trim();
2229
+ const storedHash = storedHashMatch[1];
2223
2230
  const resp = await doFetch(sourceUrl, FETCH_OPTS);
2224
2231
  if (!resp.ok) {
2225
2232
  results.push({
@@ -2234,29 +2241,47 @@ async function runDrift(input) {
2234
2241
  }
2235
2242
  const currentHash = createHash2("sha256").update(Buffer.from(resp.data.body, "utf8")).digest("hex");
2236
2243
  const drifted2 = currentHash !== storedHash;
2237
- results.push({
2238
- raw_path: raw.relPath,
2239
- source_url: sourceUrl,
2240
- stored_sha256: storedHash,
2241
- current_sha256: currentHash,
2242
- status: drifted2 ? "drifted" : "unchanged"
2243
- });
2244
+ if (drifted2 && input.apply) {
2245
+ const newFm = rawFrontmatter.replace(/^sha256:\s*[a-f0-9]+$/m, `sha256: ${currentHash}`);
2246
+ const newText = `---
2247
+ ${newFm}
2248
+ ---
2249
+ ${body}`;
2250
+ await writeFile7(raw.absPath, newText, "utf8");
2251
+ results.push({
2252
+ raw_path: raw.relPath,
2253
+ source_url: sourceUrl,
2254
+ stored_sha256: storedHash,
2255
+ current_sha256: currentHash,
2256
+ status: "updated"
2257
+ });
2258
+ } else {
2259
+ results.push({
2260
+ raw_path: raw.relPath,
2261
+ source_url: sourceUrl,
2262
+ stored_sha256: storedHash,
2263
+ current_sha256: currentHash,
2264
+ status: drifted2 ? "drifted" : "unchanged"
2265
+ });
2266
+ }
2244
2267
  }
2245
2268
  const drifted = results.filter((r) => r.status === "drifted");
2246
2269
  const fetchFailed = results.filter((r) => r.status === "fetch_failed");
2270
+ const updated = results.filter((r) => r.status === "updated");
2247
2271
  const unchanged = results.filter((r) => r.status === "unchanged").length;
2248
2272
  const exitCode = drifted.length > 0 ? ExitCode.DRIFT_DETECTED : ExitCode.OK;
2249
2273
  const hintLines = [`scanned: ${results.length}, unchanged: ${unchanged}`];
2250
2274
  if (drifted.length > 0) hintLines.push(`drifted: ${drifted.length}`, ...drifted.map((d) => ` ${d.raw_path}`));
2251
2275
  if (fetchFailed.length > 0) hintLines.push(`fetch_failed: ${fetchFailed.length}`, ...fetchFailed.map((f) => ` ${f.raw_path}: ${f.fetch_error}`));
2276
+ if (updated.length > 0) hintLines.push(`updated: ${updated.length}`, ...updated.map((u) => ` ${u.raw_path}`));
2252
2277
  return {
2253
2278
  exitCode,
2254
- result: ok({ scanned: results.length, drifted, fetch_failed: fetchFailed, unchanged, humanHint: hintLines.join("\n") })
2279
+ result: ok({ scanned: results.length, drifted, fetch_failed: fetchFailed, updated, unchanged, humanHint: hintLines.join("\n") })
2255
2280
  };
2256
2281
  }
2257
2282
 
2258
2283
  // src/commands/migrate-citations.ts
2259
- import { writeFile as writeFile7 } from "fs/promises";
2284
+ import { writeFile as writeFile8 } from "fs/promises";
2260
2285
  var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
2261
2286
  function moveMarkersToParagraphEnd(body) {
2262
2287
  const lines = body.split("\n");
@@ -2379,7 +2404,7 @@ ${migratedBody}${newFooter}`;
2379
2404
  continue;
2380
2405
  }
2381
2406
  if (!input.dryRun) {
2382
- await writeFile7(page.absPath, newText, "utf8");
2407
+ await writeFile8(page.absPath, newText, "utf8");
2383
2408
  }
2384
2409
  migrated.push(page.relPath);
2385
2410
  }
@@ -2401,7 +2426,7 @@ ${migratedBody}${newFooter}`;
2401
2426
  }
2402
2427
 
2403
2428
  // src/commands/frontmatter-fix.ts
2404
- import { writeFile as writeFile8 } from "fs/promises";
2429
+ import { writeFile as writeFile9 } from "fs/promises";
2405
2430
  function isoToday() {
2406
2431
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2407
2432
  }
@@ -2443,7 +2468,7 @@ ${newBody}`;
2443
2468
  continue;
2444
2469
  }
2445
2470
  if (!input.dryRun) {
2446
- await writeFile8(page.absPath, newText, "utf8");
2471
+ await writeFile9(page.absPath, newText, "utf8");
2447
2472
  }
2448
2473
  fixed.push(page.relPath);
2449
2474
  }
@@ -2661,10 +2686,10 @@ program.command("archive <page> [vault]").description("archive a typed-knowledge
2661
2686
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2662
2687
  else emit(await runArchive({ vault: v.vault, page }));
2663
2688
  });
2664
- program.command("drift [vault]").description("detect content drift in raw sources").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2689
+ program.command("drift [vault]").description("detect content drift in raw sources").option("--apply", "update sha256 in drifted sources").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2665
2690
  const v = await resolveVaultArg(vault, opts.wiki);
2666
2691
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2667
- else emit(await runDrift({ vault: v.vault }));
2692
+ else emit(await runDrift({ vault: v.vault, apply: opts.apply }));
2668
2693
  });
2669
2694
  program.command("dedup [vault]").description("detect duplicate raw sources by sha256").option("--apply", "rewire citations and remove duplicate raw files", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2670
2695
  const v = await resolveVaultArg(vault, opts.wiki);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.2.0-beta.31",
3
+ "version": "0.2.0-beta.33",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.2.0-beta.31",
3
+ "version": "0.2.0-beta.33",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 11 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI (8 subcommands, JSON-by-default).",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.2.0-beta.31",
3
+ "version": "0.2.0-beta.33",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",