skillwiki 0.10.20 → 0.10.22

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.
@@ -6,7 +6,7 @@ import {
6
6
  err,
7
7
  getErrorMessage,
8
8
  ok
9
- } from "./chunk-Y6KRDGI2.js";
9
+ } from "./chunk-C2DKFJFA.js";
10
10
 
11
11
  // src/utils/index-projection.ts
12
12
  import { readFile as readFile4 } from "fs/promises";
@@ -19,7 +19,7 @@ import {
19
19
  splitFrontmatter,
20
20
  vaultIoConcurrency,
21
21
  writeRootIndexProjection
22
- } from "./chunk-I5JD3BQZ.js";
22
+ } from "./chunk-54JVBNWK.js";
23
23
  import {
24
24
  CONFIG_KEYS,
25
25
  git,
@@ -34,7 +34,7 @@ import {
34
34
  satelliteGateFromFleetLoad,
35
35
  snapshotterAliasForLocalHost,
36
36
  writeDotenv
37
- } from "./chunk-GNS2ZV5P.js";
37
+ } from "./chunk-PQG26AGJ.js";
38
38
  import {
39
39
  CompoundSchema,
40
40
  ExitCode,
@@ -49,7 +49,7 @@ import {
49
49
  ok,
50
50
  parseVaultSyncKeyValue,
51
51
  systemdPropertyFor
52
- } from "./chunk-Y6KRDGI2.js";
52
+ } from "./chunk-C2DKFJFA.js";
53
53
 
54
54
  // src/commands/log-append.ts
55
55
  import { readFile as readFile2, stat } from "fs/promises";
@@ -315,13 +315,17 @@ function operationMarkers(operationId) {
315
315
  }
316
316
  return ok([
317
317
  `<!-- skillwiki-log-op:${operationId} -->`,
318
- `<!-- skillwiki-page-publish:${operationId} -->`
318
+ `<!-- skillwiki-page-publish:${operationId} -->`,
319
+ `<!-- skillwiki-project-page-publish:${operationId} -->`
319
320
  ]);
320
321
  }
321
322
  function preferredMarker(operationId, eventKind) {
322
323
  if (!eventKind || eventKind === "page-publish") {
323
324
  return `<!-- skillwiki-page-publish:${operationId} -->`;
324
325
  }
326
+ if (eventKind === "project-page-publish") {
327
+ return `<!-- skillwiki-project-page-publish:${operationId} -->`;
328
+ }
325
329
  return `<!-- skillwiki-log-op:${operationId} -->`;
326
330
  }
327
331
  async function appendWhileLocked(logPath, content, markers, writeMarker) {
@@ -3227,6 +3231,7 @@ function buildCliSurface() {
3227
3231
  program.command("ingest").requiredOption("--vault <path>").requiredOption("--type <type>").requiredOption("--title <title>").option("--tags <csv>").option("--provenance <provenance>").option("--dry-run");
3228
3232
  program.command("fleet");
3229
3233
  program.command("page");
3234
+ program.command("project-page");
3230
3235
  program.command("write-preflight").option("--command <name>").option("--dirty-threshold <n>").option("--skip-dirty").option("--prior-artifact-file <path>").option("--prior-artifact-text <text>").option("--consecutive-no-decision <n>").option("--no-decision-threshold <n>").option("--human-allow").option("--mission-kind <kind>").option("--skip-mission").option("--project <slug>").option("--capture-day <date>").option("--capture-budget <n>").option("--severity <level>").option("--skip-budget").option("--checks <list>").option("--wiki <name>");
3231
3236
  program.command("mcp");
3232
3237
  const graphCmd = program.commands.find((c) => c.name() === "graph");
@@ -3245,7 +3250,9 @@ function buildCliSurface() {
3245
3250
  const tagCmd = program.commands.find((c) => c.name() === "tag");
3246
3251
  tagCmd.command("reconcile").requiredOption("--page <path>").option("--from <path>").option("--tags <csv>").option("--reason <text>").option("--write").option("--wiki <name>");
3247
3252
  const pageCmd = program.commands.find((c) => c.name() === "page");
3248
- pageCmd.command("publish").requiredOption("--target <path>").option("--log-note <text>").option("--write").option("--wiki <name>");
3253
+ pageCmd.command("publish").requiredOption("--target <path>").option("--log-note <text>").option("--write").option("--approve <token>").option("--wiki <name>");
3254
+ const projectPageCmd = program.commands.find((c) => c.name() === "project-page");
3255
+ projectPageCmd.command("publish").requiredOption("--project <slug>").requiredOption("--target <path>").option("--log-note <text>").option("--write").option("--approve <token>").option("--wiki <name>");
3249
3256
  const logCmd = program.commands.find((c) => c.name() === "log");
3250
3257
  logCmd.command("materialize").option("--write").option("--wiki <name>");
3251
3258
  logCmd.command("migrate-legacy").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
@@ -9,16 +9,16 @@ import {
9
9
  resolveConfiguredSnapshotWorktree,
10
10
  runVaultSyncPullHelper,
11
11
  supersedeStaleReviewRequiredJournals
12
- } from "./chunk-GNS2ZV5P.js";
12
+ } from "./chunk-PQG26AGJ.js";
13
13
  import {
14
14
  ExitCode,
15
15
  err,
16
16
  ok
17
- } from "./chunk-Y6KRDGI2.js";
17
+ } from "./chunk-C2DKFJFA.js";
18
18
 
19
19
  // src/utils/managed-write-preflight.ts
20
20
  import { existsSync as existsSync2 } from "fs";
21
- import { join as join2, resolve } from "path";
21
+ import { join as join2, resolve as resolve2 } from "path";
22
22
 
23
23
  // src/utils/managed-write-lock.ts
24
24
  import { randomBytes } from "crypto";
@@ -29,7 +29,8 @@ import {
29
29
  unlinkSync,
30
30
  writeFileSync
31
31
  } from "fs";
32
- import { dirname, join } from "path";
32
+ import { hostname } from "os";
33
+ import { dirname, join, resolve } from "path";
33
34
  function managedWriteLockPath(vault) {
34
35
  const gitPath = git(vault, ["rev-parse", "--git-path", "vault-sync/managed-write.lock"]);
35
36
  if (gitPath) return gitPath.startsWith("/") ? gitPath : join(vault, gitPath);
@@ -65,19 +66,35 @@ function hasUnsafeGitState(vault) {
65
66
  const unmerged = git(vault, ["ls-files", "-u"]);
66
67
  return Boolean(unmerged && unmerged.trim().length > 0);
67
68
  }
68
- function reclaimDeadManagedWriteLockOwner(vault) {
69
+ function isGitBackedVault(vault) {
70
+ return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
71
+ }
72
+ function hasLocalOwnerProof(vault, record) {
73
+ return isGitBackedVault(vault) || typeof record.owner_hostname === "string" && record.owner_hostname === hostname();
74
+ }
75
+ function reclaimDeadManagedWriteLockOwner(vault, options = {}) {
69
76
  const path = managedWriteLockPath(vault);
77
+ const gitStateVault = resolve(options.gitStateVault ?? vault);
70
78
  if (!existsSync(path)) return ok({ reclaimed: false });
71
79
  const record = readLockRecord(path);
72
80
  if (!record) {
73
81
  return err("SYNC_LOCK_HELD", { path, message: "managed-write lock unreadable" });
74
82
  }
83
+ if (!hasLocalOwnerProof(vault, record)) {
84
+ return err("SYNC_LOCK_HELD", {
85
+ path,
86
+ owner_hostname: record.owner_hostname,
87
+ current_hostname: hostname(),
88
+ message: "managed-write lock origin is foreign or unknown"
89
+ });
90
+ }
75
91
  if (isManagedWriteLockOwnerAlive(record.pid)) {
76
92
  return err("SYNC_LOCK_HELD", { path, message: "managed-write lock owner is alive" });
77
93
  }
78
- if (hasUnsafeGitState(vault)) {
94
+ if (hasUnsafeGitState(gitStateVault)) {
79
95
  return err("SYNC_LOCK_HELD", {
80
96
  path,
97
+ git_state_vault: gitStateVault,
81
98
  message: "managed-write lock not reclaimed: unsafe git state"
82
99
  });
83
100
  }
@@ -90,6 +107,7 @@ function reclaimDeadManagedWriteLockOwner(vault) {
90
107
  recovered_at: (/* @__PURE__ */ new Date()).toISOString(),
91
108
  recovery_reason: "owner_pid_dead",
92
109
  owner_pid_alive: false,
110
+ git_state_vault: gitStateVault,
93
111
  lock: record
94
112
  };
95
113
  writeFileSync(recoveryPath, `${JSON.stringify(meta, null, 2)}
@@ -107,7 +125,13 @@ function tryCreateLock(path, command) {
107
125
  mkdirSync(dirname(path), { recursive: true });
108
126
  writeFileSync(
109
127
  path,
110
- `${JSON.stringify({ pid: process.pid, owner_token: ownerToken, acquired, command })}
128
+ `${JSON.stringify({
129
+ pid: process.pid,
130
+ owner_hostname: hostname(),
131
+ owner_token: ownerToken,
132
+ acquired,
133
+ command
134
+ })}
111
135
  `,
112
136
  { flag: "wx" }
113
137
  );
@@ -117,14 +141,14 @@ function tryCreateLock(path, command) {
117
141
  return err("WRITE_FAILED", { path, message: String(error) });
118
142
  }
119
143
  }
120
- function acquireManagedWriteLock(vault, command) {
144
+ function acquireManagedWriteLock(vault, command, options = {}) {
121
145
  const path = managedWriteLockPath(vault);
122
146
  const first = tryCreateLock(path, command);
123
147
  if (first.ok) {
124
148
  return ok({ ...first.data, vault });
125
149
  }
126
150
  if (first.error !== "SYNC_LOCK_HELD") return first;
127
- const reclaimed = reclaimDeadManagedWriteLockOwner(vault);
151
+ const reclaimed = reclaimDeadManagedWriteLockOwner(vault, options);
128
152
  if (!reclaimed.ok || !reclaimed.data.reclaimed) {
129
153
  return err("SYNC_LOCK_HELD", { path });
130
154
  }
@@ -180,8 +204,8 @@ function isGitVault(vault) {
180
204
  return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
181
205
  }
182
206
  async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
183
- const mutationVault = resolve(input.vault);
184
- let convergenceVault = input.convergenceVault && resolve(input.convergenceVault) !== mutationVault ? resolve(input.convergenceVault) : void 0;
207
+ const mutationVault = resolve2(input.vault);
208
+ let convergenceVault = input.convergenceVault && resolve2(input.convergenceVault) !== mutationVault ? resolve2(input.convergenceVault) : void 0;
185
209
  let convergenceSource = convergenceVault ? "explicit" : "single-path";
186
210
  const mutationBlocker = preflightBlocker(mutationVault);
187
211
  if (mutationBlocker) {
@@ -264,7 +288,7 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
264
288
  })
265
289
  };
266
290
  }
267
- convergenceVault = resolve(configured);
291
+ convergenceVault = resolve2(configured);
268
292
  convergenceSource = "configured";
269
293
  if (convergenceVault === mutationVault) {
270
294
  return {
@@ -373,8 +397,10 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
373
397
  };
374
398
  }
375
399
  async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
376
- const mutationVault = resolve(input.vault);
377
- const lock = acquireManagedWriteLock(mutationVault, input.command);
400
+ const mutationVault = resolve2(input.vault);
401
+ const lock = acquireManagedWriteLock(mutationVault, input.command, {
402
+ gitStateVault: input.convergenceVault ? resolve2(input.convergenceVault) : mutationVault
403
+ });
378
404
  if (!lock.ok) {
379
405
  return { exitCode: ExitCode.SYNC_LOCK_HELD, result: lock };
380
406
  }
@@ -56,7 +56,12 @@ var ExitCode = {
56
56
  SENSITIVE_CONTENT_DETECTED: 51,
57
57
  FLEET_SATELLITE_HEALTH_FAILED: 52,
58
58
  PROTECTED_SNAPSHOTTER_WRITE_BLOCKED: 53,
59
- INVALID_CONFIG_VALUE: 54
59
+ INVALID_CONFIG_VALUE: 54,
60
+ APPROVAL_REQUIRED: 55,
61
+ APPROVAL_INVALID: 56,
62
+ APPROVAL_MISMATCH: 57,
63
+ TARGET_DRIFT: 58,
64
+ RECOVERY_EVIDENCE_MISSING: 59
60
65
  };
61
66
 
62
67
  // ../shared/src/json-output.ts
@@ -5,7 +5,7 @@ import {
5
5
  err,
6
6
  isVaultSyncKey,
7
7
  ok
8
- } from "./chunk-Y6KRDGI2.js";
8
+ } from "./chunk-C2DKFJFA.js";
9
9
 
10
10
  // src/utils/dotenv.ts
11
11
  import { readFile, writeFile, mkdir } from "fs/promises";