skillwiki 0.9.37 → 0.9.39

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.
@@ -1110,7 +1110,13 @@ async function runOrphans(input) {
1110
1110
  if (input.vault) {
1111
1111
  vault = input.vault;
1112
1112
  } else {
1113
- const r = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home ?? "", wiki: input.wiki });
1113
+ const r = await resolveRuntimePath({
1114
+ flag: void 0,
1115
+ envValue: input.envValue,
1116
+ home: input.home ?? "",
1117
+ wiki: input.wiki,
1118
+ cwd: input.cwd
1119
+ });
1114
1120
  if (!r.ok) {
1115
1121
  const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? ExitCode.UNKNOWN_WIKI_PROFILE : ExitCode.NO_VAULT_CONFIGURED;
1116
1122
  return { exitCode, result: r };
@@ -4399,7 +4405,7 @@ function formatKnownContext(input) {
4399
4405
  const selfAliases = collectSelfAliases(input.manifest, input.hostId);
4400
4406
  const outbound = collectOutboundAccess(input.manifest, input.hostId);
4401
4407
  const maintenanceLines = formatMaintenanceLines(host);
4402
- const guidance = host.role === "snapshotter" && host.protected === true ? `this session is already on \`${input.hostId}\`; this is a protected snapshotter host. Do not mutate the vault or repo-local project workspaces from this session except explicitly approved snapshot maintenance. Use read-only investigation here and route authoring to a leaf host.` : input.hostId === "macos-dev" ? "use declared SSH aliases for remote work when needed; do not assume undeclared hosts have reciprocal SSH access." : `this session is already on \`${input.hostId}\`; do not SSH to self aliases unless the user explicitly asks. Do not assume outbound SSH to other fleet hosts is configured.`;
4408
+ const guidance = host.role === "snapshotter" && host.protected === true ? `this session is already on \`${input.hostId}\`; this is a protected snapshotter host. Live-vault authoring at the resolved \`skillwiki path\` is allowed here. Do not mutate snapshot worktrees or repo-local project workspaces from this session except explicitly approved snapshot maintenance. Keep release-validation workflows read-only when they are documented as such.` : input.hostId === "macos-dev" ? "use declared SSH aliases for remote work when needed; do not assume undeclared hosts have reciprocal SSH access." : `this session is already on \`${input.hostId}\`; do not SSH to self aliases unless the user explicitly asks. Do not assume outbound SSH to other fleet hosts is configured.`;
4403
4409
  return [
4404
4410
  "## Runtime Host Context",
4405
4411
  "",
@@ -6393,7 +6399,12 @@ async function runDoctor(input) {
6393
6399
  checks.push(await checkConfigFile(input.home));
6394
6400
  checks.push(await checkProfiles(input.home));
6395
6401
  checks.push(await checkProjectLocalOverride(input.cwd));
6396
- const resolved = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
6402
+ const resolved = await resolveRuntimePath({
6403
+ flag: void 0,
6404
+ envValue: input.envValue,
6405
+ home: input.home,
6406
+ cwd: input.cwd
6407
+ });
6397
6408
  if (resolved.ok) {
6398
6409
  checks.push(check("pass", "wiki_path_set", "WIKI_PATH configured", `Resolved via ${resolved.data.source}: ${resolved.data.path}`));
6399
6410
  } else {
@@ -7923,7 +7934,8 @@ async function resolveMcpVault(input) {
7923
7934
  envValue: process.env.WIKI_PATH,
7924
7935
  wikiEnv: process.env.WIKI,
7925
7936
  home,
7926
- wiki: input.wiki
7937
+ wiki: input.wiki,
7938
+ cwd: input.cwd ?? process.cwd()
7927
7939
  });
7928
7940
  if (!r.ok) return r;
7929
7941
  vaultPath = resolve7(r.data.path);
package/dist/cli.js CHANGED
@@ -75,13 +75,13 @@ import {
75
75
  triggerAutoUpdate,
76
76
  writeCache,
77
77
  writeDotenv
78
- } from "./chunk-TK4ZIQNE.js";
78
+ } from "./chunk-XBYPWCIS.js";
79
79
  import {
80
80
  normalizeDistTag
81
81
  } from "./chunk-E6UWZ3S3.js";
82
82
 
83
83
  // src/cli.ts
84
- import { join as join25 } from "path";
84
+ import { join as join26 } from "path";
85
85
  import { Command } from "commander";
86
86
 
87
87
  // src/utils/output.ts
@@ -425,6 +425,7 @@ async function runPath(input) {
425
425
  flag: input.flag,
426
426
  envValue: input.envValue,
427
427
  home: input.home,
428
+ cwd: input.cwd,
428
429
  explain: input.explain
429
430
  });
430
431
  return { exitCode: ExitCode.OK, result: ok({ path: r2.path, source: r2.source, ...r2.chain ? { chain: r2.chain } : {}, humanHint: `${r2.path} (via ${r2.source})` }) };
@@ -434,6 +435,7 @@ async function runPath(input) {
434
435
  envValue: input.envValue,
435
436
  home: input.home,
436
437
  wiki: input.wiki,
438
+ cwd: input.cwd,
437
439
  explain: input.explain
438
440
  });
439
441
  if (!r.ok) {
@@ -4795,13 +4797,19 @@ async function postCommit(vault, exitCode) {
4795
4797
  }
4796
4798
 
4797
4799
  // src/utils/protected-vault-write-guard.ts
4800
+ import { readFileSync as readFileSync7 } from "fs";
4801
+ import { join as join25, resolve as resolvePath } from "path";
4798
4802
  async function guardProtectedVaultWrite(input) {
4803
+ const env = input.env ?? process.env;
4804
+ const home = input.home ?? process.env.HOME ?? "";
4805
+ const cwd = input.cwd ?? process.cwd();
4806
+ const liveVaultPath = await resolveLiveVaultPath({ env, home, cwd });
4799
4807
  const load = await loadFleetManifestAndHost({
4800
- vault: input.vault,
4808
+ vault: liveVaultPath ?? input.vault,
4801
4809
  hostId: input.hostId,
4802
- env: input.env ?? process.env,
4803
- home: input.home ?? process.env.HOME ?? "",
4804
- cwd: input.cwd ?? process.cwd(),
4810
+ env,
4811
+ home,
4812
+ cwd,
4805
4813
  osHostname: input.osHostname ?? process.env.HOSTNAME,
4806
4814
  user: input.user ?? process.env.USER
4807
4815
  });
@@ -4812,16 +4820,77 @@ async function guardProtectedVaultWrite(input) {
4812
4820
  if (!host || host.role !== "snapshotter" || host.protected !== true) {
4813
4821
  return { blocked: false };
4814
4822
  }
4815
- return {
4816
- blocked: true,
4817
- exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
4818
- result: err("PROTECTED_SNAPSHOTTER_WRITE_BLOCKED", {
4819
- host_id: load.hostId,
4820
- command: input.command,
4821
- reason: `refusing vault mutation from protected snapshotter host '${load.hostId}'`,
4822
- guidance: "Use a leaf authoring host for vault/project writes. Keep protected snapshotter sessions read-only except explicit snapshot maintenance."
4823
- })
4824
- };
4823
+ const snapshotWorktree = resolveSnapshotWorktree(home);
4824
+ const targetVault = resolvePath(input.vault);
4825
+ const canonicalLiveVault = liveVaultPath ? resolvePath(liveVaultPath) : void 0;
4826
+ const canonicalSnapshotWorktree = snapshotWorktree ? resolvePath(snapshotWorktree) : void 0;
4827
+ if (canonicalLiveVault && targetVault === canonicalLiveVault) {
4828
+ return { blocked: false };
4829
+ }
4830
+ if (canonicalSnapshotWorktree && targetVault === canonicalSnapshotWorktree) {
4831
+ return {
4832
+ blocked: true,
4833
+ exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
4834
+ result: err("PROTECTED_SNAPSHOTTER_WRITE_BLOCKED", {
4835
+ host_id: load.hostId,
4836
+ command: input.command,
4837
+ reason: `refusing mutation of snapshot worktree '${canonicalSnapshotWorktree}' on protected snapshotter host '${load.hostId}'`,
4838
+ guidance: canonicalLiveVault ? `Use the live vault path '${canonicalLiveVault}' for authoring. Keep snapshot worktrees read-only except explicit snapshot maintenance.` : "Use the live skillwiki vault path for authoring. Keep snapshot worktrees read-only except explicit snapshot maintenance."
4839
+ })
4840
+ };
4841
+ }
4842
+ if (canonicalLiveVault && targetVault !== canonicalLiveVault) {
4843
+ return {
4844
+ blocked: true,
4845
+ exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
4846
+ result: err("PROTECTED_SNAPSHOTTER_WRITE_BLOCKED", {
4847
+ host_id: load.hostId,
4848
+ command: input.command,
4849
+ reason: `refusing vault mutation outside the live vault path '${canonicalLiveVault}' on protected snapshotter host '${load.hostId}'`,
4850
+ guidance: "Use the resolved live skillwiki vault path for authoring. Keep alternate vault roots and snapshot worktrees read-only unless explicitly approved."
4851
+ })
4852
+ };
4853
+ }
4854
+ return { blocked: false };
4855
+ }
4856
+ async function resolveLiveVaultPath(input) {
4857
+ const resolved = await resolveRuntimePath({
4858
+ flag: void 0,
4859
+ envValue: input.env.WIKI_PATH,
4860
+ wikiEnv: input.env.WIKI,
4861
+ home: input.home,
4862
+ cwd: input.cwd
4863
+ });
4864
+ return resolved.ok ? resolved.data.path : void 0;
4865
+ }
4866
+ function resolveSnapshotWorktree(home) {
4867
+ const skillwikiEnv = join25(home, ".skillwiki", ".env");
4868
+ const explicitWorktree = readEnvKey(skillwikiEnv, ["vault_sync.snapshot_worktree"]);
4869
+ if (explicitWorktree) return explicitWorktree;
4870
+ const snapshotProfile = readEnvKey(skillwikiEnv, ["vault_sync.snapshot_profile"]);
4871
+ if (snapshotProfile) {
4872
+ const fromProfile = readEnvKey(snapshotProfile, ["WIKI_GIT_WORKTREE", "SNAPSHOT_WORKTREE", "GIT_DIR"]);
4873
+ if (fromProfile) return fromProfile;
4874
+ }
4875
+ return "/root/wiki-git";
4876
+ }
4877
+ function readEnvKey(path, keys) {
4878
+ try {
4879
+ const content = readFileSync7(path, "utf8");
4880
+ for (const line of content.split(/\r?\n/)) {
4881
+ const trimmed = line.trim();
4882
+ if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
4883
+ const eq = trimmed.indexOf("=");
4884
+ if (eq <= 0) continue;
4885
+ const key = trimmed.slice(0, eq).trim();
4886
+ if (!keys.includes(key)) continue;
4887
+ const value = trimmed.slice(eq + 1).trim();
4888
+ if (value.length > 0) return value;
4889
+ }
4890
+ } catch {
4891
+ return void 0;
4892
+ }
4893
+ return void 0;
4825
4894
  }
4826
4895
 
4827
4896
  // src/cli.ts
@@ -4865,7 +4934,7 @@ program.command("validate <file>").description("validate vault page frontmatter
4865
4934
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
4866
4935
  });
4867
4936
  program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
4868
- const out = opts.out ?? join25(vault, ".skillwiki", "graph.json");
4937
+ const out = opts.out ?? join26(vault, ".skillwiki", "graph.json");
4869
4938
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
4870
4939
  });
4871
4940
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -4888,7 +4957,8 @@ program.command("orphans [vault]").description("find pages not referenced by any
4888
4957
  vault,
4889
4958
  envValue: process.env.WIKI_PATH,
4890
4959
  home: process.env.HOME ?? "",
4891
- wiki: opts.wiki
4960
+ wiki: opts.wiki,
4961
+ cwd: process.cwd()
4892
4962
  })));
4893
4963
  program.command("audit <file>").description("audit citation markers and source provenance for a vault page").action(async (file) => emit(await runAudit({ file })));
4894
4964
  program.command("install").description("install skillwiki SKILL.md files into ~/.claude/skills/").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)").option("--symlink", "create symlinks instead of copies (dev mode \u2014 edits to source are immediately visible)", false).option("--force", "install CLI copies even when the skillwiki@llm-wiki plugin channel is active", false).action(async (opts) => {
@@ -4904,6 +4974,7 @@ program.command("path").description("show the resolved vault path").option("--va
4904
4974
  home: process.env.HOME ?? "",
4905
4975
  initTime,
4906
4976
  wiki: opts.wiki,
4977
+ cwd: process.cwd(),
4907
4978
  explain: !!opts.explain
4908
4979
  }));
4909
4980
  });
@@ -4938,7 +5009,8 @@ async function resolveVaultArg(arg, wiki) {
4938
5009
  envValue: process.env.WIKI_PATH,
4939
5010
  wikiEnv: process.env.WIKI,
4940
5011
  home: process.env.HOME ?? "",
4941
- wiki
5012
+ wiki,
5013
+ cwd: process.cwd()
4942
5014
  });
4943
5015
  if (!r.ok) {
4944
5016
  const exitCode = r.error === "UNKNOWN_WIKI_PROFILE" ? 35 : 25;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-TK4ZIQNE.js";
4
+ } from "./chunk-XBYPWCIS.js";
5
5
  import "./chunk-E6UWZ3S3.js";
6
6
 
7
7
  // src/mcp-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
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.9.37",
3
+ "version": "0.9.39",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 18 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/skills",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -124,7 +124,11 @@ Key CLI subcommands: `init`, `health`, `lint`, `config`, `doctor`, `path`, `lang
124
124
  Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes doctor, lint, vault-sync, query-readiness, source-freshness, risk flags, and self-check coverage. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only. Run `skillwiki config list` to see current configuration.
125
125
 
126
126
  ## Runtime Host Context and Fleet Freshness
127
- The live output of `skillwiki --human fleet context <vault>` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
127
+ Resolve the active project vault with `skillwiki path` first. Then pass that exact path to `skillwiki --human fleet context <vault>` for host identity and safety guidance. `fleet context` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
128
+
129
+ Do not substitute infrastructure mirrors such as `~/wiki-git` or other snapshot worktrees for the project vault just to inspect fleet status. Those paths are snapshot infrastructure unless `skillwiki path` itself resolves there.
130
+
131
+ On snapshotter hosts, `protected: true` does not by itself mean the live vault is read-only for agent authoring. Treat the resolved `skillwiki path` as the live authoring vault when the host policy allows it, and treat snapshot worktrees such as `~/wiki-git` as protected infrastructure unless the user explicitly asks for snapshot maintenance.
128
132
 
129
133
  Use the local identity check for ordinary runtime context:
130
134
  ```bash
@@ -182,6 +182,8 @@ Some deployments use a cloud-backed vault (`rclone mount`) with a separate git r
182
182
  ~/wiki-git → git repository cloned from GitHub — snapshot target
183
183
  cron hourly → rsync ~/wiki/ → ~/wiki-git/ → git commit → git push
184
184
  ```
185
+ On snapshotter hosts, `~/wiki` remains the active SkillWiki vault for path resolution unless the operator explicitly configures otherwise. `~/wiki-git` is snapshot infrastructure, not the default authoring or dev-loop vault. Agents may author the live vault path when the host policy allows it, but should not point project work or `fleet context` at `~/wiki-git` unless `skillwiki path` intentionally resolves there.
186
+
185
187
  ### Implementation (wiki-snapshot.sh)
186
188
  ```bash
187
189
  #!/bin/bash
@@ -217,7 +219,7 @@ git reset --hard origin/main
217
219
  bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
218
220
  ```
219
221
  **Prevention**: Avoid editing the GitHub repo directly via web interface or uncoordinated clones. The canonical flow is **single-writer-git** (see `concepts/vault-write-authority-model.md`):
220
- - Server (sg01): rclone mount rsync git commit "Snapshot ..." git push — **sole git writer to `main`**
222
+ - Server (sg01): agents may author the live vault at `~/wiki`; the snapshot job promotes cloud-backed live-vault state into `~/wiki-git`, then commits and pushes — **sole git writer to `main`**
221
223
  - macOS/desktop: edit → `wiki-push` rclone copy to S3 (NO git push) → consume sg01 snapshots via `wiki-fetch-notify` (opt-in `WIKI_FETCH_PULL_ON_DELTA=1`) or manual `skillwiki sync`
222
224
  - `wiki-sync` skill push is for **explicit** agent/human edit commits only, not automated background pushes
223
225
  2. **Slow rsync on rclone mounts**: The rclone FUSE mount can be slow for large directory listings. Use `rsync -q` (quiet) to reduce output overhead, and consider `--delete-delay` instead of `--delete` if file churn is high. The rclone mount latency can cause `du` and `find` operations to timeout — this is normal, not an error.
@@ -124,7 +124,11 @@ Key CLI subcommands: `init`, `health`, `lint`, `config`, `doctor`, `path`, `lang
124
124
  Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes doctor, lint, vault-sync, query-readiness, source-freshness, risk flags, and self-check coverage. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only. Run `skillwiki config list` to see current configuration.
125
125
 
126
126
  ## Runtime Host Context and Fleet Freshness
127
- The live output of `skillwiki --human fleet context <vault>` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
127
+ Resolve the active project vault with `skillwiki path` first. Then pass that exact path to `skillwiki --human fleet context <vault>` for host identity and safety guidance. `fleet context` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
128
+
129
+ Do not substitute infrastructure mirrors such as `~/wiki-git` or other snapshot worktrees for the project vault just to inspect fleet status. Those paths are snapshot infrastructure unless `skillwiki path` itself resolves there.
130
+
131
+ On snapshotter hosts, `protected: true` does not by itself mean the live vault is read-only for agent authoring. Treat the resolved `skillwiki path` as the live authoring vault when the host policy allows it, and treat snapshot worktrees such as `~/wiki-git` as protected infrastructure unless the user explicitly asks for snapshot maintenance.
128
132
 
129
133
  Use the local identity check for ordinary runtime context:
130
134
  ```bash
@@ -182,6 +182,8 @@ Some deployments use a cloud-backed vault (`rclone mount`) with a separate git r
182
182
  ~/wiki-git → git repository cloned from GitHub — snapshot target
183
183
  cron hourly → rsync ~/wiki/ → ~/wiki-git/ → git commit → git push
184
184
  ```
185
+ On snapshotter hosts, `~/wiki` remains the active SkillWiki vault for path resolution unless the operator explicitly configures otherwise. `~/wiki-git` is snapshot infrastructure, not the default authoring or dev-loop vault. Agents may author the live vault path when the host policy allows it, but should not point project work or `fleet context` at `~/wiki-git` unless `skillwiki path` intentionally resolves there.
186
+
185
187
  ### Implementation (wiki-snapshot.sh)
186
188
  ```bash
187
189
  #!/bin/bash
@@ -217,7 +219,7 @@ git reset --hard origin/main
217
219
  bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
218
220
  ```
219
221
  **Prevention**: Avoid editing the GitHub repo directly via web interface or uncoordinated clones. The canonical flow is **single-writer-git** (see `concepts/vault-write-authority-model.md`):
220
- - Server (sg01): rclone mount rsync git commit "Snapshot ..." git push — **sole git writer to `main`**
222
+ - Server (sg01): agents may author the live vault at `~/wiki`; the snapshot job promotes cloud-backed live-vault state into `~/wiki-git`, then commits and pushes — **sole git writer to `main`**
221
223
  - macOS/desktop: edit → `wiki-push` rclone copy to S3 (NO git push) → consume sg01 snapshots via `wiki-fetch-notify` (opt-in `WIKI_FETCH_PULL_ON_DELTA=1`) or manual `skillwiki sync`
222
224
  - `wiki-sync` skill push is for **explicit** agent/human edit commits only, not automated background pushes
223
225
  2. **Slow rsync on rclone mounts**: The rclone FUSE mount can be slow for large directory listings. Use `rsync -q` (quiet) to reduce output overhead, and consider `--delete-delay` instead of `--delete` if file churn is high. The rclone mount latency can cause `du` and `find` operations to timeout — this is normal, not an error.