skillwiki 0.9.38 → 0.9.40

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.
@@ -4405,7 +4405,7 @@ function formatKnownContext(input) {
4405
4405
  const selfAliases = collectSelfAliases(input.manifest, input.hostId);
4406
4406
  const outbound = collectOutboundAccess(input.manifest, input.hostId);
4407
4407
  const maintenanceLines = formatMaintenanceLines(host);
4408
- 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.`;
4409
4409
  return [
4410
4410
  "## Runtime Host Context",
4411
4411
  "",
package/dist/cli.js CHANGED
@@ -75,13 +75,13 @@ import {
75
75
  triggerAutoUpdate,
76
76
  writeCache,
77
77
  writeDotenv
78
- } from "./chunk-G52HKU7I.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
@@ -4797,13 +4797,19 @@ async function postCommit(vault, exitCode) {
4797
4797
  }
4798
4798
 
4799
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";
4800
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 });
4801
4807
  const load = await loadFleetManifestAndHost({
4802
- vault: input.vault,
4808
+ vault: liveVaultPath ?? input.vault,
4803
4809
  hostId: input.hostId,
4804
- env: input.env ?? process.env,
4805
- home: input.home ?? process.env.HOME ?? "",
4806
- cwd: input.cwd ?? process.cwd(),
4810
+ env,
4811
+ home,
4812
+ cwd,
4807
4813
  osHostname: input.osHostname ?? process.env.HOSTNAME,
4808
4814
  user: input.user ?? process.env.USER
4809
4815
  });
@@ -4814,16 +4820,77 @@ async function guardProtectedVaultWrite(input) {
4814
4820
  if (!host || host.role !== "snapshotter" || host.protected !== true) {
4815
4821
  return { blocked: false };
4816
4822
  }
4817
- return {
4818
- blocked: true,
4819
- exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
4820
- result: err("PROTECTED_SNAPSHOTTER_WRITE_BLOCKED", {
4821
- host_id: load.hostId,
4822
- command: input.command,
4823
- reason: `refusing vault mutation from protected snapshotter host '${load.hostId}'`,
4824
- guidance: "Use a leaf authoring host for vault/project writes. Keep protected snapshotter sessions read-only except explicit snapshot maintenance."
4825
- })
4826
- };
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;
4827
4894
  }
4828
4895
 
4829
4896
  // src/cli.ts
@@ -4867,7 +4934,7 @@ program.command("validate <file>").description("validate vault page frontmatter
4867
4934
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
4868
4935
  });
4869
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) => {
4870
- const out = opts.out ?? join25(vault, ".skillwiki", "graph.json");
4937
+ const out = opts.out ?? join26(vault, ".skillwiki", "graph.json");
4871
4938
  return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
4872
4939
  });
4873
4940
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-G52HKU7I.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.38",
3
+ "version": "0.9.40",
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.38",
3
+ "version": "0.9.40",
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.38",
3
+ "version": "0.9.40",
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.38",
3
+ "version": "0.9.40",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -128,6 +128,8 @@ Resolve the active project vault with `skillwiki path` first. Then pass that exa
128
128
 
129
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
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.
132
+
131
133
  Use the local identity check for ordinary runtime context:
132
134
  ```bash
133
135
  VAULT="$(skillwiki --human path | sed 's/ (via.*//')"
@@ -182,7 +182,7 @@ 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. Do not point project work or `fleet context` at `~/wiki-git` unless `skillwiki path` intentionally resolves there.
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
186
 
187
187
  ### Implementation (wiki-snapshot.sh)
188
188
  ```bash
@@ -219,7 +219,7 @@ git reset --hard origin/main
219
219
  bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
220
220
  ```
221
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`):
222
- - 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`**
223
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`
224
224
  - `wiki-sync` skill push is for **explicit** agent/human edit commits only, not automated background pushes
225
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.
@@ -128,6 +128,8 @@ Resolve the active project vault with `skillwiki path` first. Then pass that exa
128
128
 
129
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
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.
132
+
131
133
  Use the local identity check for ordinary runtime context:
132
134
  ```bash
133
135
  VAULT="$(skillwiki --human path | sed 's/ (via.*//')"
@@ -182,7 +182,7 @@ 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. Do not point project work or `fleet context` at `~/wiki-git` unless `skillwiki path` intentionally resolves there.
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
186
 
187
187
  ### Implementation (wiki-snapshot.sh)
188
188
  ```bash
@@ -219,7 +219,7 @@ git reset --hard origin/main
219
219
  bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
220
220
  ```
221
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`):
222
- - 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`**
223
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`
224
224
  - `wiki-sync` skill push is for **explicit** agent/human edit commits only, not automated background pushes
225
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.