skillwiki 0.10.2 → 0.10.3

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.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ FLEET_REL_PATH,
3
4
  findReviewRequiredOp,
4
5
  git,
5
6
  hasActiveGitSequencer,
@@ -14,6 +15,10 @@ import {
14
15
  ok
15
16
  } from "./chunk-C5OLZRRM.js";
16
17
 
18
+ // src/utils/managed-write-preflight.ts
19
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
20
+ import { join as join2, resolve } from "path";
21
+
17
22
  // src/utils/managed-write-lock.ts
18
23
  import { randomBytes } from "crypto";
19
24
  import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
@@ -78,19 +83,52 @@ function preflightBlocker(vault) {
78
83
  if (op) return { reason: "review-required", operation_id: op };
79
84
  return null;
80
85
  }
86
+ function fleetManifestBytes(vault) {
87
+ const path = join2(vault, FLEET_REL_PATH);
88
+ if (!existsSync(path)) return null;
89
+ return readFileSync2(path);
90
+ }
91
+ function isGitVault(vault) {
92
+ return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
93
+ }
81
94
  async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
82
95
  const vault = input.vault;
83
- const blocker = preflightBlocker(vault);
84
- if (blocker) {
96
+ const convergenceVault = input.convergenceVault && resolve(input.convergenceVault) !== resolve(vault) ? resolve(input.convergenceVault) : void 0;
97
+ const gitVault = convergenceVault ?? vault;
98
+ const mutationBlocker = preflightBlocker(vault);
99
+ if (mutationBlocker) {
85
100
  return {
86
101
  exitCode: ExitCode.PREFLIGHT_FAILED,
87
102
  result: err("PREFLIGHT_FAILED", {
88
- reason: blocker.reason,
89
- operation_id: blocker.operation_id,
90
- unmerged_paths: blocker.unmerged_paths
103
+ reason: mutationBlocker.reason,
104
+ operation_id: mutationBlocker.operation_id,
105
+ unmerged_paths: mutationBlocker.unmerged_paths
91
106
  })
92
107
  };
93
108
  }
109
+ if (convergenceVault) {
110
+ if (!isGitVault(convergenceVault)) {
111
+ return {
112
+ exitCode: ExitCode.PREFLIGHT_FAILED,
113
+ result: err("PREFLIGHT_FAILED", {
114
+ reason: "convergence-vault-not-git",
115
+ convergence_vault: convergenceVault
116
+ })
117
+ };
118
+ }
119
+ const gitBlocker = preflightBlocker(convergenceVault);
120
+ if (gitBlocker) {
121
+ return {
122
+ exitCode: ExitCode.PREFLIGHT_FAILED,
123
+ result: err("PREFLIGHT_FAILED", {
124
+ reason: gitBlocker.reason,
125
+ operation_id: gitBlocker.operation_id,
126
+ unmerged_paths: gitBlocker.unmerged_paths,
127
+ convergence_vault: convergenceVault
128
+ })
129
+ };
130
+ }
131
+ }
94
132
  const fleet = await loadFleetManifestAndHost({
95
133
  vault,
96
134
  hostId: input.hostId,
@@ -98,14 +136,16 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
98
136
  home: input.home,
99
137
  osHostname: input.osHostname
100
138
  });
139
+ const dualPathMeta = convergenceVault ? { convergence_vault: convergenceVault } : {};
101
140
  if (!fleet) {
102
- const head = git(vault, ["rev-parse", "HEAD"]) || null;
141
+ const head = git(gitVault, ["rev-parse", "HEAD"]) || null;
103
142
  return {
104
143
  exitCode: ExitCode.OK,
105
144
  result: ok({
106
145
  mode: "standalone",
107
146
  base_oid: head,
108
- converged: false
147
+ converged: false,
148
+ ...dualPathMeta
109
149
  })
110
150
  };
111
151
  }
@@ -119,6 +159,27 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
119
159
  })
120
160
  };
121
161
  }
162
+ if (convergenceVault && fleetManifestBytes(convergenceVault)) {
163
+ const convergeFleetCtx = await loadFleetManifestAndHost({
164
+ vault: convergenceVault,
165
+ hostId: input.hostId ?? fleet.hostId,
166
+ env: input.env,
167
+ home: input.home,
168
+ osHostname: input.osHostname
169
+ });
170
+ if (!convergeFleetCtx || convergeFleetCtx.identityStatus !== "known" || convergeFleetCtx.hostId !== fleet.hostId) {
171
+ return {
172
+ exitCode: ExitCode.PREFLIGHT_FAILED,
173
+ result: err("PREFLIGHT_FAILED", {
174
+ reason: "convergence-vault-identity-mismatch",
175
+ host_id: fleet.hostId,
176
+ convergence_host_id: convergeFleetCtx?.hostId,
177
+ convergence_identity_status: convergeFleetCtx?.identityStatus,
178
+ convergence_vault: convergenceVault
179
+ })
180
+ };
181
+ }
182
+ }
122
183
  const host = fleet.manifest.hosts[fleet.hostId];
123
184
  if (!host) {
124
185
  return {
@@ -134,12 +195,13 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
134
195
  mode: "immutable-record",
135
196
  host_id: fleet.hostId,
136
197
  base_oid: null,
137
- converged: false
198
+ converged: false,
199
+ ...dualPathMeta
138
200
  })
139
201
  };
140
202
  }
141
203
  const converge = await deps.converge({
142
- vault,
204
+ vault: gitVault,
143
205
  lockToken: input.lockToken,
144
206
  env: input.env,
145
207
  home: input.home
@@ -148,11 +210,14 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
148
210
  const exitCode = converge.error === "PREFLIGHT_FAILED" ? ExitCode.PREFLIGHT_FAILED : ExitCode.SYNC_PULL_FAILED;
149
211
  return { exitCode, result: converge };
150
212
  }
151
- const baseOid = git(vault, ["rev-parse", "HEAD"]);
213
+ const baseOid = git(gitVault, ["rev-parse", "HEAD"]);
152
214
  if (!baseOid) {
153
215
  return {
154
216
  exitCode: ExitCode.PREFLIGHT_FAILED,
155
- result: err("PREFLIGHT_FAILED", { reason: "missing-head-after-converge" })
217
+ result: err("PREFLIGHT_FAILED", {
218
+ reason: "missing-head-after-converge",
219
+ ...dualPathMeta
220
+ })
156
221
  };
157
222
  }
158
223
  return {
@@ -162,7 +227,8 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
162
227
  host_id: fleet.hostId,
163
228
  base_oid: baseOid,
164
229
  converged: true,
165
- helper_path: converge.data.helper_path
230
+ helper_path: converge.data.helper_path,
231
+ ...dualPathMeta
166
232
  })
167
233
  };
168
234
  }
@@ -177,6 +243,7 @@ async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
177
243
  {
178
244
  vault: input.vault,
179
245
  command: input.command,
246
+ convergenceVault: input.convergenceVault,
180
247
  hostId: input.hostId,
181
248
  lockToken: handle.ownerToken,
182
249
  env: input.env,
package/dist/cli.js CHANGED
@@ -94,7 +94,7 @@ import {
94
94
  releaseManagedWriteLock,
95
95
  runManagedWritePreflight,
96
96
  runManagedWriteTransaction
97
- } from "./chunk-SCGC7YNM.js";
97
+ } from "./chunk-QH5MAV74.js";
98
98
  import {
99
99
  FLEET_REL_PATH,
100
100
  git,
@@ -6749,11 +6749,12 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
6749
6749
  if (guard.blocked) {
6750
6750
  return emit({ exitCode: guard.exitCode, result: guard.result }, void 0, { postCommit: false });
6751
6751
  }
6752
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-PW4OOOMV.js");
6752
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-DTNXZBLI.js");
6753
6753
  const run = await runManagedWriteTransaction2({
6754
6754
  vault,
6755
6755
  command,
6756
6756
  allowImmutableRecord: opts?.allowImmutableRecord === true,
6757
+ convergenceVault: opts?.convergenceVault,
6757
6758
  mutate: async (receipt) => await mutate(receipt)
6758
6759
  });
6759
6760
  return emit(run, vault, opts);
@@ -6982,26 +6983,28 @@ logCmd.command("materialize [vault]").description("preview or write root log.md
6982
6983
  );
6983
6984
  } else emit(await runLogMaterialize({ vault: v.vault, write: false }), v.vault);
6984
6985
  });
6985
- logCmd.command("migrate-legacy [vault]").description("backfill immutable events from historical root log.md blocks").option("--write", "write events", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6986
+ logCmd.command("migrate-legacy [vault]").description("backfill immutable events from historical root log.md blocks").option("--write", "write events", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6986
6987
  const v = await resolveVaultArg(vault, opts.wiki);
6987
6988
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6988
6989
  else if (opts.write) {
6989
6990
  return emitManagedVaultWrite(
6990
6991
  v.vault,
6991
6992
  "log migrate-legacy",
6992
- () => runLogMigrateLegacy({ vault: v.vault, write: true })
6993
+ () => runLogMigrateLegacy({ vault: v.vault, write: true }),
6994
+ { convergenceVault: opts.convergeVault }
6993
6995
  );
6994
6996
  } else emit(await runLogMigrateLegacy({ vault: v.vault, write: false }), v.vault);
6995
6997
  });
6996
6998
  var projectionsCmd = program.command("projections").description("root index/log projection materialization");
6997
- projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6999
+ projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6998
7000
  const v = await resolveVaultArg(vault, opts.wiki);
6999
7001
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
7000
7002
  else if (opts.write) {
7001
7003
  return emitManagedVaultWrite(
7002
7004
  v.vault,
7003
7005
  "projections materialize",
7004
- () => runProjectionsMaterialize({ vault: v.vault, write: true })
7006
+ () => runProjectionsMaterialize({ vault: v.vault, write: true }),
7007
+ { convergenceVault: opts.convergeVault }
7005
7008
  );
7006
7009
  } else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
7007
7010
  });
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  runManagedWritePreflight,
4
4
  runManagedWriteTransaction
5
- } from "./chunk-SCGC7YNM.js";
5
+ } from "./chunk-QH5MAV74.js";
6
6
  import "./chunk-R6BKJWVC.js";
7
7
  import "./chunk-C5OLZRRM.js";
8
8
  export {
@@ -308,9 +308,35 @@ fi
308
308
 
309
309
  refresh_git_baseline
310
310
 
311
+ # Dual-path requires distinct live mutation vs Git convergence roots.
312
+ # Same-path would materialize projections then immediately rclone-overwrite them.
313
+ if [ "$(cd "$WIKI_DIR" 2>/dev/null && pwd -P)" = "$(cd "$SNAPSHOT_WORKTREE" 2>/dev/null && pwd -P)" ]; then
314
+ log "ERROR: WIKI_DIR and WIKI_GIT_WORKTREE must be distinct paths for dual-path projection (got: $WIKI_DIR)"
315
+ exit 1
316
+ fi
317
+
311
318
  # Single-authority root projections before FUSE/S3 pull promotion.
312
- if command -v skillwiki >/dev/null 2>&1; then
313
- if ! skillwiki projections materialize "$WIKI_DIR" --write >>"$LOG_FILE" 2>&1; then
319
+ # Mutation target is the live vault ($WIKI_DIR); Git pull/base-OID use
320
+ # $SNAPSHOT_WORKTREE so FUSE/S3 hosts without a local Git HEAD still work.
321
+ SKILLWIKI_BIN="${WIKI_SNAPSHOT_SKILLWIKI_BIN:-skillwiki}"
322
+ if command -v "$SKILLWIKI_BIN" >/dev/null 2>&1; then
323
+ case "${WIKI_SNAPSHOT_MIGRATE_LEGACY:-0}" in
324
+ 0) ;;
325
+ 1)
326
+ if ! "$SKILLWIKI_BIN" log migrate-legacy "$WIKI_DIR" --write \
327
+ --converge-vault "$SNAPSHOT_WORKTREE" >>"$LOG_FILE" 2>&1; then
328
+ log "FAIL legacy log migration; snapshot promotion refused"
329
+ exit 1
330
+ fi
331
+ log "OK legacy log migration before projection (attended one-run)"
332
+ ;;
333
+ *)
334
+ log "ERROR: WIKI_SNAPSHOT_MIGRATE_LEGACY must be 0 or 1 (got: ${WIKI_SNAPSHOT_MIGRATE_LEGACY})"
335
+ exit 1
336
+ ;;
337
+ esac
338
+ if ! "$SKILLWIKI_BIN" projections materialize "$WIKI_DIR" --write \
339
+ --converge-vault "$SNAPSHOT_WORKTREE" >>"$LOG_FILE" 2>&1; then
314
340
  log "FAIL root projection materialization; snapshot promotion refused"
315
341
  exit 1
316
342
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
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.2",
3
+ "version": "0.10.3",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 19 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.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 19 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.10.2",
3
+ "version": "0.10.3",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",