skillwiki 0.10.19 → 0.10.21

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.
@@ -18,7 +18,7 @@ import {
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
  }
@@ -164,6 +164,11 @@ function redactMarker(kind, value) {
164
164
  function isSyntheticPlaceholder(value) {
165
165
  return REDACTED_RE.test(value) || SYNTHETIC_RE.test(value.trim());
166
166
  }
167
+ function isNonSecretTokenCapture(value) {
168
+ if (value.startsWith("//")) return true;
169
+ if (/^[a-z]{2,}(?:-[a-z]{2,})+$/.test(value)) return true;
170
+ return false;
171
+ }
167
172
  function collectMatches(text) {
168
173
  const matches = [];
169
174
  for (const matcher of MATCHERS) {
@@ -174,6 +179,7 @@ function collectMatches(text) {
174
179
  if (REDACTED_RE.test(whole)) continue;
175
180
  const value = matcher.valueGroup ? m[matcher.valueGroup] : whole;
176
181
  if (isSyntheticPlaceholder(value)) continue;
182
+ if (matcher.kind === "token" && isNonSecretTokenCapture(value)) continue;
177
183
  const valueOffset = whole.lastIndexOf(value);
178
184
  const valueStart = start + Math.max(0, valueOffset);
179
185
  matches.push({
@@ -19,7 +19,7 @@ import {
19
19
  splitFrontmatter,
20
20
  vaultIoConcurrency,
21
21
  writeRootIndexProjection
22
- } from "./chunk-IGCW3DR2.js";
22
+ } from "./chunk-I5JD3BQZ.js";
23
23
  import {
24
24
  CONFIG_KEYS,
25
25
  git,
@@ -3196,6 +3196,9 @@ function buildCliSurface() {
3196
3196
  program.command("log-append").requiredOption("--content <text>").option("--operation-id <id>").option("--write-event").option("--wiki <name>");
3197
3197
  program.command("work-complete").requiredOption("--work-item <path>").option("--operation-id <id>").option("--no-commit").option("--wiki <name>");
3198
3198
  program.command("work-validate").requiredOption("--work-item <path>").option("--require-complete").option("--wiki <name>");
3199
+ program.command("log");
3200
+ program.command("index");
3201
+ program.command("projections");
3199
3202
  program.command("lint").option("--days <n>").option("--lines <n>").option("--log-threshold <n>").option("--fix").option("--only <bucket>").option("--summary").option("--examples <n>").option("--wiki <name>");
3200
3203
  program.command("config");
3201
3204
  program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
@@ -3225,6 +3228,7 @@ function buildCliSurface() {
3225
3228
  program.command("fleet");
3226
3229
  program.command("page");
3227
3230
  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
+ program.command("mcp");
3228
3232
  const graphCmd = program.commands.find((c) => c.name() === "graph");
3229
3233
  graphCmd.command("build").option("--out <path>").option("--wiki <name>");
3230
3234
  const canvasCmd = program.commands.find((c) => c.name() === "canvas");
@@ -3242,6 +3246,14 @@ function buildCliSurface() {
3242
3246
  tagCmd.command("reconcile").requiredOption("--page <path>").option("--from <path>").option("--tags <csv>").option("--reason <text>").option("--write").option("--wiki <name>");
3243
3247
  const pageCmd = program.commands.find((c) => c.name() === "page");
3244
3248
  pageCmd.command("publish").requiredOption("--target <path>").option("--log-note <text>").option("--write").option("--wiki <name>");
3249
+ const logCmd = program.commands.find((c) => c.name() === "log");
3250
+ logCmd.command("materialize").option("--write").option("--wiki <name>");
3251
+ logCmd.command("migrate-legacy").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
3252
+ const indexCmd = program.commands.find((c) => c.name() === "index");
3253
+ indexCmd.command("rebuild").option("--write").option("--wiki <name>");
3254
+ const projectionsCmd = program.commands.find((c) => c.name() === "projections");
3255
+ projectionsCmd.command("materialize").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
3256
+ projectionsCmd.command("repair-legacy").requiredOption("--event-operation-id <id>").option("--write").option("--converge-vault <dir>").option("--wiki <name>");
3245
3257
  const syncCmd = program.commands.find((c) => c.name() === "sync");
3246
3258
  syncCmd.command("status").option("--wiki <name>").option("--include-stashes").option("--include-remote-health").option("--check-snapshotter");
3247
3259
  syncCmd.command("push").option("--wiki <name>");
@@ -3346,7 +3358,8 @@ function validateCliRefs(text, page, surface) {
3346
3358
  // src/utils/source-identity.ts
3347
3359
  var PROJECT_PATTERNS = {
3348
3360
  hermes: [/\bhermes\b/i, /nousresearch\s*hermes/i, /nousresearch\/hermes-agent/i, /hermes agent/i],
3349
- skillwiki: [/\bskillwiki\b/i, /\bllm[-_ ]?wiki\b/i, /karpathy'?s llm wiki/i],
3361
+ // normalize() splits CamelCase ("SkillWiki" → "skill wiki"), so match both forms.
3362
+ skillwiki: [/\bskillwiki\b/i, /\bskill\s+wiki\b/i, /\bllm[-_ ]?wiki\b/i, /karpathy'?s llm wiki/i],
3350
3363
  superpowers: [/\bsuperpowers\b/i, /obra\/superpowers/i, /complete software development methodology/i],
3351
3364
  playwright: [/\bplaywright\b/i, /microsoft\s*playwright/i, /microsoft\/playwright/i],
3352
3365
  convex: [/\bconvex\b/i],
@@ -3359,6 +3372,9 @@ var PROJECT_PATTERNS = {
3359
3372
  var COMPATIBLE = /* @__PURE__ */ new Set([
3360
3373
  "hermes|skillwiki",
3361
3374
  "skillwiki|hermes",
3375
+ // SkillWiki work items commonly reference Superpowers skills/methodology.
3376
+ "skillwiki|superpowers",
3377
+ "superpowers|skillwiki",
3362
3378
  "proxmox|seaweedfs",
3363
3379
  "seaweedfs|proxmox",
3364
3380
  "coolify|seaweedfs",
package/dist/cli.js CHANGED
@@ -83,7 +83,7 @@ import {
83
83
  upsertIndexEntry,
84
84
  validateLogEvent,
85
85
  writeLogEvent
86
- } from "./chunk-IAPD6YTC.js";
86
+ } from "./chunk-UNPZDCWN.js";
87
87
  import {
88
88
  normalizeDistTag,
89
89
  readCache,
@@ -105,11 +105,11 @@ import {
105
105
  scanVault,
106
106
  splitFrontmatter,
107
107
  writeRootIndexProjection
108
- } from "./chunk-IGCW3DR2.js";
108
+ } from "./chunk-I5JD3BQZ.js";
109
109
  import {
110
110
  runManagedWritePreflight,
111
111
  runManagedWriteTransaction
112
- } from "./chunk-QJPCCW4N.js";
112
+ } from "./chunk-65Q5UGND.js";
113
113
  import {
114
114
  FLEET_REL_PATH,
115
115
  canSupersedeJournal,
@@ -3204,7 +3204,7 @@ ${fmRewritten}
3204
3204
  await rename2(join18(input.vault, relPath), join18(input.vault, archivePath));
3205
3205
  let indexUpdated = false;
3206
3206
  if (!isRaw) {
3207
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-DENEYZTK.js");
3207
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-HAXDEM2F.js");
3208
3208
  const before = await readFile7(join18(input.vault, "index.md"), "utf8").catch(() => "");
3209
3209
  const fullTarget = relPath.replace(/\.md$/, "");
3210
3210
  const bare = fullTarget.split("/").pop() ?? fullTarget;
@@ -3315,7 +3315,7 @@ async function runRemove(input) {
3315
3315
  if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
3316
3316
  const { readFile: readFile17 } = await import("fs/promises");
3317
3317
  const { join: pathJoin } = await import("path");
3318
- const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-DENEYZTK.js");
3318
+ const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-HAXDEM2F.js");
3319
3319
  const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
3320
3320
  const fullTarget = relPath.replace(/\.md$/, "");
3321
3321
  const bare = fullTarget.split("/").pop() ?? fullTarget;
@@ -7862,7 +7862,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
7862
7862
  if (dirty) {
7863
7863
  return emit(dirty, void 0, { postCommit: false });
7864
7864
  }
7865
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-LO4LZ6OQ.js");
7865
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-CTXX2MHQ.js");
7866
7866
  const run = await runManagedWriteTransaction2({
7867
7867
  vault,
7868
7868
  command,
@@ -4,7 +4,7 @@ import {
4
4
  UNMANAGED_START,
5
5
  renderRootIndex,
6
6
  writeRootIndexProjection
7
- } from "./chunk-IGCW3DR2.js";
7
+ } from "./chunk-I5JD3BQZ.js";
8
8
  import "./chunk-Y6KRDGI2.js";
9
9
  export {
10
10
  UNMANAGED_END,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  runManagedWritePreflight,
4
4
  runManagedWriteTransaction
5
- } from "./chunk-QJPCCW4N.js";
5
+ } from "./chunk-65Q5UGND.js";
6
6
  import "./chunk-GNS2ZV5P.js";
7
7
  import "./chunk-Y6KRDGI2.js";
8
8
  export {
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-IAPD6YTC.js";
4
+ } from "./chunk-UNPZDCWN.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
- import "./chunk-IGCW3DR2.js";
6
+ import "./chunk-I5JD3BQZ.js";
7
7
  import "./chunk-GNS2ZV5P.js";
8
8
  import "./chunk-Y6KRDGI2.js";
9
9
 
@@ -159,7 +159,7 @@ vault_sync_managed_lock_reclaim_dead_owner() {
159
159
  vault_sync_managed_lock_acquire() {
160
160
  local repo="${1:-.}"
161
161
  local command="${2:-wiki-pull}"
162
- local path token now inherited attempt
162
+ local path token now owner_hostname inherited attempt
163
163
 
164
164
  path="$(vault_sync_managed_lock_path "$repo")" || return 1
165
165
  VAULT_SYNC_MANAGED_LOCK_PATH="$path"
@@ -185,8 +185,9 @@ vault_sync_managed_lock_acquire() {
185
185
  token="$(od -An -N16 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n')"
186
186
  [ -n "$token" ] || token="$$-$(date +%s)"
187
187
  now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
188
- if ( set -o noclobber; printf '{"pid":%s,"owner_token":"%s","acquired":"%s","command":"%s"}\n' \
189
- "$$" "$token" "$now" "$command" >"$path" ) 2>/dev/null; then
188
+ owner_hostname="$(hostname 2>/dev/null || printf unknown)"
189
+ if ( set -o noclobber; printf '{"pid":%s,"owner_hostname":"%s","owner_token":"%s","acquired":"%s","command":"%s"}\n' \
190
+ "$$" "$owner_hostname" "$token" "$now" "$command" >"$path" ) 2>/dev/null; then
190
191
  VAULT_SYNC_MANAGED_LOCK_TOKEN_OWNED="$token"
191
192
  VAULT_SYNC_MANAGED_LOCK_ACQUIRED="$now"
192
193
  VAULT_SYNC_MANAGED_LOCK_OWNS_RELEASE=1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.19",
3
+ "version": "0.10.21",
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.19",
3
+ "version": "0.10.21",
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.19",
3
+ "version": "0.10.21",
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.19",
3
+ "version": "0.10.21",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",