skillwiki 0.2.0-beta.26 → 0.2.0-beta.28
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 +100 -37
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/wiki-archive/SKILL.md +2 -1
- package/skills/wiki-lint/SKILL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-XM5IYZX7.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
|
-
import { readFileSync as
|
|
7
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/utils/output.ts
|
|
@@ -72,7 +72,8 @@ var ExitCode = {
|
|
|
72
72
|
DRIFT_DETECTED: 32,
|
|
73
73
|
RAW_DEDUP_DETECTED: 33,
|
|
74
74
|
MIGRATION_APPLIED: 34,
|
|
75
|
-
UNKNOWN_WIKI_PROFILE: 35
|
|
75
|
+
UNKNOWN_WIKI_PROFILE: 35,
|
|
76
|
+
DEDUP_APPLIED: 36
|
|
76
77
|
};
|
|
77
78
|
|
|
78
79
|
// ../shared/src/json-output.ts
|
|
@@ -796,6 +797,9 @@ var MARKER_RE = /\^\[(raw\/[^\]]+)\]/g;
|
|
|
796
797
|
function stripFences(body) {
|
|
797
798
|
return body.replace(FENCE2, "").replace(INLINE_CODE, "");
|
|
798
799
|
}
|
|
800
|
+
function stripFencedBlocks(body) {
|
|
801
|
+
return body.replace(FENCE2, "");
|
|
802
|
+
}
|
|
799
803
|
function extractCitationMarkers(body) {
|
|
800
804
|
const stripped = stripFences(body);
|
|
801
805
|
const out = [];
|
|
@@ -806,7 +810,7 @@ function extractCitationMarkers(body) {
|
|
|
806
810
|
return out;
|
|
807
811
|
}
|
|
808
812
|
function hasSourcesFooter(body) {
|
|
809
|
-
return /^## Sources\s*$/m.test(
|
|
813
|
+
return /^## Sources\s*$/m.test(stripFencedBlocks(body));
|
|
810
814
|
}
|
|
811
815
|
function isLegacyCitationStyle(body) {
|
|
812
816
|
const markers = extractCitationMarkers(body);
|
|
@@ -898,7 +902,7 @@ async function runAudit(input) {
|
|
|
898
902
|
return { ...m, resolved: false };
|
|
899
903
|
}
|
|
900
904
|
}));
|
|
901
|
-
const sources = fm.data.sources ?? [];
|
|
905
|
+
const sources = (fm.data.sources ?? []).map((s) => s.replace(/^\^\[/, "").replace(/\]$/, ""));
|
|
902
906
|
const referenced = new Set(resolved.map((m) => m.target));
|
|
903
907
|
const unused_sources = sources.filter((s) => !referenced.has(s));
|
|
904
908
|
const missing_from_sources = [...referenced].filter((t) => !sources.includes(t));
|
|
@@ -1303,8 +1307,7 @@ async function runInit(input) {
|
|
|
1303
1307
|
return tpl.replace(/\{\{INIT_DATE\}\}/g, today).replace("{{DOMAIN}}", domain).replace("{{WIKI_LANG}}", canonicalLang);
|
|
1304
1308
|
});
|
|
1305
1309
|
if (err22) return err22;
|
|
1306
|
-
const
|
|
1307
|
-
const skipEnv = !!input.noEnv || isTempPath;
|
|
1310
|
+
const skipEnv = !!input.noEnv;
|
|
1308
1311
|
let envWritten = "";
|
|
1309
1312
|
if (!skipEnv) {
|
|
1310
1313
|
try {
|
|
@@ -1600,6 +1603,8 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
|
|
|
1600
1603
|
}
|
|
1601
1604
|
|
|
1602
1605
|
// src/commands/dedup.ts
|
|
1606
|
+
import { readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
1607
|
+
import { join as join13 } from "path";
|
|
1603
1608
|
async function runDedup(input) {
|
|
1604
1609
|
const scan = await scanVault(input.vault);
|
|
1605
1610
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -1616,17 +1621,63 @@ async function runDedup(input) {
|
|
|
1616
1621
|
else hashMap.set(sha, [raw.relPath]);
|
|
1617
1622
|
}
|
|
1618
1623
|
const duplicates = [...hashMap.entries()].filter(([, files]) => files.length > 1).map(([sha256, files]) => ({ sha256, files }));
|
|
1619
|
-
const
|
|
1624
|
+
const rewired = [];
|
|
1625
|
+
const removed = [];
|
|
1626
|
+
if (input.apply && duplicates.length > 0) {
|
|
1627
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
1628
|
+
for (const group of duplicates) {
|
|
1629
|
+
const canonical = group.files[0];
|
|
1630
|
+
for (let i = 1; i < group.files.length; i++) {
|
|
1631
|
+
replacements.set(group.files[i], canonical);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
for (const page of scan.data.typedKnowledge) {
|
|
1635
|
+
const text = readFileSync(join13(input.vault, page.relPath), "utf-8");
|
|
1636
|
+
let updated = text;
|
|
1637
|
+
let changed = false;
|
|
1638
|
+
for (const [oldPath, newPath] of replacements) {
|
|
1639
|
+
const oldMarker = `^[${oldPath}]`;
|
|
1640
|
+
const newMarker = `^[${newPath}]`;
|
|
1641
|
+
if (updated.includes(oldMarker)) {
|
|
1642
|
+
updated = updated.replaceAll(oldMarker, newMarker);
|
|
1643
|
+
changed = true;
|
|
1644
|
+
}
|
|
1645
|
+
const oldFm = `- "^[${oldPath}]"`;
|
|
1646
|
+
const newFm = `- "^[${newPath}]"`;
|
|
1647
|
+
if (updated.includes(oldFm)) {
|
|
1648
|
+
updated = updated.replaceAll(oldFm, newFm);
|
|
1649
|
+
changed = true;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
if (changed) {
|
|
1653
|
+
writeFileSync(join13(input.vault, page.relPath), updated);
|
|
1654
|
+
rewired.push(page.relPath);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
for (const [oldPath] of replacements) {
|
|
1658
|
+
const fullPath = join13(input.vault, oldPath);
|
|
1659
|
+
try {
|
|
1660
|
+
unlinkSync(fullPath);
|
|
1661
|
+
removed.push(oldPath);
|
|
1662
|
+
} catch {
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
const exitCode = duplicates.length > 0 ? input.apply ? ExitCode.DEDUP_APPLIED : ExitCode.RAW_DEDUP_DETECTED : ExitCode.OK;
|
|
1620
1667
|
const hintLines = [`scanned: ${totalFiles} raw files`];
|
|
1621
1668
|
if (duplicates.length > 0) {
|
|
1622
1669
|
hintLines.push(`duplicates: ${duplicates.length}`);
|
|
1623
1670
|
for (const d of duplicates) hintLines.push(` ${d.sha256.slice(0, 12)}... \u2192 ${d.files.join(", ")}`);
|
|
1671
|
+
if (input.apply) {
|
|
1672
|
+
hintLines.push(`rewired: ${rewired.length} pages`);
|
|
1673
|
+
hintLines.push(`removed: ${removed.length} raw files`);
|
|
1674
|
+
}
|
|
1624
1675
|
} else {
|
|
1625
1676
|
hintLines.push("0 duplicates");
|
|
1626
1677
|
}
|
|
1627
1678
|
return {
|
|
1628
1679
|
exitCode,
|
|
1629
|
-
result: ok({ scanned: totalFiles, duplicates, humanHint: hintLines.join("\n") })
|
|
1680
|
+
result: ok({ scanned: totalFiles, duplicates, rewired, removed, humanHint: hintLines.join("\n") })
|
|
1630
1681
|
};
|
|
1631
1682
|
}
|
|
1632
1683
|
|
|
@@ -1645,8 +1696,8 @@ function hasDuplicateFrontmatter(body) {
|
|
|
1645
1696
|
return false;
|
|
1646
1697
|
}
|
|
1647
1698
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "tag_not_in_taxonomy"];
|
|
1648
|
-
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "
|
|
1649
|
-
var INFO_ORDER = ["bridges", "
|
|
1699
|
+
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "orphans", "legacy_citation_style", "orphaned_citations", "duplicate_frontmatter", "missing_overview"];
|
|
1700
|
+
var INFO_ORDER = ["bridges", "page_structure", "topic_map_recommended", "frontmatter_wikilink"];
|
|
1650
1701
|
async function runLint(input) {
|
|
1651
1702
|
const buckets = {};
|
|
1652
1703
|
const links = await runLinks({ vault: input.vault });
|
|
@@ -1690,20 +1741,31 @@ async function runLint(input) {
|
|
|
1690
1741
|
const dedup = await runDedup({ vault: input.vault });
|
|
1691
1742
|
if (dedup.result.ok && dedup.result.data.duplicates.length > 0) buckets.raw_dedup = dedup.result.data.duplicates;
|
|
1692
1743
|
const scan = await scanVault(input.vault);
|
|
1744
|
+
const slugs = scan.ok ? buildSlugMap(scan.data.typedKnowledge) : /* @__PURE__ */ new Map();
|
|
1693
1745
|
if (scan.ok) {
|
|
1694
1746
|
const legacyPages = [];
|
|
1695
1747
|
const orphanedPages = [];
|
|
1696
1748
|
const structFlags = [];
|
|
1697
1749
|
const dupFrontmatter = [];
|
|
1698
1750
|
const noOverview = [];
|
|
1751
|
+
const fmWikilinkFlags = [];
|
|
1699
1752
|
for (const page of scan.data.typedKnowledge) {
|
|
1700
1753
|
const text = await readPage(page);
|
|
1701
1754
|
const split = splitFrontmatter(text);
|
|
1702
1755
|
if (!split.ok) continue;
|
|
1703
1756
|
const body = split.data.body;
|
|
1757
|
+
const rawFm = split.data.rawFrontmatter;
|
|
1704
1758
|
if (hasDuplicateFrontmatter(body)) dupFrontmatter.push(page.relPath);
|
|
1705
1759
|
if (isLegacyCitationStyle(body)) legacyPages.push(page.relPath);
|
|
1706
1760
|
if (hasOrphanedCitations(body)) orphanedPages.push(page.relPath);
|
|
1761
|
+
const fmLinks = rawFm.match(/\[\[([^\[\]|]+)(?:\|[^\[\]]*)?\]\]/g) ?? [];
|
|
1762
|
+
for (const link of fmLinks) {
|
|
1763
|
+
const target = link.replace(/^\[\[/, "").replace(/(?:\|[^\[\]]*)?\]\]$/, "").trim();
|
|
1764
|
+
const tail = target.split("/").pop();
|
|
1765
|
+
if (!slugs.has(tail.toLowerCase())) {
|
|
1766
|
+
fmWikilinkFlags.push(`${page.relPath}: [[${target}]] does not resolve`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1707
1769
|
const bodyLines = body.split("\n").filter((l) => l.trim().length > 0).length;
|
|
1708
1770
|
const hasOverview = /^## Overview/m.test(body);
|
|
1709
1771
|
if (!hasOverview) noOverview.push(page.relPath);
|
|
@@ -1723,6 +1785,7 @@ async function runLint(input) {
|
|
|
1723
1785
|
if (structFlags.length > 0) buckets.page_structure = structFlags;
|
|
1724
1786
|
if (dupFrontmatter.length > 0) buckets.duplicate_frontmatter = dupFrontmatter;
|
|
1725
1787
|
if (noOverview.length > 0) buckets.missing_overview = noOverview;
|
|
1788
|
+
if (fmWikilinkFlags.length > 0) buckets.frontmatter_wikilink = fmWikilinkFlags;
|
|
1726
1789
|
}
|
|
1727
1790
|
const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
1728
1791
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
@@ -1758,12 +1821,12 @@ async function runLint(input) {
|
|
|
1758
1821
|
// src/commands/config.ts
|
|
1759
1822
|
import { readFile as readFile12 } from "fs/promises";
|
|
1760
1823
|
import { existsSync } from "fs";
|
|
1761
|
-
import { join as
|
|
1824
|
+
import { join as join14 } from "path";
|
|
1762
1825
|
function validateKey(key) {
|
|
1763
1826
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
1764
1827
|
}
|
|
1765
1828
|
function configPath(home) {
|
|
1766
|
-
return
|
|
1829
|
+
return join14(home, ".skillwiki", ".env");
|
|
1767
1830
|
}
|
|
1768
1831
|
async function runConfigGet(input) {
|
|
1769
1832
|
if (!validateKey(input.key)) {
|
|
@@ -1817,13 +1880,13 @@ async function runConfigPath(input) {
|
|
|
1817
1880
|
}
|
|
1818
1881
|
|
|
1819
1882
|
// src/commands/doctor.ts
|
|
1820
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as
|
|
1821
|
-
import { join as
|
|
1883
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
1884
|
+
import { join as join16 } from "path";
|
|
1822
1885
|
import { execSync } from "child_process";
|
|
1823
1886
|
|
|
1824
1887
|
// src/utils/auto-update.ts
|
|
1825
|
-
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
1826
|
-
import { join as
|
|
1888
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync } from "fs";
|
|
1889
|
+
import { join as join15, dirname as dirname6 } from "path";
|
|
1827
1890
|
import { spawn } from "child_process";
|
|
1828
1891
|
|
|
1829
1892
|
// src/utils/update-consts.ts
|
|
@@ -1834,11 +1897,11 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
|
|
|
1834
1897
|
|
|
1835
1898
|
// src/utils/auto-update.ts
|
|
1836
1899
|
function cachePath(home) {
|
|
1837
|
-
return
|
|
1900
|
+
return join15(home, ".skillwiki", CACHE_FILENAME);
|
|
1838
1901
|
}
|
|
1839
1902
|
function readCacheRaw(home) {
|
|
1840
1903
|
try {
|
|
1841
|
-
const raw =
|
|
1904
|
+
const raw = readFileSync2(cachePath(home), "utf8");
|
|
1842
1905
|
return JSON.parse(raw);
|
|
1843
1906
|
} catch {
|
|
1844
1907
|
return null;
|
|
@@ -1854,7 +1917,7 @@ function readCache(home) {
|
|
|
1854
1917
|
function writeCache(home, cache) {
|
|
1855
1918
|
const p = cachePath(home);
|
|
1856
1919
|
mkdirSync(dirname6(p), { recursive: true });
|
|
1857
|
-
|
|
1920
|
+
writeFileSync2(p, JSON.stringify(cache, null, 2));
|
|
1858
1921
|
}
|
|
1859
1922
|
function latestFromCache(home, currentVersion) {
|
|
1860
1923
|
const { cache } = readCache(home);
|
|
@@ -1937,9 +2000,9 @@ function checkVaultStructure(resolvedPath) {
|
|
|
1937
2000
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
1938
2001
|
}
|
|
1939
2002
|
const missing = [];
|
|
1940
|
-
if (!existsSync3(
|
|
2003
|
+
if (!existsSync3(join16(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
1941
2004
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
1942
|
-
if (!existsSync3(
|
|
2005
|
+
if (!existsSync3(join16(resolvedPath, dir))) missing.push(dir + "/");
|
|
1943
2006
|
}
|
|
1944
2007
|
if (missing.length === 0) {
|
|
1945
2008
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -1947,7 +2010,7 @@ function checkVaultStructure(resolvedPath) {
|
|
|
1947
2010
|
return check("error", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")}`);
|
|
1948
2011
|
}
|
|
1949
2012
|
function checkSkillsInstalled(home) {
|
|
1950
|
-
const skillsDir =
|
|
2013
|
+
const skillsDir = join16(home, ".claude", "skills");
|
|
1951
2014
|
if (!existsSync3(skillsDir)) {
|
|
1952
2015
|
return check("warn", "skills_installed", "Skills installed", `${skillsDir} not found`);
|
|
1953
2016
|
}
|
|
@@ -1968,12 +2031,12 @@ function checkNpmUpdate(home, currentVersion) {
|
|
|
1968
2031
|
return check("pass", "npm_update", "npm CLI version", `v${currentVersion} (latest: v${latest})`);
|
|
1969
2032
|
}
|
|
1970
2033
|
function checkPluginVersionDrift(home, currentVersion) {
|
|
1971
|
-
const pluginJsonPath =
|
|
2034
|
+
const pluginJsonPath = join16(home, ".claude", "plugins", "cache", "llm-wiki", "plugin.json");
|
|
1972
2035
|
if (!existsSync3(pluginJsonPath)) {
|
|
1973
2036
|
return check("pass", "plugin_version_drift", "Plugin/CLI version", "Plugin cache not found \u2014 plugin not installed");
|
|
1974
2037
|
}
|
|
1975
2038
|
try {
|
|
1976
|
-
const content =
|
|
2039
|
+
const content = readFileSync3(pluginJsonPath, { encoding: "utf8" });
|
|
1977
2040
|
const pluginData = JSON.parse(content);
|
|
1978
2041
|
const pluginVersion = pluginData.version;
|
|
1979
2042
|
if (!pluginVersion) {
|
|
@@ -2015,7 +2078,7 @@ async function checkProfiles(home) {
|
|
|
2015
2078
|
}
|
|
2016
2079
|
async function checkProjectLocalOverride(cwd) {
|
|
2017
2080
|
const dir = cwd ?? process.cwd();
|
|
2018
|
-
const envPath =
|
|
2081
|
+
const envPath = join16(dir, ".skillwiki", ".env");
|
|
2019
2082
|
if (existsSync3(envPath)) {
|
|
2020
2083
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
2021
2084
|
}
|
|
@@ -2031,9 +2094,9 @@ function findSkillMd(dir) {
|
|
|
2031
2094
|
}
|
|
2032
2095
|
for (const entry of entries) {
|
|
2033
2096
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
2034
|
-
results.push(
|
|
2097
|
+
results.push(join16(dir, entry.name));
|
|
2035
2098
|
} else if (entry.isDirectory()) {
|
|
2036
|
-
results.push(...findSkillMd(
|
|
2099
|
+
results.push(...findSkillMd(join16(dir, entry.name)));
|
|
2037
2100
|
}
|
|
2038
2101
|
}
|
|
2039
2102
|
return results;
|
|
@@ -2077,7 +2140,7 @@ async function runDoctor(input) {
|
|
|
2077
2140
|
|
|
2078
2141
|
// src/commands/archive.ts
|
|
2079
2142
|
import { rename as rename3, mkdir as mkdir5, readFile as readFile13, writeFile as writeFile6 } from "fs/promises";
|
|
2080
|
-
import { join as
|
|
2143
|
+
import { join as join17, dirname as dirname7 } from "path";
|
|
2081
2144
|
async function runArchive(input) {
|
|
2082
2145
|
const scan = await scanVault(input.vault);
|
|
2083
2146
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -2089,10 +2152,10 @@ async function runArchive(input) {
|
|
|
2089
2152
|
}
|
|
2090
2153
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
2091
2154
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
2092
|
-
const archivePath =
|
|
2093
|
-
await mkdir5(dirname7(
|
|
2155
|
+
const archivePath = join17("_archive", relPath);
|
|
2156
|
+
await mkdir5(dirname7(join17(input.vault, archivePath)), { recursive: true });
|
|
2094
2157
|
let indexUpdated = false;
|
|
2095
|
-
const indexPath =
|
|
2158
|
+
const indexPath = join17(input.vault, "index.md");
|
|
2096
2159
|
try {
|
|
2097
2160
|
const idx = await readFile13(indexPath, "utf8");
|
|
2098
2161
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
@@ -2105,7 +2168,7 @@ async function runArchive(input) {
|
|
|
2105
2168
|
} catch (e) {
|
|
2106
2169
|
if (e?.code !== "ENOENT") throw e;
|
|
2107
2170
|
}
|
|
2108
|
-
await rename3(
|
|
2171
|
+
await rename3(join17(input.vault, relPath), join17(input.vault, archivePath));
|
|
2109
2172
|
return { exitCode: ExitCode.OK, result: ok({ archived_from: relPath, archived_to: archivePath, index_updated: indexUpdated, humanHint: `${relPath} -> ${archivePath}${indexUpdated ? " (index updated)" : ""}` }) };
|
|
2110
2173
|
}
|
|
2111
2174
|
|
|
@@ -2404,10 +2467,10 @@ ${newBody}`;
|
|
|
2404
2467
|
|
|
2405
2468
|
// src/commands/update.ts
|
|
2406
2469
|
import { execSync as execSync2 } from "child_process";
|
|
2407
|
-
import { readFileSync as
|
|
2470
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2408
2471
|
async function runUpdate(input) {
|
|
2409
2472
|
const pkg2 = JSON.parse(
|
|
2410
|
-
|
|
2473
|
+
readFileSync4(new URL("../../package.json", import.meta.url), "utf8")
|
|
2411
2474
|
);
|
|
2412
2475
|
const currentVersion = pkg2.version;
|
|
2413
2476
|
const tag = input.distTag ?? "beta";
|
|
@@ -2464,7 +2527,7 @@ async function runUpdate(input) {
|
|
|
2464
2527
|
}
|
|
2465
2528
|
|
|
2466
2529
|
// src/cli.ts
|
|
2467
|
-
var pkg = JSON.parse(
|
|
2530
|
+
var pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
|
|
2468
2531
|
var program = new Command();
|
|
2469
2532
|
program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(pkg.version);
|
|
2470
2533
|
program.option("--human", "render terminal-readable output instead of JSON");
|
|
@@ -2603,10 +2666,10 @@ program.command("drift [vault]").description("detect content drift in raw source
|
|
|
2603
2666
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2604
2667
|
else emit(await runDrift({ vault: v.vault }));
|
|
2605
2668
|
});
|
|
2606
|
-
program.command("dedup [vault]").description("detect duplicate raw sources by sha256").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2669
|
+
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) => {
|
|
2607
2670
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2608
2671
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2609
|
-
else emit(await runDedup({ vault: v.vault }));
|
|
2672
|
+
else emit(await runDedup({ vault: v.vault, apply: opts.apply }));
|
|
2610
2673
|
});
|
|
2611
2674
|
program.command("migrate-citations [vault]").description("migrate ^[raw/...] markers to paragraph-end citations").option("--dry-run", "preview changes without writing", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2612
2675
|
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.
|
|
3
|
+
"version": "0.2.0-beta.28",
|
|
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": {
|
package/skills/package.json
CHANGED
|
@@ -24,7 +24,8 @@ Standard four reads (SCHEMA, index, log, project context if applicable).
|
|
|
24
24
|
1. Identify the target page. Confirm with the user which page to archive (show full relPath).
|
|
25
25
|
2. Run `skillwiki archive <page> [vault]`. Read the JSON output.
|
|
26
26
|
3. Verify with `skillwiki index-check [vault]` — confirm no ghost entries remain.
|
|
27
|
-
4.
|
|
27
|
+
4. Run `skillwiki lint [vault]` — check for broken wikilinks from other pages that still reference the archived page. If found, update those pages to point to the replacement or remove the stale link.
|
|
28
|
+
5. Append a `log.md` entry: `## [{date}] archive | {relPath} → _archive/{subdir}/`.
|
|
28
29
|
|
|
29
30
|
## Reversibility
|
|
30
31
|
|
|
@@ -18,7 +18,7 @@ Standard four reads.
|
|
|
18
18
|
|
|
19
19
|
0. Resolve vault: `skillwiki path` (record source for context).
|
|
20
20
|
1. Run `skillwiki lint <vault>`. Read the JSON.
|
|
21
|
-
2. Reason over findings; present grouped by severity with concrete suggested actions per kind.
|
|
21
|
+
2. Reason over findings; present grouped by severity with concrete suggested actions per kind. If the CLI was recently updated with new lint checks, re-running lint on the full vault may flag pre-existing pages that predate the new rule — treat these as legitimate findings, not false positives.
|
|
22
22
|
3. If `log_rotate_needed` is present and the user consents, run `skillwiki log-rotate <vault> --apply`. Otherwise leave alone.
|
|
23
23
|
4. Append one `log.md` entry summarizing the lint counts (errors/warnings/info).
|
|
24
24
|
|