skillwiki 0.10.98 → 0.10.100

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.
@@ -40,7 +40,7 @@ import {
40
40
  snapshotterAliasForLocalHost,
41
41
  toUndirectedWeighted,
42
42
  writeDotenv
43
- } from "./chunk-MRYWP5HY.js";
43
+ } from "./chunk-B4QTKCEK.js";
44
44
  import {
45
45
  atomicWriteText,
46
46
  prepareTypedPage
@@ -3439,7 +3439,7 @@ function buildCliSurface() {
3439
3439
  fleetCmd.command("context").option("--file <path>").option("--host-id <id>");
3440
3440
  fleetCmd.command("health").option("--file <path>").option("--host-id <id>").option("--json");
3441
3441
  const mcpAuth = program.commands.find((c) => c.name() === "mcp-auth");
3442
- mcpAuth.command("issue-host").requiredOption("--host-id <id>").option("--map <path>").option("--write");
3442
+ mcpAuth.command("issue-host").requiredOption("--host-id <id>").option("--map <path>").option("--allowed-vaults <ids>").option("--write");
3443
3443
  const surface = /* @__PURE__ */ new Map();
3444
3444
  const rootFlags = new Set(program.options.map((o) => o.long ?? o.short).filter((f) => f != null));
3445
3445
  function walk(cmd, prefix, parentFlags) {
package/dist/cli.js CHANGED
@@ -55,7 +55,7 @@ import {
55
55
  snapshotterHealthChecks,
56
56
  upsertIndexEntry,
57
57
  vectorIndexStatus
58
- } from "./chunk-EEO2YDPX.js";
58
+ } from "./chunk-3QT3OFAL.js";
59
59
  import {
60
60
  normalizeDistTag,
61
61
  readCache,
@@ -160,7 +160,7 @@ import {
160
160
  supersedeStaleReviewRequiredJournals,
161
161
  taxonomyCommentForPage,
162
162
  writeDotenv
163
- } from "./chunk-MRYWP5HY.js";
163
+ } from "./chunk-B4QTKCEK.js";
164
164
  import {
165
165
  assertTargetInsideVault,
166
166
  atomicWriteText,
@@ -10646,6 +10646,31 @@ import { readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from
10646
10646
  import { createHash as createHash8, randomBytes } from "crypto";
10647
10647
  import yaml from "js-yaml";
10648
10648
  var HOST_ID_RE = /^[a-z][a-z0-9-]{1,62}$/;
10649
+ function writerIdFromValue(value) {
10650
+ if (typeof value === "string" && value.length > 0) return value;
10651
+ if (value && typeof value === "object" && !Array.isArray(value)) {
10652
+ const rec = value;
10653
+ if (typeof rec.writer_id === "string" && rec.writer_id.length > 0) return rec.writer_id;
10654
+ if (typeof rec.host_id === "string" && rec.host_id.length > 0) return rec.host_id;
10655
+ }
10656
+ return void 0;
10657
+ }
10658
+ function parseAllowedVaultIds(raw) {
10659
+ if (!raw || raw.length === 0) return { vaults: [] };
10660
+ const vaults = [];
10661
+ const seen = /* @__PURE__ */ new Set();
10662
+ for (const item of raw) {
10663
+ const id = item.trim();
10664
+ if (!id) continue;
10665
+ if (id.includes("*") || id.includes("/") || !HOST_ID_RE.test(id)) {
10666
+ return { error: "INVALID_ALLOWED_VAULTS" };
10667
+ }
10668
+ if (seen.has(id)) continue;
10669
+ seen.add(id);
10670
+ vaults.push(id);
10671
+ }
10672
+ return { vaults };
10673
+ }
10649
10674
  function parseMcpTokenMap(yamlText) {
10650
10675
  const parsed = yaml.load(yamlText);
10651
10676
  const map = /* @__PURE__ */ new Map();
@@ -10655,12 +10680,13 @@ function parseMcpTokenMap(yamlText) {
10655
10680
  const records = parsed;
10656
10681
  const nested = records.tokens;
10657
10682
  const source = nested && typeof nested === "object" && !Array.isArray(nested) ? nested : records;
10658
- for (const [hash, hostId] of Object.entries(source)) {
10683
+ for (const [hash, value] of Object.entries(source)) {
10659
10684
  if (hash === "tokens") continue;
10660
- if (typeof hostId !== "string" || hostId.length === 0) continue;
10685
+ const writerId = writerIdFromValue(value);
10686
+ if (!writerId) continue;
10661
10687
  const normalized = hash.trim().toLowerCase();
10662
10688
  if (!/^[0-9a-f]{64}$/.test(normalized)) continue;
10663
- map.set(normalized, hostId);
10689
+ map.set(normalized, writerId);
10664
10690
  }
10665
10691
  return map;
10666
10692
  }
@@ -10670,10 +10696,22 @@ function generateHostBearer(rng) {
10670
10696
  const hashHex = createHash8("sha256").update(raw, "utf8").digest("hex");
10671
10697
  return { raw, hashHex };
10672
10698
  }
10673
- function appendHostHash(yamlText, hashHex, hostId) {
10699
+ function formatHostRow(hashHex, hostId, allowedVaults) {
10700
+ if (allowedVaults.length === 0) {
10701
+ return `${hashHex}: ${hostId}
10702
+ `;
10703
+ }
10704
+ return `${hashHex}:
10705
+ writer_id: ${hostId}
10706
+ allowed_vaults: [${allowedVaults.join(", ")}]
10707
+ `;
10708
+ }
10709
+ function appendHostHash(yamlText, hashHex, hostId, allowedVaults) {
10674
10710
  if (!HOST_ID_RE.test(hostId)) {
10675
10711
  return { error: "INVALID_HOST_ID" };
10676
10712
  }
10713
+ const granted = parseAllowedVaultIds(allowedVaults);
10714
+ if ("error" in granted) return granted;
10677
10715
  const map = parseMcpTokenMap(yamlText);
10678
10716
  const normalizedHash = hashHex.trim().toLowerCase();
10679
10717
  if ([...map.values()].includes(hostId)) {
@@ -10683,8 +10721,7 @@ function appendHostHash(yamlText, hashHex, hostId) {
10683
10721
  return { error: "DUPLICATE_HASH" };
10684
10722
  }
10685
10723
  return { yaml: yamlText.replace(/\s*$/, "") + `
10686
- ${normalizedHash}: ${hostId}
10687
- ` };
10724
+ ${formatHostRow(normalizedHash, hostId, granted.vaults)}` };
10688
10725
  }
10689
10726
 
10690
10727
  // src/commands/mcp-auth.ts
@@ -10706,6 +10743,8 @@ async function runMcpAuthIssueHost(input) {
10706
10743
  const hostId = input.hostId;
10707
10744
  if (!mapPath) return preflight("MAP_PATH_REQUIRED");
10708
10745
  if (!HOST_ID_RE.test(hostId)) return preflight("INVALID_HOST_ID");
10746
+ const granted = parseAllowedVaultIds(input.allowedVaults);
10747
+ if ("error" in granted) return preflight(granted.error);
10709
10748
  if (write && !isTty) return preflight("NO_TTY");
10710
10749
  let yamlText = "";
10711
10750
  try {
@@ -10722,12 +10761,13 @@ async function runMcpAuthIssueHost(input) {
10722
10761
  host_id: hostId,
10723
10762
  hash_prefix: "",
10724
10763
  map_path: mapPath,
10725
- wrote: false
10764
+ wrote: false,
10765
+ allowed_vaults: granted.vaults
10726
10766
  })
10727
10767
  };
10728
10768
  }
10729
10769
  const { raw, hashHex } = generateHostBearer(input.rng);
10730
- const appended = appendHostHash(yamlText, hashHex, hostId);
10770
+ const appended = appendHostHash(yamlText, hashHex, hostId, granted.vaults);
10731
10771
  if ("error" in appended) return preflight(appended.error);
10732
10772
  try {
10733
10773
  writeFile10(mapPath, appended.yaml, { encoding: "utf8", mode: 416 });
@@ -10737,7 +10777,8 @@ async function runMcpAuthIssueHost(input) {
10737
10777
  }
10738
10778
  throw error;
10739
10779
  }
10740
- stderrWrite(`issued host_id=${hostId} copy once: ${raw}
10780
+ const grantNote = granted.vaults.length > 0 ? ` allowed_vaults=${granted.vaults.join(",")}` : "";
10781
+ stderrWrite(`issued host_id=${hostId}${grantNote} copy once: ${raw}
10741
10782
  `);
10742
10783
  return {
10743
10784
  exitCode: ExitCode.OK,
@@ -10745,7 +10786,8 @@ async function runMcpAuthIssueHost(input) {
10745
10786
  host_id: hostId,
10746
10787
  hash_prefix: hashHex.slice(0, 8),
10747
10788
  map_path: mapPath,
10748
- wrote: true
10789
+ wrote: true,
10790
+ allowed_vaults: granted.vaults
10749
10791
  })
10750
10792
  };
10751
10793
  }
@@ -10888,7 +10930,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
10888
10930
  if (dirty) {
10889
10931
  return emit(dirty, void 0, { postCommit: false });
10890
10932
  }
10891
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-THGIM2U7.js");
10933
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-32523BPV.js");
10892
10934
  const run = await runManagedWriteTransaction2({
10893
10935
  vault,
10894
10936
  command,
@@ -12123,12 +12165,14 @@ program.command("mcp").description("start stdio Model Context Protocol server (r
12123
12165
  await runSkillwikiMcpStdio();
12124
12166
  });
12125
12167
  var mcpAuthCmd = program.command("mcp-auth").description("attended HTTP MCP host-id bearer issuance (metal)");
12126
- mcpAuthCmd.command("issue-host").description("append a host-id hash to the metal map; print raw once on a TTY").requiredOption("--host-id <id>", "host identity to bind").option("--map <path>", "hash map file (defaults to SKILLWIKI_MCP_TOKEN_MAP)").option("--write", "append the hash (default is dry-run)", false).action(async (opts) => {
12168
+ mcpAuthCmd.command("issue-host").description("append a host-id hash to the metal map; print raw once on a TTY").requiredOption("--host-id <id>", "host identity to bind").option("--map <path>", "hash map file (defaults to SKILLWIKI_MCP_TOKEN_MAP)").option("--allowed-vaults <ids>", "comma-separated exact vault ids; omit for default vault only").option("--write", "append the hash (default is dry-run)", false).action(async (opts) => {
12127
12169
  const mapPath = opts.map || process.env.SKILLWIKI_MCP_TOKEN_MAP || "";
12170
+ const allowedVaults = String(opts.allowedVaults ?? "").split(/[,\s]+/).map((id) => id.trim()).filter(Boolean);
12128
12171
  emit(await runMcpAuthIssueHost({
12129
12172
  hostId: String(opts.hostId),
12130
12173
  mapPath,
12131
- write: !!opts.write
12174
+ write: !!opts.write,
12175
+ allowedVaults
12132
12176
  }));
12133
12177
  });
12134
12178
  for (const w of getDeprecatedWarnings(process.env.HOME ?? "")) {
@@ -6,7 +6,7 @@ import {
6
6
  runManagedWritePeerGate,
7
7
  runManagedWritePreflight,
8
8
  runManagedWriteTransaction
9
- } from "./chunk-MRYWP5HY.js";
9
+ } from "./chunk-B4QTKCEK.js";
10
10
  import "./chunk-KDH352MC.js";
11
11
  import "./chunk-F5GFUYPF.js";
12
12
  import "./chunk-QM64LUEY.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-EEO2YDPX.js";
4
+ } from "./chunk-3QT3OFAL.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
6
  import "./chunk-XYXTV2YD.js";
7
- import "./chunk-MRYWP5HY.js";
7
+ import "./chunk-B4QTKCEK.js";
8
8
  import "./chunk-KDH352MC.js";
9
9
  import "./chunk-H42EY6E7.js";
10
10
  import "./chunk-F5GFUYPF.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.98",
3
+ "version": "0.10.100",
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.10.98",
3
+ "version": "0.10.100",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.98",
3
+ "version": "0.10.100",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 21 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.98",
3
+ "version": "0.10.100",
4
4
  "description": "Project-aware Karpathy-style knowledge base: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by HTTP MCP wiki_capture.",
5
5
  "author": {
6
6
  "name": "karlorz"
@@ -20,6 +20,11 @@
20
20
  "type": "string",
21
21
  "title": "SkillWiki HTTP MCP bearer",
22
22
  "description": "Bearer for https://wiki.karldigi.dev/mcp. Same SKILLWIKI_MCP_TOKEN used by Grok CLI. Never put the value in plugin files."
23
+ },
24
+ "SKILLWIKI_EXTRA_VAULTS": {
25
+ "type": "string",
26
+ "title": "SkillWiki extra vault ids",
27
+ "description": "Optional comma-separated extra vault ids (example: wiki-fin). Non-secret. Same connector URL https://wiki.karldigi.dev/mcp. Default central stays on. Server allowed_vaults is the security boundary; listing an extra here does not authorize it. Do not put a bearer in this field."
23
28
  }
24
29
  },
25
30
  "required": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.10.98",
3
+ "version": "0.10.100",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -14,7 +14,7 @@ SkillWiki captures are HTTP MCP only (`type: http`). Claude/Grok use `SKILLWIKI_
14
14
  ## First-run readiness
15
15
 
16
16
  - Resolve the installed plugin root from `GROK_PLUGIN_ROOT`, falling back to `CLAUDE_PLUGIN_ROOT`, and run `python3 "$PLUGIN_ROOT/scripts/check_readiness.py" --apply --json` before the first SkillWiki MCP call.
17
- - **Cursor / Grok Bot:** the Cursor-native plugin requires `SKILLWIKI_MCP_TOKEN` under **Plugins → Configure**. It pins `https://wiki.karldigi.dev/mcp`. Grok Bot does not inherit Mac process env or `~/.cursor/mcp.json`. `failed_to_load` with no token box means this package is missing; after this package is installed, Configure is the token field (same pattern as grok-search).
17
+ - **Cursor / Grok Bot:** the Cursor-native plugin requires `SKILLWIKI_MCP_TOKEN` under **Plugins → Configure**. Optional non-secret `SKILLWIKI_EXTRA_VAULTS` (comma-separated vault ids such as `wiki-fin`) opts the client into extra vaults on the **same** connector URL. It is not a second connector, not a secret, and not written to `~/.cursor/mcp.json` or Grok `config.toml`. Server `allowed_vaults` remains the security boundary; an extra listed here but missing from the bearer still fails closed. Omit `vault=` on tools to use the handshake `default_vault` (central). Grok Bot does not inherit Mac process env or `~/.cursor/mcp.json`. `failed_to_load` with no token box means this package is missing; after this package is installed, Configure is the token field (same pattern as grok-search).
18
18
  - `missing_prereq` means `SKILLWIKI_MCP_TOKEN` is absent from process environment; stop and ask for a bearer. Do not invent a stdio MCP.
19
19
  - A new host (a machine that should write the wiki for the first time) needs an issued host-id bearer before handshake can pass. The operator runs `skillwiki mcp-auth issue-host --host-id <id> --write` on metal (TTY required). The client pastes the printed value into process env or host Configure. Do not auto-write `mcp.env`, `mcp.json`, or Grok `config.toml`. Do not invent a second admin skill. A local vault/FUSE mirror is optional.
20
20
  - First-run order: install plugin/CLI → operator issues on metal → paste into process env or Configure → **new session** → `skillwiki doctor --check-mcp` → one write (`wiki_capture`, work-item write, or page publish).
@@ -14,7 +14,7 @@ SkillWiki captures are HTTP MCP only (`type: http`). Claude/Grok use `SKILLWIKI_
14
14
  ## First-run readiness
15
15
 
16
16
  - Resolve the installed plugin root from `GROK_PLUGIN_ROOT`, falling back to `CLAUDE_PLUGIN_ROOT`, and run `python3 "$PLUGIN_ROOT/scripts/check_readiness.py" --apply --json` before the first SkillWiki MCP call.
17
- - **Cursor / Grok Bot:** the Cursor-native plugin requires `SKILLWIKI_MCP_TOKEN` under **Plugins → Configure**. It pins `https://wiki.karldigi.dev/mcp`. Grok Bot does not inherit Mac process env or `~/.cursor/mcp.json`. `failed_to_load` with no token box means this package is missing; after this package is installed, Configure is the token field (same pattern as grok-search).
17
+ - **Cursor / Grok Bot:** the Cursor-native plugin requires `SKILLWIKI_MCP_TOKEN` under **Plugins → Configure**. Optional non-secret `SKILLWIKI_EXTRA_VAULTS` (comma-separated vault ids such as `wiki-fin`) opts the client into extra vaults on the **same** connector URL. It is not a second connector, not a secret, and not written to `~/.cursor/mcp.json` or Grok `config.toml`. Server `allowed_vaults` remains the security boundary; an extra listed here but missing from the bearer still fails closed. Omit `vault=` on tools to use the handshake `default_vault` (central). Grok Bot does not inherit Mac process env or `~/.cursor/mcp.json`. `failed_to_load` with no token box means this package is missing; after this package is installed, Configure is the token field (same pattern as grok-search).
18
18
  - `missing_prereq` means `SKILLWIKI_MCP_TOKEN` is absent from process environment; stop and ask for a bearer. Do not invent a stdio MCP.
19
19
  - A new host (a machine that should write the wiki for the first time) needs an issued host-id bearer before handshake can pass. The operator runs `skillwiki mcp-auth issue-host --host-id <id> --write` on metal (TTY required). The client pastes the printed value into process env or host Configure. Do not auto-write `mcp.env`, `mcp.json`, or Grok `config.toml`. Do not invent a second admin skill. A local vault/FUSE mirror is optional.
20
20
  - First-run order: install plugin/CLI → operator issues on metal → paste into process env or Configure → **new session** → `skillwiki doctor --check-mcp` → one write (`wiki_capture`, work-item write, or page publish).