skillwiki 0.2.0-beta.15 → 0.2.0-beta.19
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/auto-update-bg.js +43 -0
- package/dist/chunk-XM5IYZX7.js +45 -0
- package/dist/cli.js +256 -22
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/using-skillwiki/SKILL.md +25 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
import {
|
|
4
|
+
semverGt
|
|
5
|
+
} from "./chunk-XM5IYZX7.js";
|
|
6
|
+
|
|
7
|
+
// src/auto-update-bg.ts
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
10
|
+
import { join, dirname } from "path";
|
|
11
|
+
var home = process.argv[2];
|
|
12
|
+
var currentVersion = process.argv[3];
|
|
13
|
+
if (!home || !currentVersion) process.exit(0);
|
|
14
|
+
var cacheFile = join(home, ".skillwiki", ".update-cache.json");
|
|
15
|
+
setTimeout(() => process.exit(0), 3e4);
|
|
16
|
+
try {
|
|
17
|
+
const latest = execSync("npm view skillwiki@beta version", {
|
|
18
|
+
encoding: "utf8",
|
|
19
|
+
timeout: 15e3
|
|
20
|
+
}).trim();
|
|
21
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
22
|
+
const cache = {
|
|
23
|
+
lastCheck: Date.now(),
|
|
24
|
+
latestVersion: latest,
|
|
25
|
+
currentVersion
|
|
26
|
+
};
|
|
27
|
+
if (semverGt(latest, currentVersion)) {
|
|
28
|
+
execSync("npm install -g skillwiki@beta", {
|
|
29
|
+
stdio: "ignore",
|
|
30
|
+
timeout: 6e4
|
|
31
|
+
});
|
|
32
|
+
writeFileSync(cacheFile, JSON.stringify({ ...cache, updateAppliedAt: Date.now() }, null, 2));
|
|
33
|
+
} else {
|
|
34
|
+
writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
try {
|
|
38
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
39
|
+
writeFileSync(cacheFile, JSON.stringify({ lastCheck: Date.now(), latestVersion: "", currentVersion }, null, 2));
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
process.exit(0);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/semver.ts
|
|
4
|
+
function semverGt(a, b) {
|
|
5
|
+
const pa = parseSemver(a);
|
|
6
|
+
const pb = parseSemver(b);
|
|
7
|
+
if (!pa || !pb) return a > b;
|
|
8
|
+
if (pa.major !== pb.major) return pa.major > pb.major;
|
|
9
|
+
if (pa.minor !== pb.minor) return pa.minor > pb.minor;
|
|
10
|
+
if (pa.patch !== pb.patch) return pa.patch > pb.patch;
|
|
11
|
+
if (!pa.pre && pb.pre) return true;
|
|
12
|
+
if (pa.pre && !pb.pre) return false;
|
|
13
|
+
if (!pa.pre && !pb.pre) return false;
|
|
14
|
+
const aParts = pa.pre.split(".");
|
|
15
|
+
const bParts = pb.pre.split(".");
|
|
16
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
17
|
+
for (let i = 0; i < len; i++) {
|
|
18
|
+
const ai = aParts[i];
|
|
19
|
+
const bi = bParts[i];
|
|
20
|
+
if (ai === void 0) return false;
|
|
21
|
+
if (bi === void 0) return true;
|
|
22
|
+
const aNum = parseInt(ai, 10);
|
|
23
|
+
const bNum = parseInt(bi, 10);
|
|
24
|
+
if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
25
|
+
if (aNum !== bNum) return aNum > bNum;
|
|
26
|
+
} else {
|
|
27
|
+
if (ai !== bi) return ai > bi;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
function parseSemver(version) {
|
|
33
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
|
34
|
+
if (!match) return null;
|
|
35
|
+
return {
|
|
36
|
+
major: parseInt(match[1], 10),
|
|
37
|
+
minor: parseInt(match[2], 10),
|
|
38
|
+
patch: parseInt(match[3], 10),
|
|
39
|
+
pre: match[4] ?? null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
semverGt
|
|
45
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
semverGt
|
|
4
|
+
} from "./chunk-XM5IYZX7.js";
|
|
2
5
|
|
|
3
6
|
// src/cli.ts
|
|
4
|
-
import { readFileSync } from "fs";
|
|
7
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
5
8
|
import { Command } from "commander";
|
|
6
9
|
|
|
7
10
|
// src/utils/output.ts
|
|
@@ -745,6 +748,48 @@ function isLegacyCitationStyle(body) {
|
|
|
745
748
|
}
|
|
746
749
|
return false;
|
|
747
750
|
}
|
|
751
|
+
function hasOrphanedCitations(body) {
|
|
752
|
+
const stripped = stripFences(body);
|
|
753
|
+
const lines = stripped.split("\n");
|
|
754
|
+
let inSources = false;
|
|
755
|
+
let sourcesEnded = false;
|
|
756
|
+
let sourcesStartLine = -1;
|
|
757
|
+
let lastNonBlankInSources = -1;
|
|
758
|
+
for (let i = 0; i < lines.length; i++) {
|
|
759
|
+
const line = lines[i];
|
|
760
|
+
const trimmed = line.trim();
|
|
761
|
+
if (/^## Sources\b/.test(trimmed)) {
|
|
762
|
+
inSources = true;
|
|
763
|
+
sourcesStartLine = i;
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (!inSources || sourcesEnded) continue;
|
|
767
|
+
if (trimmed.length === 0) {
|
|
768
|
+
if (lastNonBlankInSources >= 0) {
|
|
769
|
+
sourcesEnded = true;
|
|
770
|
+
}
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
const isListItem = /^\s*[-*]\s+/.test(line);
|
|
774
|
+
const hasMarker = /\^\[raw\//.test(line);
|
|
775
|
+
if (isListItem && hasMarker) {
|
|
776
|
+
lastNonBlankInSources = i;
|
|
777
|
+
} else if (hasMarker && !isListItem) {
|
|
778
|
+
return true;
|
|
779
|
+
} else {
|
|
780
|
+
sourcesEnded = true;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (sourcesStartLine === -1) return false;
|
|
784
|
+
if (sourcesEnded) {
|
|
785
|
+
for (let i = lastNonBlankInSources + 1; i < lines.length; i++) {
|
|
786
|
+
if (/\^\[raw\//.test(lines[i])) {
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
748
793
|
|
|
749
794
|
// src/commands/audit.ts
|
|
750
795
|
async function runAudit(input) {
|
|
@@ -1475,9 +1520,11 @@ async function runDedup(input) {
|
|
|
1475
1520
|
}
|
|
1476
1521
|
|
|
1477
1522
|
// src/commands/lint.ts
|
|
1523
|
+
var STRUCT_MIN_BODY_LINES = 60;
|
|
1524
|
+
var STRUCT_MIN_SECTIONS = 3;
|
|
1478
1525
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "tag_not_in_taxonomy"];
|
|
1479
|
-
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "contested", "orphans", "legacy_citation_style"];
|
|
1480
|
-
var INFO_ORDER = ["bridges", "low_confidence_single_source", "topic_map_recommended"];
|
|
1526
|
+
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "contested", "orphans", "legacy_citation_style", "orphaned_citations"];
|
|
1527
|
+
var INFO_ORDER = ["bridges", "low_confidence_single_source", "page_structure", "topic_map_recommended"];
|
|
1481
1528
|
async function runLint(input) {
|
|
1482
1529
|
const buckets = {};
|
|
1483
1530
|
const links = await runLinks({ vault: input.vault });
|
|
@@ -1523,13 +1570,32 @@ async function runLint(input) {
|
|
|
1523
1570
|
const scan = await scanVault(input.vault);
|
|
1524
1571
|
if (scan.ok) {
|
|
1525
1572
|
const legacyPages = [];
|
|
1573
|
+
const orphanedPages = [];
|
|
1574
|
+
const structFlags = [];
|
|
1526
1575
|
for (const page of scan.data.typedKnowledge) {
|
|
1527
1576
|
const text = await readPage(page);
|
|
1528
1577
|
const split = splitFrontmatter(text);
|
|
1529
1578
|
if (!split.ok) continue;
|
|
1530
|
-
|
|
1579
|
+
const body = split.data.body;
|
|
1580
|
+
if (isLegacyCitationStyle(body)) legacyPages.push(page.relPath);
|
|
1581
|
+
if (hasOrphanedCitations(body)) orphanedPages.push(page.relPath);
|
|
1582
|
+
const bodyLines = body.split("\n").filter((l) => l.trim().length > 0).length;
|
|
1583
|
+
if (bodyLines < STRUCT_MIN_BODY_LINES) {
|
|
1584
|
+
const hasOverview = /^## Overview/m.test(body);
|
|
1585
|
+
const hasRelated = /^## (Related|Relationships)/m.test(body);
|
|
1586
|
+
const sectionCount = (body.match(/^## /gm) ?? []).length;
|
|
1587
|
+
if (!hasOverview || !hasRelated || sectionCount < STRUCT_MIN_SECTIONS) {
|
|
1588
|
+
const reasons = [];
|
|
1589
|
+
if (!hasOverview) reasons.push("no Overview");
|
|
1590
|
+
if (!hasRelated) reasons.push("no Related or Relationships");
|
|
1591
|
+
if (sectionCount < STRUCT_MIN_SECTIONS) reasons.push(`only ${sectionCount} sections`);
|
|
1592
|
+
structFlags.push(`${page.relPath}: ${bodyLines} lines, ${reasons.join(", ")}`);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1531
1595
|
}
|
|
1532
1596
|
if (legacyPages.length > 0) buckets.legacy_citation_style = legacyPages;
|
|
1597
|
+
if (orphanedPages.length > 0) buckets.orphaned_citations = orphanedPages;
|
|
1598
|
+
if (structFlags.length > 0) buckets.page_structure = structFlags;
|
|
1533
1599
|
}
|
|
1534
1600
|
const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
1535
1601
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
@@ -1610,9 +1676,72 @@ async function runConfigPath(input) {
|
|
|
1610
1676
|
}
|
|
1611
1677
|
|
|
1612
1678
|
// src/commands/doctor.ts
|
|
1613
|
-
import { existsSync as
|
|
1614
|
-
import { join as
|
|
1679
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
1680
|
+
import { join as join15 } from "path";
|
|
1615
1681
|
import { execSync } from "child_process";
|
|
1682
|
+
|
|
1683
|
+
// src/utils/auto-update.ts
|
|
1684
|
+
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
1685
|
+
import { join as join14, dirname as dirname6 } from "path";
|
|
1686
|
+
import { spawn } from "child_process";
|
|
1687
|
+
|
|
1688
|
+
// src/utils/update-consts.ts
|
|
1689
|
+
var CACHE_FILENAME = ".update-cache.json";
|
|
1690
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1691
|
+
var ENV_DISABLE_KEY = "NO_UPDATE_NOTIFIER";
|
|
1692
|
+
var CLI_DISABLE_FLAG = "--no-update-notifier";
|
|
1693
|
+
|
|
1694
|
+
// src/utils/auto-update.ts
|
|
1695
|
+
function cachePath(home) {
|
|
1696
|
+
return join14(home, ".skillwiki", CACHE_FILENAME);
|
|
1697
|
+
}
|
|
1698
|
+
function readCacheRaw(home) {
|
|
1699
|
+
try {
|
|
1700
|
+
const raw = readFileSync(cachePath(home), "utf8");
|
|
1701
|
+
return JSON.parse(raw);
|
|
1702
|
+
} catch {
|
|
1703
|
+
return null;
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
function readCache(home) {
|
|
1707
|
+
const cache = readCacheRaw(home);
|
|
1708
|
+
if (!cache) return { cache: null, hasUpdate: false, isStale: true };
|
|
1709
|
+
const isStale = Date.now() - cache.lastCheck >= CHECK_INTERVAL_MS;
|
|
1710
|
+
const hasUpdate = !!cache.latestVersion && semverGt(cache.latestVersion, cache.currentVersion);
|
|
1711
|
+
return { cache, hasUpdate, isStale };
|
|
1712
|
+
}
|
|
1713
|
+
function writeCache(home, cache) {
|
|
1714
|
+
const p = cachePath(home);
|
|
1715
|
+
mkdirSync(dirname6(p), { recursive: true });
|
|
1716
|
+
writeFileSync(p, JSON.stringify(cache, null, 2));
|
|
1717
|
+
}
|
|
1718
|
+
function latestFromCache(home, currentVersion) {
|
|
1719
|
+
const { cache } = readCache(home);
|
|
1720
|
+
if (!cache || !cache.latestVersion) return { hasUpdate: false, latest: null };
|
|
1721
|
+
return {
|
|
1722
|
+
hasUpdate: semverGt(cache.latestVersion, currentVersion),
|
|
1723
|
+
latest: cache.latestVersion
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
function isDisabled() {
|
|
1727
|
+
return !!(process.env[ENV_DISABLE_KEY] || process.env.NODE_ENV === "test" || process.argv.includes(CLI_DISABLE_FLAG));
|
|
1728
|
+
}
|
|
1729
|
+
function triggerAutoUpdate(home, currentVersion) {
|
|
1730
|
+
if (isDisabled()) return;
|
|
1731
|
+
const { isStale } = readCache(home);
|
|
1732
|
+
if (!isStale) return;
|
|
1733
|
+
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
1734
|
+
if (!existsSync2(bgScript)) return;
|
|
1735
|
+
const child = spawn(process.execPath, [bgScript, home, currentVersion], {
|
|
1736
|
+
detached: true,
|
|
1737
|
+
stdio: "ignore"
|
|
1738
|
+
});
|
|
1739
|
+
child.on("error", () => {
|
|
1740
|
+
});
|
|
1741
|
+
child.unref();
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// src/commands/doctor.ts
|
|
1616
1745
|
function check(status, id, label, detail) {
|
|
1617
1746
|
return { id, label, status, detail };
|
|
1618
1747
|
}
|
|
@@ -1639,7 +1768,7 @@ function checkCliOnPath(argv) {
|
|
|
1639
1768
|
}
|
|
1640
1769
|
async function checkConfigFile(home) {
|
|
1641
1770
|
const cfgPath = configPath(home);
|
|
1642
|
-
if (!
|
|
1771
|
+
if (!existsSync3(cfgPath)) {
|
|
1643
1772
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
1644
1773
|
}
|
|
1645
1774
|
try {
|
|
@@ -1654,7 +1783,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
1654
1783
|
if (resolvedPath === void 0) {
|
|
1655
1784
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
1656
1785
|
}
|
|
1657
|
-
if (
|
|
1786
|
+
if (existsSync3(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
1658
1787
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
1659
1788
|
}
|
|
1660
1789
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -1663,13 +1792,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
1663
1792
|
if (resolvedPath === void 0) {
|
|
1664
1793
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
1665
1794
|
}
|
|
1666
|
-
if (!
|
|
1795
|
+
if (!existsSync3(resolvedPath)) {
|
|
1667
1796
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
1668
1797
|
}
|
|
1669
1798
|
const missing = [];
|
|
1670
|
-
if (!
|
|
1799
|
+
if (!existsSync3(join15(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
1671
1800
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
1672
|
-
if (!
|
|
1801
|
+
if (!existsSync3(join15(resolvedPath, dir))) missing.push(dir + "/");
|
|
1673
1802
|
}
|
|
1674
1803
|
if (missing.length === 0) {
|
|
1675
1804
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -1677,8 +1806,8 @@ function checkVaultStructure(resolvedPath) {
|
|
|
1677
1806
|
return check("error", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")}`);
|
|
1678
1807
|
}
|
|
1679
1808
|
function checkSkillsInstalled(home) {
|
|
1680
|
-
const skillsDir =
|
|
1681
|
-
if (!
|
|
1809
|
+
const skillsDir = join15(home, ".claude", "skills");
|
|
1810
|
+
if (!existsSync3(skillsDir)) {
|
|
1682
1811
|
return check("warn", "skills_installed", "Skills installed", `${skillsDir} not found`);
|
|
1683
1812
|
}
|
|
1684
1813
|
const found = findSkillMd(skillsDir);
|
|
@@ -1687,6 +1816,42 @@ function checkSkillsInstalled(home) {
|
|
|
1687
1816
|
}
|
|
1688
1817
|
return check("warn", "skills_installed", "Skills installed", "No SKILL.md files found in ~/.claude/skills/");
|
|
1689
1818
|
}
|
|
1819
|
+
function checkNpmUpdate(home, currentVersion) {
|
|
1820
|
+
const { hasUpdate, latest } = latestFromCache(home, currentVersion);
|
|
1821
|
+
if (!latest) {
|
|
1822
|
+
return check("pass", "npm_update", "npm CLI version", `v${currentVersion} (no cache yet)`);
|
|
1823
|
+
}
|
|
1824
|
+
if (hasUpdate) {
|
|
1825
|
+
return check("warn", "npm_update", "npm CLI version", `v${currentVersion} \u2014 update available: v${latest}. Run \`skillwiki update\`.`);
|
|
1826
|
+
}
|
|
1827
|
+
return check("pass", "npm_update", "npm CLI version", `v${currentVersion} (latest: v${latest})`);
|
|
1828
|
+
}
|
|
1829
|
+
function checkPluginVersionDrift(home, currentVersion) {
|
|
1830
|
+
const pluginJsonPath = join15(home, ".claude", "plugins", "cache", "llm-wiki", "plugin.json");
|
|
1831
|
+
if (!existsSync3(pluginJsonPath)) {
|
|
1832
|
+
return check("pass", "plugin_version_drift", "Plugin/CLI version", "Plugin cache not found \u2014 plugin not installed");
|
|
1833
|
+
}
|
|
1834
|
+
try {
|
|
1835
|
+
const content = readFileSync2(pluginJsonPath, { encoding: "utf8" });
|
|
1836
|
+
const pluginData = JSON.parse(content);
|
|
1837
|
+
const pluginVersion = pluginData.version;
|
|
1838
|
+
if (!pluginVersion) {
|
|
1839
|
+
return check("pass", "plugin_version_drift", "Plugin/CLI version", "Plugin version not found in cache");
|
|
1840
|
+
}
|
|
1841
|
+
if (pluginVersion === currentVersion) {
|
|
1842
|
+
return check("pass", "plugin_version_drift", "Plugin/CLI version", `Both at v${currentVersion}`);
|
|
1843
|
+
}
|
|
1844
|
+
const updateCmd = semverGt(pluginVersion, currentVersion) ? "npm install -g skillwiki@beta" : "claude plugin update skillwiki@llm-wiki";
|
|
1845
|
+
return check(
|
|
1846
|
+
"warn",
|
|
1847
|
+
"plugin_version_drift",
|
|
1848
|
+
"Plugin/CLI version",
|
|
1849
|
+
`Plugin v${pluginVersion} \u2260 CLI v${currentVersion} \u2014 run \`${updateCmd}\``
|
|
1850
|
+
);
|
|
1851
|
+
} catch {
|
|
1852
|
+
return check("pass", "plugin_version_drift", "Plugin/CLI version", "Could not read plugin cache");
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1690
1855
|
function findSkillMd(dir) {
|
|
1691
1856
|
const results = [];
|
|
1692
1857
|
let entries;
|
|
@@ -1697,9 +1862,9 @@ function findSkillMd(dir) {
|
|
|
1697
1862
|
}
|
|
1698
1863
|
for (const entry of entries) {
|
|
1699
1864
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
1700
|
-
results.push(
|
|
1865
|
+
results.push(join15(dir, entry.name));
|
|
1701
1866
|
} else if (entry.isDirectory()) {
|
|
1702
|
-
results.push(...findSkillMd(
|
|
1867
|
+
results.push(...findSkillMd(join15(dir, entry.name)));
|
|
1703
1868
|
}
|
|
1704
1869
|
}
|
|
1705
1870
|
return results;
|
|
@@ -1719,6 +1884,8 @@ async function runDoctor(input) {
|
|
|
1719
1884
|
checks.push(checkWikiPathExists(resolvedPath));
|
|
1720
1885
|
checks.push(checkVaultStructure(resolvedPath));
|
|
1721
1886
|
checks.push(checkSkillsInstalled(input.home));
|
|
1887
|
+
checks.push(checkNpmUpdate(input.home, input.currentVersion));
|
|
1888
|
+
checks.push(checkPluginVersionDrift(input.home, input.currentVersion));
|
|
1722
1889
|
const summary = {
|
|
1723
1890
|
pass: checks.filter((c) => c.status === "pass").length,
|
|
1724
1891
|
warn: checks.filter((c) => c.status === "warn").length,
|
|
@@ -1739,7 +1906,7 @@ async function runDoctor(input) {
|
|
|
1739
1906
|
|
|
1740
1907
|
// src/commands/archive.ts
|
|
1741
1908
|
import { rename as rename3, mkdir as mkdir5, readFile as readFile13, writeFile as writeFile6 } from "fs/promises";
|
|
1742
|
-
import { join as
|
|
1909
|
+
import { join as join16, dirname as dirname7 } from "path";
|
|
1743
1910
|
async function runArchive(input) {
|
|
1744
1911
|
const scan = await scanVault(input.vault);
|
|
1745
1912
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -1751,10 +1918,10 @@ async function runArchive(input) {
|
|
|
1751
1918
|
}
|
|
1752
1919
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
1753
1920
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
1754
|
-
const archivePath =
|
|
1755
|
-
await mkdir5(
|
|
1921
|
+
const archivePath = join16("_archive", relPath);
|
|
1922
|
+
await mkdir5(dirname7(join16(input.vault, archivePath)), { recursive: true });
|
|
1756
1923
|
let indexUpdated = false;
|
|
1757
|
-
const indexPath =
|
|
1924
|
+
const indexPath = join16(input.vault, "index.md");
|
|
1758
1925
|
try {
|
|
1759
1926
|
const idx = await readFile13(indexPath, "utf8");
|
|
1760
1927
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
@@ -1767,7 +1934,7 @@ async function runArchive(input) {
|
|
|
1767
1934
|
} catch (e) {
|
|
1768
1935
|
if (e?.code !== "ENOENT") throw e;
|
|
1769
1936
|
}
|
|
1770
|
-
await rename3(
|
|
1937
|
+
await rename3(join16(input.vault, relPath), join16(input.vault, archivePath));
|
|
1771
1938
|
return { exitCode: ExitCode.OK, result: ok({ archived_from: relPath, archived_to: archivePath, index_updated: indexUpdated, humanHint: `${relPath} -> ${archivePath}${indexUpdated ? " (index updated)" : ""}` }) };
|
|
1772
1939
|
}
|
|
1773
1940
|
|
|
@@ -1999,8 +2166,69 @@ ${migratedBody}${newFooter}`;
|
|
|
1999
2166
|
};
|
|
2000
2167
|
}
|
|
2001
2168
|
|
|
2169
|
+
// src/commands/update.ts
|
|
2170
|
+
import { execSync as execSync2 } from "child_process";
|
|
2171
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2172
|
+
async function runUpdate(input) {
|
|
2173
|
+
const pkg2 = JSON.parse(
|
|
2174
|
+
readFileSync3(new URL("../../package.json", import.meta.url), "utf8")
|
|
2175
|
+
);
|
|
2176
|
+
const currentVersion = pkg2.version;
|
|
2177
|
+
const tag = input.distTag ?? "beta";
|
|
2178
|
+
let latest;
|
|
2179
|
+
try {
|
|
2180
|
+
latest = execSync2(`npm view skillwiki@${tag} version`, {
|
|
2181
|
+
encoding: "utf8",
|
|
2182
|
+
timeout: 15e3
|
|
2183
|
+
}).trim();
|
|
2184
|
+
} catch (e) {
|
|
2185
|
+
return {
|
|
2186
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
2187
|
+
result: err("PREFLIGHT_FAILED", { message: `Failed to query npm registry: ${String(e)}` })
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
const cache = {
|
|
2191
|
+
lastCheck: Date.now(),
|
|
2192
|
+
latestVersion: latest,
|
|
2193
|
+
currentVersion
|
|
2194
|
+
};
|
|
2195
|
+
if (latest === currentVersion) {
|
|
2196
|
+
writeCache(input.home, cache);
|
|
2197
|
+
return {
|
|
2198
|
+
exitCode: ExitCode.OK,
|
|
2199
|
+
result: ok({
|
|
2200
|
+
previousVersion: currentVersion,
|
|
2201
|
+
newVersion: null,
|
|
2202
|
+
wasAlreadyLatest: true,
|
|
2203
|
+
humanHint: `Already on latest ${tag}: v${currentVersion}`
|
|
2204
|
+
})
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
try {
|
|
2208
|
+
execSync2(`npm install -g skillwiki@${tag}`, {
|
|
2209
|
+
stdio: "pipe",
|
|
2210
|
+
timeout: 6e4
|
|
2211
|
+
});
|
|
2212
|
+
} catch (e) {
|
|
2213
|
+
return {
|
|
2214
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
2215
|
+
result: err("PREFLIGHT_FAILED", { message: `npm install failed: ${String(e)}` })
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
writeCache(input.home, { ...cache, updateAppliedAt: Date.now() });
|
|
2219
|
+
return {
|
|
2220
|
+
exitCode: ExitCode.OK,
|
|
2221
|
+
result: ok({
|
|
2222
|
+
previousVersion: currentVersion,
|
|
2223
|
+
newVersion: latest,
|
|
2224
|
+
wasAlreadyLatest: false,
|
|
2225
|
+
humanHint: `Updated skillwiki ${currentVersion} \u2192 ${latest}`
|
|
2226
|
+
})
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2002
2230
|
// src/cli.ts
|
|
2003
|
-
var pkg = JSON.parse(
|
|
2231
|
+
var pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
|
|
2004
2232
|
var program = new Command();
|
|
2005
2233
|
program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(pkg.version);
|
|
2006
2234
|
program.option("--human", "render terminal-readable output instead of JSON");
|
|
@@ -2117,7 +2345,8 @@ configCmd.command("path").description("print the config file path").action(async
|
|
|
2117
2345
|
program.command("doctor").description("diagnose skillwiki setup issues").action(async () => emit(await runDoctor({
|
|
2118
2346
|
home: process.env.HOME ?? "",
|
|
2119
2347
|
envValue: process.env.WIKI_PATH,
|
|
2120
|
-
argv: process.argv
|
|
2348
|
+
argv: process.argv,
|
|
2349
|
+
currentVersion: pkg.version
|
|
2121
2350
|
})));
|
|
2122
2351
|
program.command("archive <page> [vault]").description("archive a typed-knowledge page").action(async (page, vault) => {
|
|
2123
2352
|
const v = await resolveVaultArg(vault);
|
|
@@ -2139,6 +2368,11 @@ program.command("migrate-citations [vault]").description("migrate ^[raw/...] mar
|
|
|
2139
2368
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2140
2369
|
else emit(await runMigrateCitations({ vault: v.vault, dryRun: !!opts.dryRun }));
|
|
2141
2370
|
});
|
|
2371
|
+
program.command("update").description("update skillwiki CLI to the latest version").option("--tag <tag>", "npm dist-tag", "beta").action(async (opts) => emit(await runUpdate({
|
|
2372
|
+
home: process.env.HOME ?? "",
|
|
2373
|
+
distTag: opts.tag
|
|
2374
|
+
})));
|
|
2375
|
+
triggerAutoUpdate(process.env.HOME ?? "", pkg.version);
|
|
2142
2376
|
program.parseAsync(process.argv).catch((e) => {
|
|
2143
2377
|
process.stdout.write(JSON.stringify({ ok: false, error: "INTERNAL", detail: { message: String(e) } }) + "\n");
|
|
2144
2378
|
process.exit(1);
|
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.19",
|
|
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
|
@@ -25,6 +25,31 @@ Invoke a skillwiki skill when the user:
|
|
|
25
25
|
- Has a spec/plan in a non-skillwiki format (CodeStable, RFC, AIDE)
|
|
26
26
|
- Asks about their skillwiki configuration or setup health
|
|
27
27
|
|
|
28
|
+
## Vault Structure
|
|
29
|
+
|
|
30
|
+
A skillwiki vault has two layers:
|
|
31
|
+
|
|
32
|
+
**Layer 1 — Raw (`raw/`):** Immutable source material. Never modify after ingest.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
raw/
|
|
36
|
+
├── articles/ # Web articles, clippings
|
|
37
|
+
├── papers/ # PDFs, arxiv papers
|
|
38
|
+
├── transcripts/ # Meeting notes, interviews
|
|
39
|
+
└── assets/ # Images, diagrams referenced by sources
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Raw frontmatter:
|
|
43
|
+
```yaml
|
|
44
|
+
---
|
|
45
|
+
source_url: https://…
|
|
46
|
+
ingested: YYYY-MM-DD
|
|
47
|
+
sha256: # computed by skillwiki hash over body bytes after closing ---
|
|
48
|
+
---
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Layer 2 — Agent-owned pages:** `entities/`, `concepts/`, `comparisons/`, `queries/`, `meta/`, `projects/`. Citations use `^[raw/articles/source-file.md]` markers at paragraph-end.
|
|
52
|
+
|
|
28
53
|
## Skill Map
|
|
29
54
|
|
|
30
55
|
| Skill | When to Invoke |
|