skillwiki 0.2.0-beta.20 → 0.2.0-beta.23
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 +208 -48
- package/package.json +3 -2
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/using-skillwiki/SKILL.md +12 -0
package/dist/cli.js
CHANGED
|
@@ -71,7 +71,8 @@ var ExitCode = {
|
|
|
71
71
|
ARCHIVE_ALREADY_ARCHIVED: 31,
|
|
72
72
|
DRIFT_DETECTED: 32,
|
|
73
73
|
RAW_DEDUP_DETECTED: 33,
|
|
74
|
-
MIGRATION_APPLIED: 34
|
|
74
|
+
MIGRATION_APPLIED: 34,
|
|
75
|
+
UNKNOWN_WIKI_PROFILE: 35
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
// ../shared/src/json-output.ts
|
|
@@ -504,6 +505,16 @@ import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from
|
|
|
504
505
|
import { dirname as dirname2 } from "path";
|
|
505
506
|
var CONFIG_KEYS = ["WIKI_PATH", "WIKI_LANG"];
|
|
506
507
|
var _whitelist = new Set(CONFIG_KEYS);
|
|
508
|
+
var PROFILE_PATH_RE = /^WIKI_([A-Z][A-Z0-9_]{0,31})_PATH$/;
|
|
509
|
+
var PROFILE_LANG_RE = /^WIKI_([A-Z][A-Z0-9_]{0,31})_LANG$/;
|
|
510
|
+
var PROFILE_DEFAULT_RE = /^WIKI_DEFAULT$/;
|
|
511
|
+
function isValidWikiProfileKey(key) {
|
|
512
|
+
if (key === "WIKI_PATH" || key === "WIKI_LANG") return false;
|
|
513
|
+
return PROFILE_PATH_RE.test(key) || PROFILE_LANG_RE.test(key) || PROFILE_DEFAULT_RE.test(key);
|
|
514
|
+
}
|
|
515
|
+
function profileKey(name, suffix) {
|
|
516
|
+
return `WIKI_${name.toUpperCase().replace(/-/g, "_").replace(/[^A-Z0-9_]/g, "")}_${suffix}`;
|
|
517
|
+
}
|
|
507
518
|
function parseDotenvText(text) {
|
|
508
519
|
const out = {};
|
|
509
520
|
for (const rawLine of text.split(/\r?\n/)) {
|
|
@@ -513,7 +524,7 @@ function parseDotenvText(text) {
|
|
|
513
524
|
if (eq <= 0) continue;
|
|
514
525
|
const key = line.slice(0, eq).trim();
|
|
515
526
|
const value = line.slice(eq + 1).trim();
|
|
516
|
-
if (!_whitelist.has(key)) continue;
|
|
527
|
+
if (!_whitelist.has(key) && !isValidWikiProfileKey(key)) continue;
|
|
517
528
|
if (value.length === 0) continue;
|
|
518
529
|
out[key] = value;
|
|
519
530
|
}
|
|
@@ -598,6 +609,14 @@ async function resolveInitTimePath(input) {
|
|
|
598
609
|
return { path: hermes.WIKI_PATH, source: "hermes-dotenv", ...input.explain ? { chain } : {} };
|
|
599
610
|
}
|
|
600
611
|
if (input.explain) chain.push({ source: "hermes-dotenv", matched: false });
|
|
612
|
+
if (input.cwd) {
|
|
613
|
+
const projCfg = await parseDotenvFile(join2(input.cwd, ".skillwiki", ".env"));
|
|
614
|
+
if (projCfg.WIKI_PATH !== void 0) {
|
|
615
|
+
if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
|
|
616
|
+
return { path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (input.explain) chain.push({ source: "project-dotenv", matched: false });
|
|
601
620
|
const fallback = join2(input.home, "wiki");
|
|
602
621
|
if (input.explain) chain.push({ source: "default", matched: true, value: fallback });
|
|
603
622
|
return { path: fallback, source: "default", ...input.explain ? { chain } : {} };
|
|
@@ -609,15 +628,73 @@ async function resolveRuntimePath(input) {
|
|
|
609
628
|
return ok({ path: input.flag, source: "flag", ...input.explain ? { chain } : {} });
|
|
610
629
|
}
|
|
611
630
|
if (input.explain) chain.push({ source: "flag", matched: false });
|
|
631
|
+
const swGlobal = await parseDotenvFile(join2(input.home, ".skillwiki", ".env"));
|
|
632
|
+
const wikiName = input.wiki;
|
|
633
|
+
if (wikiName !== void 0 && wikiName.length > 0) {
|
|
634
|
+
if (wikiName.toLowerCase() === "default") {
|
|
635
|
+
const path2 = swGlobal.WIKI_PATH;
|
|
636
|
+
if (path2 !== void 0) {
|
|
637
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: true, value: path2 });
|
|
638
|
+
return ok({ path: path2, source: "skillwiki-dotenv", ...input.explain ? { chain } : {} });
|
|
639
|
+
}
|
|
640
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: false });
|
|
641
|
+
return err("UNKNOWN_WIKI_PROFILE", {
|
|
642
|
+
message: `Wiki profile "default" not found. Set it with: skillwiki config set wiki.path <dir>`
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const key = profileKey(wikiName, "PATH");
|
|
646
|
+
const path = swGlobal[key];
|
|
647
|
+
if (path !== void 0) {
|
|
648
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: true, value: path });
|
|
649
|
+
return ok({ path, source: "wiki-profile", ...input.explain ? { chain } : {} });
|
|
650
|
+
}
|
|
651
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: false });
|
|
652
|
+
return err("UNKNOWN_WIKI_PROFILE", {
|
|
653
|
+
message: `Wiki profile "${wikiName}" not found. Set it with: skillwiki config set wiki.${wikiName}.path <dir>`
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
if (input.wikiEnv !== void 0 && input.wikiEnv.length > 0) {
|
|
657
|
+
const key = profileKey(input.wikiEnv, "PATH");
|
|
658
|
+
const path = swGlobal[key];
|
|
659
|
+
if (path !== void 0) {
|
|
660
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: true, value: path });
|
|
661
|
+
return ok({ path, source: "wiki-profile", ...input.explain ? { chain } : {} });
|
|
662
|
+
}
|
|
663
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: false });
|
|
664
|
+
return err("UNKNOWN_WIKI_PROFILE", {
|
|
665
|
+
message: `Wiki profile "${input.wikiEnv}" not found (from $WIKI env). Set it with: skillwiki config set wiki.${input.wikiEnv}.path <dir>`
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
if (input.explain) chain.push({ source: "wiki-profile", matched: false });
|
|
612
669
|
if (input.envValue !== void 0 && input.envValue.length > 0) {
|
|
613
670
|
if (input.explain) chain.push({ source: "env", matched: true, value: input.envValue });
|
|
614
671
|
return ok({ path: input.envValue, source: "env", ...input.explain ? { chain } : {} });
|
|
615
672
|
}
|
|
616
673
|
if (input.explain) chain.push({ source: "env", matched: false });
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (
|
|
620
|
-
|
|
674
|
+
if (input.cwd) {
|
|
675
|
+
const projCfg = await parseDotenvFile(join2(input.cwd, ".skillwiki", ".env"));
|
|
676
|
+
if (projCfg.WIKI_PATH !== void 0) {
|
|
677
|
+
if (input.explain) chain.push({ source: "project-dotenv", matched: true, value: projCfg.WIKI_PATH });
|
|
678
|
+
return ok({ path: projCfg.WIKI_PATH, source: "project-dotenv", ...input.explain ? { chain } : {} });
|
|
679
|
+
}
|
|
680
|
+
if (input.explain) chain.push({ source: "project-dotenv", matched: false });
|
|
681
|
+
}
|
|
682
|
+
const defaultProfile = swGlobal["WIKI_DEFAULT"];
|
|
683
|
+
if (defaultProfile !== void 0) {
|
|
684
|
+
const key = profileKey(defaultProfile, "PATH");
|
|
685
|
+
const path = swGlobal[key];
|
|
686
|
+
if (path !== void 0) {
|
|
687
|
+
if (input.explain) chain.push({ source: "wiki-default", matched: true, value: path });
|
|
688
|
+
return ok({ path, source: "wiki-default", ...input.explain ? { chain } : {} });
|
|
689
|
+
}
|
|
690
|
+
if (input.explain) chain.push({ source: "wiki-default", matched: false });
|
|
691
|
+
return err("UNKNOWN_WIKI_PROFILE", {
|
|
692
|
+
message: `Default wiki profile "${defaultProfile}" not found. Set it with: skillwiki config set wiki.${defaultProfile}.path <dir>`
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
if (swGlobal.WIKI_PATH !== void 0) {
|
|
696
|
+
if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: true, value: swGlobal.WIKI_PATH });
|
|
697
|
+
return ok({ path: swGlobal.WIKI_PATH, source: "skillwiki-dotenv", ...input.explain ? { chain } : {} });
|
|
621
698
|
}
|
|
622
699
|
if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: false });
|
|
623
700
|
return err("NO_VAULT_CONFIGURED", {
|
|
@@ -631,8 +708,11 @@ async function runOrphans(input) {
|
|
|
631
708
|
if (input.vault) {
|
|
632
709
|
vault = input.vault;
|
|
633
710
|
} else {
|
|
634
|
-
const r = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home ?? "" });
|
|
635
|
-
if (!r.ok)
|
|
711
|
+
const r = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home ?? "", wiki: input.wiki });
|
|
712
|
+
if (!r.ok) {
|
|
713
|
+
const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? ExitCode.UNKNOWN_WIKI_PROFILE : ExitCode.NO_VAULT_CONFIGURED;
|
|
714
|
+
return { exitCode, result: r };
|
|
715
|
+
}
|
|
636
716
|
vault = r.data.path;
|
|
637
717
|
}
|
|
638
718
|
const scan = await scanVault(vault);
|
|
@@ -953,9 +1033,13 @@ async function runPath(input) {
|
|
|
953
1033
|
flag: input.flag,
|
|
954
1034
|
envValue: input.envValue,
|
|
955
1035
|
home: input.home,
|
|
1036
|
+
wiki: input.wiki,
|
|
956
1037
|
explain: input.explain
|
|
957
1038
|
});
|
|
958
|
-
if (!r.ok)
|
|
1039
|
+
if (!r.ok) {
|
|
1040
|
+
const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? ExitCode.UNKNOWN_WIKI_PROFILE : ExitCode.NO_VAULT_CONFIGURED;
|
|
1041
|
+
return { exitCode, result: r };
|
|
1042
|
+
}
|
|
959
1043
|
return { exitCode: ExitCode.OK, result: ok({ path: r.data.path, source: r.data.source, ...r.data.chain ? { chain: r.data.chain } : {}, humanHint: `${r.data.path} (via ${r.data.source})` }) };
|
|
960
1044
|
}
|
|
961
1045
|
|
|
@@ -1124,13 +1208,13 @@ async function runInit(input) {
|
|
|
1124
1208
|
}
|
|
1125
1209
|
const existingEnv = parseDotenvText(existingEnvRaw);
|
|
1126
1210
|
const swDotenvHadPath = existingEnv.WIKI_PATH !== void 0;
|
|
1127
|
-
if (existingEnv.WIKI_PATH !== void 0 && existingEnv.WIKI_PATH !== target && !input.force) {
|
|
1211
|
+
if (!input.profile && existingEnv.WIKI_PATH !== void 0 && existingEnv.WIKI_PATH !== target && !input.force) {
|
|
1128
1212
|
return {
|
|
1129
1213
|
exitCode: ExitCode.ENV_WRITE_CONFLICT,
|
|
1130
1214
|
result: err("ENV_WRITE_CONFLICT", { key: "WIKI_PATH", existing: existingEnv.WIKI_PATH, attempted: target })
|
|
1131
1215
|
};
|
|
1132
1216
|
}
|
|
1133
|
-
if (existingEnv.WIKI_LANG !== void 0 && existingEnv.WIKI_LANG !== canonicalLang && !input.force) {
|
|
1217
|
+
if (!input.profile && existingEnv.WIKI_LANG !== void 0 && existingEnv.WIKI_LANG !== canonicalLang && !input.force) {
|
|
1134
1218
|
return {
|
|
1135
1219
|
exitCode: ExitCode.ENV_WRITE_CONFLICT,
|
|
1136
1220
|
result: err("ENV_WRITE_CONFLICT", { key: "WIKI_LANG", existing: existingEnv.WIKI_LANG, attempted: canonicalLang })
|
|
@@ -1209,7 +1293,16 @@ async function runInit(input) {
|
|
|
1209
1293
|
let envWritten = "";
|
|
1210
1294
|
if (!skipEnv) {
|
|
1211
1295
|
try {
|
|
1212
|
-
|
|
1296
|
+
const envEntries = {};
|
|
1297
|
+
if (input.profile) {
|
|
1298
|
+
envEntries[profileKey(input.profile, "PATH")] = target;
|
|
1299
|
+
envEntries[profileKey(input.profile, "LANG")] = canonicalLang;
|
|
1300
|
+
envEntries["WIKI_DEFAULT"] = input.profile;
|
|
1301
|
+
} else {
|
|
1302
|
+
envEntries["WIKI_PATH"] = target;
|
|
1303
|
+
envEntries["WIKI_LANG"] = canonicalLang;
|
|
1304
|
+
}
|
|
1305
|
+
await writeDotenv(envPath, envEntries, existingEnvRaw);
|
|
1213
1306
|
envWritten = envPath;
|
|
1214
1307
|
} catch (e) {
|
|
1215
1308
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: envPath, message: String(e) }) };
|
|
@@ -1525,8 +1618,19 @@ async function runDedup(input) {
|
|
|
1525
1618
|
// src/commands/lint.ts
|
|
1526
1619
|
var STRUCT_MIN_BODY_LINES = 60;
|
|
1527
1620
|
var STRUCT_MIN_SECTIONS = 3;
|
|
1621
|
+
function hasDuplicateFrontmatter(body) {
|
|
1622
|
+
if (/^---\r?\n/.test(body)) return true;
|
|
1623
|
+
const lines = body.split(/\r?\n/);
|
|
1624
|
+
const limit = Math.min(lines.length, 20);
|
|
1625
|
+
let seenYamlKey = false;
|
|
1626
|
+
for (let i = 0; i < limit; i++) {
|
|
1627
|
+
if (/^\w[\w-]*:/.test(lines[i].trim())) seenYamlKey = true;
|
|
1628
|
+
if (seenYamlKey && lines[i].trim() === "---") return true;
|
|
1629
|
+
}
|
|
1630
|
+
return false;
|
|
1631
|
+
}
|
|
1528
1632
|
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "tag_not_in_taxonomy"];
|
|
1529
|
-
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "contested", "orphans", "legacy_citation_style", "orphaned_citations"];
|
|
1633
|
+
var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "contested", "orphans", "legacy_citation_style", "orphaned_citations", "duplicate_frontmatter"];
|
|
1530
1634
|
var INFO_ORDER = ["bridges", "low_confidence_single_source", "page_structure", "topic_map_recommended"];
|
|
1531
1635
|
async function runLint(input) {
|
|
1532
1636
|
const buckets = {};
|
|
@@ -1575,11 +1679,13 @@ async function runLint(input) {
|
|
|
1575
1679
|
const legacyPages = [];
|
|
1576
1680
|
const orphanedPages = [];
|
|
1577
1681
|
const structFlags = [];
|
|
1682
|
+
const dupFrontmatter = [];
|
|
1578
1683
|
for (const page of scan.data.typedKnowledge) {
|
|
1579
1684
|
const text = await readPage(page);
|
|
1580
1685
|
const split = splitFrontmatter(text);
|
|
1581
1686
|
if (!split.ok) continue;
|
|
1582
1687
|
const body = split.data.body;
|
|
1688
|
+
if (hasDuplicateFrontmatter(body)) dupFrontmatter.push(page.relPath);
|
|
1583
1689
|
if (isLegacyCitationStyle(body)) legacyPages.push(page.relPath);
|
|
1584
1690
|
if (hasOrphanedCitations(body)) orphanedPages.push(page.relPath);
|
|
1585
1691
|
const bodyLines = body.split("\n").filter((l) => l.trim().length > 0).length;
|
|
@@ -1599,6 +1705,7 @@ async function runLint(input) {
|
|
|
1599
1705
|
if (legacyPages.length > 0) buckets.legacy_citation_style = legacyPages;
|
|
1600
1706
|
if (orphanedPages.length > 0) buckets.orphaned_citations = orphanedPages;
|
|
1601
1707
|
if (structFlags.length > 0) buckets.page_structure = structFlags;
|
|
1708
|
+
if (dupFrontmatter.length > 0) buckets.duplicate_frontmatter = dupFrontmatter;
|
|
1602
1709
|
}
|
|
1603
1710
|
const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
1604
1711
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
@@ -1636,7 +1743,7 @@ import { readFile as readFile12 } from "fs/promises";
|
|
|
1636
1743
|
import { existsSync } from "fs";
|
|
1637
1744
|
import { join as join13 } from "path";
|
|
1638
1745
|
function validateKey(key) {
|
|
1639
|
-
return CONFIG_KEYS.includes(key);
|
|
1746
|
+
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
1640
1747
|
}
|
|
1641
1748
|
function configPath(home) {
|
|
1642
1749
|
return join13(home, ".skillwiki", ".env");
|
|
@@ -1671,7 +1778,21 @@ async function runConfigSet(input) {
|
|
|
1671
1778
|
async function runConfigList(input) {
|
|
1672
1779
|
const map = await parseDotenvFile(configPath(input.home));
|
|
1673
1780
|
const entries = Object.entries(map).map(([key, value]) => ({ key, value: value ?? "" }));
|
|
1674
|
-
|
|
1781
|
+
let profiles;
|
|
1782
|
+
if (input.profiles) {
|
|
1783
|
+
const defaultProfile = map["WIKI_DEFAULT"];
|
|
1784
|
+
profiles = [];
|
|
1785
|
+
for (const key of Object.keys(map)) {
|
|
1786
|
+
const m = key.match(/^WIKI_([A-Z][A-Z0-9_]{0,31})_PATH$/);
|
|
1787
|
+
if (m && key !== "WIKI_PATH") {
|
|
1788
|
+
const name = m[1].toLowerCase().replace(/_/g, "-");
|
|
1789
|
+
profiles.push({ name, path: map[key] ?? "", isDefault: name === defaultProfile });
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
profiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
1793
|
+
}
|
|
1794
|
+
const hint = profiles ? profiles.map((p) => `${p.isDefault ? "* " : " "}${p.name} \u2192 ${p.path}`).join("\n") || "(no profiles)" : entries.map((e) => `${e.key}=${e.value}`).join("\n");
|
|
1795
|
+
return { exitCode: ExitCode.OK, result: ok({ entries, profiles, humanHint: hint }) };
|
|
1675
1796
|
}
|
|
1676
1797
|
async function runConfigPath(input) {
|
|
1677
1798
|
const filePath = configPath(input.home);
|
|
@@ -1855,6 +1976,34 @@ function checkPluginVersionDrift(home, currentVersion) {
|
|
|
1855
1976
|
return check("pass", "plugin_version_drift", "Plugin/CLI version", "Could not read plugin cache");
|
|
1856
1977
|
}
|
|
1857
1978
|
}
|
|
1979
|
+
async function checkProfiles(home) {
|
|
1980
|
+
const map = await parseDotenvFile(configPath(home));
|
|
1981
|
+
const profiles = [];
|
|
1982
|
+
for (const key of Object.keys(map)) {
|
|
1983
|
+
if (key.startsWith("WIKI_") && key.endsWith("_PATH") && key !== "WIKI_PATH") {
|
|
1984
|
+
const name = key.slice(5, -5).toLowerCase().replace(/_/g, "-");
|
|
1985
|
+
profiles.push(name);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
if (profiles.length === 0) {
|
|
1989
|
+
return check("pass", "wiki_profiles", "Wiki profiles", "No named profiles configured");
|
|
1990
|
+
}
|
|
1991
|
+
const defaultProfile = map["WIKI_DEFAULT"] ?? "(none)";
|
|
1992
|
+
return check(
|
|
1993
|
+
"pass",
|
|
1994
|
+
"wiki_profiles",
|
|
1995
|
+
"Wiki profiles",
|
|
1996
|
+
`${profiles.length} profile(s): ${profiles.join(", ")}; default: ${defaultProfile}`
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
async function checkProjectLocalOverride(cwd) {
|
|
2000
|
+
const dir = cwd ?? process.cwd();
|
|
2001
|
+
const envPath = join15(dir, ".skillwiki", ".env");
|
|
2002
|
+
if (existsSync3(envPath)) {
|
|
2003
|
+
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
2004
|
+
}
|
|
2005
|
+
return check("pass", "project_local", "Project-local config", "None");
|
|
2006
|
+
}
|
|
1858
2007
|
function findSkillMd(dir) {
|
|
1859
2008
|
const results = [];
|
|
1860
2009
|
let entries;
|
|
@@ -1877,6 +2026,8 @@ async function runDoctor(input) {
|
|
|
1877
2026
|
checks.push(checkNodeVersion());
|
|
1878
2027
|
checks.push(checkCliOnPath(input.argv));
|
|
1879
2028
|
checks.push(await checkConfigFile(input.home));
|
|
2029
|
+
checks.push(await checkProfiles(input.home));
|
|
2030
|
+
checks.push(await checkProjectLocalOverride(input.cwd));
|
|
1880
2031
|
const resolved = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
|
|
1881
2032
|
if (resolved.ok) {
|
|
1882
2033
|
checks.push(check("pass", "wiki_path_set", "WIKI_PATH configured", `Resolved via ${resolved.data.source}: ${resolved.data.path}`));
|
|
@@ -2243,19 +2394,20 @@ function emit(r) {
|
|
|
2243
2394
|
program.command("hash <file>").action(async (file) => emit(await runHash({ file })));
|
|
2244
2395
|
program.command("fetch-guard <url>").action(async (url) => emit(await runFetchGuard({ url })));
|
|
2245
2396
|
program.command("validate <file>").action(async (file) => emit(await runValidate({ file })));
|
|
2246
|
-
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path", ".skillwiki/graph.json").action(async (vault, opts) => emit(await runGraphBuild({ vault, out: opts.out })));
|
|
2397
|
+
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path", ".skillwiki/graph.json").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => emit(await runGraphBuild({ vault, out: opts.out })));
|
|
2247
2398
|
program.command("overlap <vault>").action(async (vault) => emit(await runOverlap({ vault })));
|
|
2248
|
-
program.command("orphans [vault]").action(async (vault) => emit(await runOrphans({
|
|
2399
|
+
program.command("orphans [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => emit(await runOrphans({
|
|
2249
2400
|
vault,
|
|
2250
2401
|
envValue: process.env.WIKI_PATH,
|
|
2251
|
-
home: process.env.HOME ?? ""
|
|
2402
|
+
home: process.env.HOME ?? "",
|
|
2403
|
+
wiki: opts.wiki
|
|
2252
2404
|
})));
|
|
2253
2405
|
program.command("audit <file>").action(async (file) => emit(await runAudit({ file })));
|
|
2254
2406
|
program.command("install").option("--target <dir>", "target install directory", `${process.env.HOME ?? ""}/.claude/skills/`).option("--dry-run", "preview only", false).option("--skills-root <dir>", "source skills directory (defaults to packaged)").action(async (opts) => {
|
|
2255
2407
|
const skillsRoot = opts.skillsRoot ?? new URL("../skills/", import.meta.url).pathname;
|
|
2256
2408
|
emit(await runInstall({ skillsRoot, target: opts.target, dryRun: !!opts.dryRun }));
|
|
2257
2409
|
});
|
|
2258
|
-
program.command("path").option("--vault <dir>", "explicit vault override (runtime)").option("--target <dir>", "explicit target override (init-time)").option("--init-time", "use init-time chain instead of runtime", false).option("--explain", "include resolution chain in output", false).action(async (opts) => {
|
|
2410
|
+
program.command("path").option("--vault <dir>", "explicit vault override (runtime)").option("--target <dir>", "explicit target override (init-time)").option("--wiki <name>", "wiki profile name").option("--init-time", "use init-time chain instead of runtime", false).option("--explain", "include resolution chain in output", false).action(async (opts) => {
|
|
2259
2411
|
const initTime = !!opts.initTime;
|
|
2260
2412
|
const flag = initTime ? opts.target : opts.vault;
|
|
2261
2413
|
emit(await runPath({
|
|
@@ -2263,6 +2415,7 @@ program.command("path").option("--vault <dir>", "explicit vault override (runtim
|
|
|
2263
2415
|
envValue: process.env.WIKI_PATH,
|
|
2264
2416
|
home: process.env.HOME ?? "",
|
|
2265
2417
|
initTime,
|
|
2418
|
+
wiki: opts.wiki,
|
|
2266
2419
|
explain: !!opts.explain
|
|
2267
2420
|
}));
|
|
2268
2421
|
});
|
|
@@ -2274,7 +2427,7 @@ program.command("lang").option("--lang <code>", "explicit language override").op
|
|
|
2274
2427
|
explain: !!opts.explain
|
|
2275
2428
|
}));
|
|
2276
2429
|
});
|
|
2277
|
-
program.command("init").option("--target <dir>", "explicit target directory").requiredOption("--domain <text>", "knowledge domain seed").option("--taxonomy <csv>", "comma-separated tag list").option("--lang <code>", "output language (BCP 47 or alias)").option("--force", "override existing target / env conflict", false).option("--no-env", "skip writing ~/.skillwiki/.env").action(async (opts) => {
|
|
2430
|
+
program.command("init").option("--target <dir>", "explicit target directory").requiredOption("--domain <text>", "knowledge domain seed").option("--taxonomy <csv>", "comma-separated tag list").option("--lang <code>", "output language (BCP 47 or alias)").option("--force", "override existing target / env conflict", false).option("--no-env", "skip writing ~/.skillwiki/.env").option("--profile <name>", "write as named wiki profile instead of WIKI_PATH").action(async (opts) => {
|
|
2278
2431
|
const templates = new URL("../templates/", import.meta.url).pathname;
|
|
2279
2432
|
const taxonomy = typeof opts.taxonomy === "string" ? opts.taxonomy.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
|
|
2280
2433
|
emit(await runInit({
|
|
@@ -2286,51 +2439,57 @@ program.command("init").option("--target <dir>", "explicit target directory").re
|
|
|
2286
2439
|
taxonomy,
|
|
2287
2440
|
lang: opts.lang,
|
|
2288
2441
|
force: !!opts.force,
|
|
2289
|
-
noEnv: opts.env === false
|
|
2442
|
+
noEnv: opts.env === false,
|
|
2443
|
+
profile: opts.profile
|
|
2290
2444
|
}));
|
|
2291
2445
|
});
|
|
2292
|
-
async function resolveVaultArg(arg) {
|
|
2446
|
+
async function resolveVaultArg(arg, wiki) {
|
|
2293
2447
|
if (arg) return { ok: true, vault: arg };
|
|
2294
2448
|
const r = await resolveRuntimePath({
|
|
2295
2449
|
flag: void 0,
|
|
2296
2450
|
envValue: process.env.WIKI_PATH,
|
|
2297
|
-
|
|
2451
|
+
wikiEnv: process.env.WIKI,
|
|
2452
|
+
home: process.env.HOME ?? "",
|
|
2453
|
+
wiki
|
|
2298
2454
|
});
|
|
2299
|
-
if (!r.ok)
|
|
2455
|
+
if (!r.ok) {
|
|
2456
|
+
const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? 35 : 25;
|
|
2457
|
+
return { ok: false, exitCode, payload: r };
|
|
2458
|
+
}
|
|
2300
2459
|
return { ok: true, vault: r.data.path };
|
|
2301
2460
|
}
|
|
2302
|
-
program.command("links [vault]").action(async (vault) => {
|
|
2303
|
-
const v = await resolveVaultArg(vault);
|
|
2461
|
+
program.command("links [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2462
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2304
2463
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2305
2464
|
else emit(await runLinks({ vault: v.vault }));
|
|
2306
2465
|
});
|
|
2307
|
-
program.command("tag-audit [vault]").action(async (vault) => {
|
|
2308
|
-
const v = await resolveVaultArg(vault);
|
|
2466
|
+
program.command("tag-audit [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2467
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2309
2468
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2310
2469
|
else emit(await runTagAudit({ vault: v.vault }));
|
|
2311
2470
|
});
|
|
2312
|
-
program.command("index-check [vault]").action(async (vault) => {
|
|
2313
|
-
const v = await resolveVaultArg(vault);
|
|
2471
|
+
program.command("index-check [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2472
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2314
2473
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2315
2474
|
else emit(await runIndexCheck({ vault: v.vault }));
|
|
2316
2475
|
});
|
|
2317
|
-
program.command("stale [vault]").option("--days <n>", "staleness threshold in days", (s) => parseInt(s, 10), 90).action(async (vault, opts) => {
|
|
2318
|
-
const v = await resolveVaultArg(vault);
|
|
2476
|
+
program.command("stale [vault]").option("--days <n>", "staleness threshold in days", (s) => parseInt(s, 10), 90).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2477
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2319
2478
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2320
2479
|
else emit(await runStale({ vault: v.vault, days: opts.days }));
|
|
2321
2480
|
});
|
|
2322
|
-
program.command("pagesize [vault]").option("--lines <n>", "max body lines", (s) => parseInt(s, 10), 200).action(async (vault, opts) => {
|
|
2323
|
-
const v = await resolveVaultArg(vault);
|
|
2481
|
+
program.command("pagesize [vault]").option("--lines <n>", "max body lines", (s) => parseInt(s, 10), 200).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2482
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2324
2483
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2325
2484
|
else emit(await runPagesize({ vault: v.vault, lines: opts.lines }));
|
|
2326
2485
|
});
|
|
2327
|
-
program.command("log-rotate [vault]").option("--threshold <n>", "entry count threshold", (s) => parseInt(s, 10), 500).option("--apply", "actually rotate", false).action(async (vault, opts) => {
|
|
2328
|
-
const v = await resolveVaultArg(vault);
|
|
2486
|
+
program.command("log-rotate [vault]").option("--threshold <n>", "entry count threshold", (s) => parseInt(s, 10), 500).option("--apply", "actually rotate", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2487
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2329
2488
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2330
2489
|
else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: !!opts.apply }));
|
|
2331
2490
|
});
|
|
2332
|
-
program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).action(async (vault, opts) => {
|
|
2333
|
-
const v = await resolveVaultArg(vault);
|
|
2491
|
+
program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2492
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2334
2493
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2335
2494
|
else emit(await runLint({
|
|
2336
2495
|
vault: v.vault,
|
|
@@ -2343,31 +2502,32 @@ program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => p
|
|
|
2343
2502
|
var configCmd = program.command("config").description("manage skillwiki configuration");
|
|
2344
2503
|
configCmd.command("get <key>").description("print the value of a config key").action(async (key) => emit(await runConfigGet({ key, home: process.env.HOME ?? "" })));
|
|
2345
2504
|
configCmd.command("set <key> <value>").description("set a config key value").action(async (key, value) => emit(await runConfigSet({ key, value, home: process.env.HOME ?? "" })));
|
|
2346
|
-
configCmd.command("list").description("list all config key=value pairs").action(async () => emit(await runConfigList({ home: process.env.HOME ?? "" })));
|
|
2505
|
+
configCmd.command("list").option("--profiles", "show wiki profiles summary", false).description("list all config key=value pairs").action(async (opts) => emit(await runConfigList({ home: process.env.HOME ?? "", profiles: !!opts.profiles })));
|
|
2347
2506
|
configCmd.command("path").description("print the config file path").action(async () => emit(await runConfigPath({ home: process.env.HOME ?? "" })));
|
|
2348
2507
|
program.command("doctor").description("diagnose skillwiki setup issues").action(async () => emit(await runDoctor({
|
|
2349
2508
|
home: process.env.HOME ?? "",
|
|
2350
2509
|
envValue: process.env.WIKI_PATH,
|
|
2351
2510
|
argv: process.argv,
|
|
2352
|
-
currentVersion: pkg.version
|
|
2511
|
+
currentVersion: pkg.version,
|
|
2512
|
+
cwd: process.cwd()
|
|
2353
2513
|
})));
|
|
2354
|
-
program.command("archive <page> [vault]").description("archive a typed-knowledge page").action(async (page, vault) => {
|
|
2355
|
-
const v = await resolveVaultArg(vault);
|
|
2514
|
+
program.command("archive <page> [vault]").description("archive a typed-knowledge page").option("--wiki <name>", "wiki profile name").action(async (page, vault, opts) => {
|
|
2515
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2356
2516
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2357
2517
|
else emit(await runArchive({ vault: v.vault, page }));
|
|
2358
2518
|
});
|
|
2359
|
-
program.command("drift [vault]").description("detect content drift in raw sources").action(async (vault) => {
|
|
2360
|
-
const v = await resolveVaultArg(vault);
|
|
2519
|
+
program.command("drift [vault]").description("detect content drift in raw sources").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2520
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2361
2521
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2362
2522
|
else emit(await runDrift({ vault: v.vault }));
|
|
2363
2523
|
});
|
|
2364
|
-
program.command("dedup [vault]").description("detect duplicate raw sources by sha256").action(async (vault) => {
|
|
2365
|
-
const v = await resolveVaultArg(vault);
|
|
2524
|
+
program.command("dedup [vault]").description("detect duplicate raw sources by sha256").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2525
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2366
2526
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2367
2527
|
else emit(await runDedup({ vault: v.vault }));
|
|
2368
2528
|
});
|
|
2369
|
-
program.command("migrate-citations [vault]").description("migrate ^[raw/...] markers to paragraph-end citations").option("--dry-run", "preview changes without writing", false).action(async (vault, opts) => {
|
|
2370
|
-
const v = await resolveVaultArg(vault);
|
|
2529
|
+
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) => {
|
|
2530
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2371
2531
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2372
2532
|
else emit(await runMigrateCitations({ vault: v.vault, dryRun: !!opts.dryRun }));
|
|
2373
2533
|
});
|
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.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skillwiki": "dist/cli.js"
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"build": "tsup && rm -rf ./skills && cp -r ../skills ./skills",
|
|
16
16
|
"test": "vitest run",
|
|
17
17
|
"test:watch": "vitest",
|
|
18
|
-
"typecheck": "tsc --noEmit"
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
22
|
"commander": "^12.1.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.23",
|
|
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
|
@@ -88,3 +88,15 @@ Run `skillwiki doctor` to diagnose setup issues. Run `skillwiki config list` to
|
|
|
88
88
|
For longer-running project work, use `proj-init` → `proj-work` → `proj-distill` / `proj-decide`.
|
|
89
89
|
|
|
90
90
|
Maintenance: **Archive** (`wiki-archive`) superseded pages, **Drift** (`wiki-reingest`) to detect stale sources, **Adapter** (`wiki-adapter-prd`) for foreign PRD format ingestion.
|
|
91
|
+
|
|
92
|
+
## Multi-Wiki Profiles
|
|
93
|
+
|
|
94
|
+
skillwiki supports named wiki profiles for working with multiple vaults. Set `WIKI_DEFAULT` to control which wiki all skills target by default.
|
|
95
|
+
|
|
96
|
+
**Manage profiles:**
|
|
97
|
+
- `skillwiki config set wiki.<name>.path <dir>` — register a profile
|
|
98
|
+
- `skillwiki config set default <name>` — set active profile
|
|
99
|
+
- `skillwiki config list --profiles` — list all profiles
|
|
100
|
+
- `skillwiki --wiki <name> lint` — override per-command
|
|
101
|
+
|
|
102
|
+
**Project-local override:** Place a `./skillwiki/.env` in a project root to bind that project to a specific wiki. Skills will use it automatically when running from that directory.
|