skillwiki 0.2.0-beta.20 → 0.2.0-beta.21

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
@@ -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
- const sw = await parseDotenvFile(join2(input.home, ".skillwiki", ".env"));
618
- if (sw.WIKI_PATH !== void 0) {
619
- if (input.explain) chain.push({ source: "skillwiki-dotenv", matched: true, value: sw.WIKI_PATH });
620
- return ok({ path: sw.WIKI_PATH, source: "skillwiki-dotenv", ...input.explain ? { chain } : {} });
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) return { exitCode: ExitCode.NO_VAULT_CONFIGURED, result: r };
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) return { exitCode: ExitCode.NO_VAULT_CONFIGURED, result: r };
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
- await writeDotenv(envPath, { WIKI_PATH: target, WIKI_LANG: canonicalLang }, existingEnvRaw);
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) }) };
@@ -1636,7 +1729,7 @@ import { readFile as readFile12 } from "fs/promises";
1636
1729
  import { existsSync } from "fs";
1637
1730
  import { join as join13 } from "path";
1638
1731
  function validateKey(key) {
1639
- return CONFIG_KEYS.includes(key);
1732
+ return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
1640
1733
  }
1641
1734
  function configPath(home) {
1642
1735
  return join13(home, ".skillwiki", ".env");
@@ -1671,7 +1764,21 @@ async function runConfigSet(input) {
1671
1764
  async function runConfigList(input) {
1672
1765
  const map = await parseDotenvFile(configPath(input.home));
1673
1766
  const entries = Object.entries(map).map(([key, value]) => ({ key, value: value ?? "" }));
1674
- return { exitCode: ExitCode.OK, result: ok({ entries, humanHint: entries.map((e) => `${e.key}=${e.value}`).join("\n") }) };
1767
+ let profiles;
1768
+ if (input.profiles) {
1769
+ const defaultProfile = map["WIKI_DEFAULT"];
1770
+ profiles = [];
1771
+ for (const key of Object.keys(map)) {
1772
+ const m = key.match(/^WIKI_([A-Z][A-Z0-9_]{0,31})_PATH$/);
1773
+ if (m && key !== "WIKI_PATH") {
1774
+ const name = m[1].toLowerCase().replace(/_/g, "-");
1775
+ profiles.push({ name, path: map[key] ?? "", isDefault: name === defaultProfile });
1776
+ }
1777
+ }
1778
+ profiles.sort((a, b) => a.name.localeCompare(b.name));
1779
+ }
1780
+ 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");
1781
+ return { exitCode: ExitCode.OK, result: ok({ entries, profiles, humanHint: hint }) };
1675
1782
  }
1676
1783
  async function runConfigPath(input) {
1677
1784
  const filePath = configPath(input.home);
@@ -1855,6 +1962,34 @@ function checkPluginVersionDrift(home, currentVersion) {
1855
1962
  return check("pass", "plugin_version_drift", "Plugin/CLI version", "Could not read plugin cache");
1856
1963
  }
1857
1964
  }
1965
+ async function checkProfiles(home) {
1966
+ const map = await parseDotenvFile(configPath(home));
1967
+ const profiles = [];
1968
+ for (const key of Object.keys(map)) {
1969
+ if (key.startsWith("WIKI_") && key.endsWith("_PATH") && key !== "WIKI_PATH") {
1970
+ const name = key.slice(5, -5).toLowerCase().replace(/_/g, "-");
1971
+ profiles.push(name);
1972
+ }
1973
+ }
1974
+ if (profiles.length === 0) {
1975
+ return check("pass", "wiki_profiles", "Wiki profiles", "No named profiles configured");
1976
+ }
1977
+ const defaultProfile = map["WIKI_DEFAULT"] ?? "(none)";
1978
+ return check(
1979
+ "pass",
1980
+ "wiki_profiles",
1981
+ "Wiki profiles",
1982
+ `${profiles.length} profile(s): ${profiles.join(", ")}; default: ${defaultProfile}`
1983
+ );
1984
+ }
1985
+ async function checkProjectLocalOverride(cwd) {
1986
+ const dir = cwd ?? process.cwd();
1987
+ const envPath = join15(dir, ".skillwiki", ".env");
1988
+ if (existsSync3(envPath)) {
1989
+ return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
1990
+ }
1991
+ return check("pass", "project_local", "Project-local config", "None");
1992
+ }
1858
1993
  function findSkillMd(dir) {
1859
1994
  const results = [];
1860
1995
  let entries;
@@ -1877,6 +2012,8 @@ async function runDoctor(input) {
1877
2012
  checks.push(checkNodeVersion());
1878
2013
  checks.push(checkCliOnPath(input.argv));
1879
2014
  checks.push(await checkConfigFile(input.home));
2015
+ checks.push(await checkProfiles(input.home));
2016
+ checks.push(await checkProjectLocalOverride(input.cwd));
1880
2017
  const resolved = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
1881
2018
  if (resolved.ok) {
1882
2019
  checks.push(check("pass", "wiki_path_set", "WIKI_PATH configured", `Resolved via ${resolved.data.source}: ${resolved.data.path}`));
@@ -2243,19 +2380,20 @@ function emit(r) {
2243
2380
  program.command("hash <file>").action(async (file) => emit(await runHash({ file })));
2244
2381
  program.command("fetch-guard <url>").action(async (url) => emit(await runFetchGuard({ url })));
2245
2382
  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 })));
2383
+ 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
2384
  program.command("overlap <vault>").action(async (vault) => emit(await runOverlap({ vault })));
2248
- program.command("orphans [vault]").action(async (vault) => emit(await runOrphans({
2385
+ program.command("orphans [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => emit(await runOrphans({
2249
2386
  vault,
2250
2387
  envValue: process.env.WIKI_PATH,
2251
- home: process.env.HOME ?? ""
2388
+ home: process.env.HOME ?? "",
2389
+ wiki: opts.wiki
2252
2390
  })));
2253
2391
  program.command("audit <file>").action(async (file) => emit(await runAudit({ file })));
2254
2392
  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
2393
  const skillsRoot = opts.skillsRoot ?? new URL("../skills/", import.meta.url).pathname;
2256
2394
  emit(await runInstall({ skillsRoot, target: opts.target, dryRun: !!opts.dryRun }));
2257
2395
  });
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) => {
2396
+ 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
2397
  const initTime = !!opts.initTime;
2260
2398
  const flag = initTime ? opts.target : opts.vault;
2261
2399
  emit(await runPath({
@@ -2263,6 +2401,7 @@ program.command("path").option("--vault <dir>", "explicit vault override (runtim
2263
2401
  envValue: process.env.WIKI_PATH,
2264
2402
  home: process.env.HOME ?? "",
2265
2403
  initTime,
2404
+ wiki: opts.wiki,
2266
2405
  explain: !!opts.explain
2267
2406
  }));
2268
2407
  });
@@ -2274,7 +2413,7 @@ program.command("lang").option("--lang <code>", "explicit language override").op
2274
2413
  explain: !!opts.explain
2275
2414
  }));
2276
2415
  });
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) => {
2416
+ 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
2417
  const templates = new URL("../templates/", import.meta.url).pathname;
2279
2418
  const taxonomy = typeof opts.taxonomy === "string" ? opts.taxonomy.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
2280
2419
  emit(await runInit({
@@ -2286,51 +2425,57 @@ program.command("init").option("--target <dir>", "explicit target directory").re
2286
2425
  taxonomy,
2287
2426
  lang: opts.lang,
2288
2427
  force: !!opts.force,
2289
- noEnv: opts.env === false
2428
+ noEnv: opts.env === false,
2429
+ profile: opts.profile
2290
2430
  }));
2291
2431
  });
2292
- async function resolveVaultArg(arg) {
2432
+ async function resolveVaultArg(arg, wiki) {
2293
2433
  if (arg) return { ok: true, vault: arg };
2294
2434
  const r = await resolveRuntimePath({
2295
2435
  flag: void 0,
2296
2436
  envValue: process.env.WIKI_PATH,
2297
- home: process.env.HOME ?? ""
2437
+ wikiEnv: process.env.WIKI,
2438
+ home: process.env.HOME ?? "",
2439
+ wiki
2298
2440
  });
2299
- if (!r.ok) return { ok: false, exitCode: 25, payload: r };
2441
+ if (!r.ok) {
2442
+ const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? 35 : 25;
2443
+ return { ok: false, exitCode, payload: r };
2444
+ }
2300
2445
  return { ok: true, vault: r.data.path };
2301
2446
  }
2302
- program.command("links [vault]").action(async (vault) => {
2303
- const v = await resolveVaultArg(vault);
2447
+ program.command("links [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2448
+ const v = await resolveVaultArg(vault, opts.wiki);
2304
2449
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2305
2450
  else emit(await runLinks({ vault: v.vault }));
2306
2451
  });
2307
- program.command("tag-audit [vault]").action(async (vault) => {
2308
- const v = await resolveVaultArg(vault);
2452
+ program.command("tag-audit [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2453
+ const v = await resolveVaultArg(vault, opts.wiki);
2309
2454
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2310
2455
  else emit(await runTagAudit({ vault: v.vault }));
2311
2456
  });
2312
- program.command("index-check [vault]").action(async (vault) => {
2313
- const v = await resolveVaultArg(vault);
2457
+ program.command("index-check [vault]").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2458
+ const v = await resolveVaultArg(vault, opts.wiki);
2314
2459
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2315
2460
  else emit(await runIndexCheck({ vault: v.vault }));
2316
2461
  });
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);
2462
+ 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) => {
2463
+ const v = await resolveVaultArg(vault, opts.wiki);
2319
2464
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2320
2465
  else emit(await runStale({ vault: v.vault, days: opts.days }));
2321
2466
  });
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);
2467
+ 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) => {
2468
+ const v = await resolveVaultArg(vault, opts.wiki);
2324
2469
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2325
2470
  else emit(await runPagesize({ vault: v.vault, lines: opts.lines }));
2326
2471
  });
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);
2472
+ 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) => {
2473
+ const v = await resolveVaultArg(vault, opts.wiki);
2329
2474
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2330
2475
  else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: !!opts.apply }));
2331
2476
  });
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);
2477
+ 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) => {
2478
+ const v = await resolveVaultArg(vault, opts.wiki);
2334
2479
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2335
2480
  else emit(await runLint({
2336
2481
  vault: v.vault,
@@ -2343,31 +2488,32 @@ program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => p
2343
2488
  var configCmd = program.command("config").description("manage skillwiki configuration");
2344
2489
  configCmd.command("get <key>").description("print the value of a config key").action(async (key) => emit(await runConfigGet({ key, home: process.env.HOME ?? "" })));
2345
2490
  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 ?? "" })));
2491
+ 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
2492
  configCmd.command("path").description("print the config file path").action(async () => emit(await runConfigPath({ home: process.env.HOME ?? "" })));
2348
2493
  program.command("doctor").description("diagnose skillwiki setup issues").action(async () => emit(await runDoctor({
2349
2494
  home: process.env.HOME ?? "",
2350
2495
  envValue: process.env.WIKI_PATH,
2351
2496
  argv: process.argv,
2352
- currentVersion: pkg.version
2497
+ currentVersion: pkg.version,
2498
+ cwd: process.cwd()
2353
2499
  })));
2354
- program.command("archive <page> [vault]").description("archive a typed-knowledge page").action(async (page, vault) => {
2355
- const v = await resolveVaultArg(vault);
2500
+ program.command("archive <page> [vault]").description("archive a typed-knowledge page").option("--wiki <name>", "wiki profile name").action(async (page, vault, opts) => {
2501
+ const v = await resolveVaultArg(vault, opts.wiki);
2356
2502
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2357
2503
  else emit(await runArchive({ vault: v.vault, page }));
2358
2504
  });
2359
- program.command("drift [vault]").description("detect content drift in raw sources").action(async (vault) => {
2360
- const v = await resolveVaultArg(vault);
2505
+ program.command("drift [vault]").description("detect content drift in raw sources").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2506
+ const v = await resolveVaultArg(vault, opts.wiki);
2361
2507
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2362
2508
  else emit(await runDrift({ vault: v.vault }));
2363
2509
  });
2364
- program.command("dedup [vault]").description("detect duplicate raw sources by sha256").action(async (vault) => {
2365
- const v = await resolveVaultArg(vault);
2510
+ program.command("dedup [vault]").description("detect duplicate raw sources by sha256").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
2511
+ const v = await resolveVaultArg(vault, opts.wiki);
2366
2512
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2367
2513
  else emit(await runDedup({ vault: v.vault }));
2368
2514
  });
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);
2515
+ 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) => {
2516
+ const v = await resolveVaultArg(vault, opts.wiki);
2371
2517
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
2372
2518
  else emit(await runMigrateCitations({ vault: v.vault, dryRun: !!opts.dryRun }));
2373
2519
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.2.0-beta.20",
3
+ "version": "0.2.0-beta.21",
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.20",
3
+ "version": "0.2.0-beta.21",
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.20",
3
+ "version": "0.2.0-beta.21",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -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.