skillwiki 0.10.12 → 0.10.14

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.
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  runManagedWritePreflight,
4
4
  runManagedWriteTransaction
5
- } from "./chunk-3ZVZ2YEE.js";
6
- import "./chunk-TYN2IHBY.js";
5
+ } from "./chunk-UJLSUJB6.js";
6
+ import "./chunk-OWPQJOFG.js";
7
7
  import "./chunk-C5OLZRRM.js";
8
8
  export {
9
9
  runManagedWritePreflight,
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-YYWGP7DN.js";
4
+ } from "./chunk-2AOOMDZ7.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
6
  import "./chunk-NMUYMNNB.js";
7
- import "./chunk-TYN2IHBY.js";
7
+ import "./chunk-OWPQJOFG.js";
8
8
  import "./chunk-C5OLZRRM.js";
9
9
 
10
10
  // src/mcp-entry.ts
@@ -38,24 +38,40 @@ vault_sync_op_set_field() {
38
38
  vault_sync_op_set_fields() {
39
39
  local repo="$1" op_id="$2"
40
40
  shift 2
41
- local file tmp
41
+ local file tmp updates source
42
42
  file="$(vault_sync_op_journal_path "$repo" "$op_id")" || return 1
43
43
  tmp="$(mktemp)" || return 1
44
- if [ -f "$file" ]; then
45
- cp "$file" "$tmp" || { rm -f "$tmp"; return 1; }
46
- else
47
- : >"$tmp"
48
- fi
44
+ updates="$(mktemp)" || { rm -f "$tmp"; return 1; }
49
45
  while [ "$#" -ge 2 ]; do
50
- awk -F= -v k="$1" -v v="$2" '
51
- BEGIN{found=0}
52
- $1==k {print k"="v; found=1; next}
53
- {print}
54
- END{if(!found) print k"="v}
55
- ' "$tmp" >"${tmp}.new" || { rm -f "$tmp" "${tmp}.new"; return 1; }
56
- mv "${tmp}.new" "$tmp"
46
+ printf '%s=%s\n' "$1" "$2" >>"$updates" || { rm -f "$tmp" "$updates"; return 1; }
57
47
  shift 2
58
48
  done
49
+ source="$file"
50
+ [ -f "$source" ] || source=/dev/null
51
+ awk -F= '
52
+ FNR==NR {
53
+ key=$1
54
+ if (!(key in replacement)) order[++count]=key
55
+ replacement[key]=substr($0, index($0,"=")+1)
56
+ next
57
+ }
58
+ {
59
+ key=$1
60
+ if (key in replacement) {
61
+ print key"="replacement[key]
62
+ seen[key]=1
63
+ next
64
+ }
65
+ print
66
+ }
67
+ END {
68
+ for (i=1; i<=count; i++) {
69
+ key=order[i]
70
+ if (!(key in seen)) print key"="replacement[key]
71
+ }
72
+ }
73
+ ' "$updates" "$source" >"$tmp" || { rm -f "$tmp" "$updates"; return 1; }
74
+ rm -f "$updates"
59
75
  mv "$tmp" "$file"
60
76
  }
61
77
 
@@ -160,6 +176,83 @@ vault_sync_op_find_review_required() {
160
176
  return 1
161
177
  }
162
178
 
179
+ # Mark one obsolete review-required handoff complete while preserving its audit
180
+ # trail. Caller must establish that no live owner is mutating the journal.
181
+ vault_sync_op_mark_superseded_stale_review_required() {
182
+ local repo="$1" op_id="$2" by="${3:-vault-sync-managed-preflight}"
183
+ local reason="$4" prior_reason="$5" now
184
+ if [ -z "$prior_reason" ] && [ -n "$reason" ] && [ "$reason" != "superseded-stale-review-required" ]; then
185
+ prior_reason="$reason"
186
+ fi
187
+ now="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)"
188
+ vault_sync_op_set_fields "$repo" "$op_id" \
189
+ "phase" "complete" \
190
+ "handoff" "1" \
191
+ "reason" "superseded-stale-review-required" \
192
+ "prior_reason" "$prior_reason" \
193
+ "superseded_at" "$now" \
194
+ "cleared_by" "$by" \
195
+ "cleared_reason" "operator-or-preflight $now"
196
+ }
197
+
198
+ # Auto-supersede review-required handoffs whose target is already an ancestor
199
+ # of HEAD. Unrelated dirty WIP is preserved; active sequencers and unmerged
200
+ # paths fail closed. Returns nonzero while any applicable handoff remains open.
201
+ vault_sync_op_supersede_stale_review_required() {
202
+ local repo="$1" by="${2:-vault-sync-managed-preflight}"
203
+ local current_git_dir git_dir unmerged jdir jf op_id phase handoff journal_git_dir target unresolved
204
+ local reason prior_reason key value
205
+
206
+ current_git_dir="$(git -C "$repo" rev-parse --absolute-git-dir 2>/dev/null)" || return 1
207
+ git_dir="$current_git_dir"
208
+ if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] \
209
+ || [ -f "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
210
+ || [ -f "$git_dir/REVERT_HEAD" ]; then
211
+ return 1
212
+ fi
213
+ unmerged="$(git -C "$repo" ls-files -u 2>/dev/null | head -1 || true)"
214
+ [ -z "$unmerged" ] || return 1
215
+
216
+ jdir="$(vault_sync_op_journal_dir "$repo")" || return 1
217
+ [ -d "$jdir" ] || return 0
218
+ unresolved=""
219
+ for jf in "$jdir"/*.env; do
220
+ [ -f "$jf" ] || continue
221
+ op_id="$(basename "$jf" .env)"
222
+ phase=""
223
+ handoff=""
224
+ journal_git_dir=""
225
+ target=""
226
+ reason=""
227
+ prior_reason=""
228
+ while IFS='=' read -r key value; do
229
+ case "$key" in
230
+ phase) phase="$value" ;;
231
+ handoff) handoff="$value" ;;
232
+ worktree_git_dir) journal_git_dir="$value" ;;
233
+ target_oid) target="$value" ;;
234
+ reason) reason="$value" ;;
235
+ prior_reason) prior_reason="$value" ;;
236
+ esac
237
+ done <"$jf"
238
+ [ "$phase" = "review-required" ] && [ "$handoff" = "1" ] || continue
239
+ if [ -n "$journal_git_dir" ] && [ "$journal_git_dir" != "$current_git_dir" ]; then
240
+ continue
241
+ fi
242
+ if [ -z "$target" ]; then
243
+ [ -n "$unresolved" ] || unresolved="$op_id"
244
+ continue
245
+ fi
246
+ if git -C "$repo" merge-base --is-ancestor "$target" HEAD 2>/dev/null; then
247
+ vault_sync_op_mark_superseded_stale_review_required \
248
+ "$repo" "$op_id" "$by" "$reason" "$prior_reason" || return 1
249
+ else
250
+ [ -n "$unresolved" ] || unresolved="$op_id"
251
+ fi
252
+ done
253
+ [ -z "$unresolved" ]
254
+ }
255
+
163
256
  vault_sync_op_preflight_blocker() {
164
257
  local repo="$1" unmerged op_id git_dir
165
258
  unmerged="$(git -C "$repo" diff --name-only --diff-filter=U 2>/dev/null || true)"
@@ -62,7 +62,7 @@ vault_sync_managed_lock_safe_to_reclaim() {
62
62
 
63
63
  # Never reclaim during an active/leftover sequencer.
64
64
  if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] \
65
- || [ -d "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
65
+ || [ -f "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
66
66
  || [ -f "$git_dir/REVERT_HEAD" ]; then
67
67
  return 1
68
68
  fi
@@ -72,13 +72,16 @@ vault_sync_managed_lock_safe_to_reclaim() {
72
72
  return 1
73
73
  fi
74
74
 
75
- # When journal helpers are loaded, refuse reclaim while review-required handoff
76
- # journals exist. Standalone lock tests may not source the journal library.
77
- if command -v vault_sync_op_find_review_required >/dev/null 2>&1; then
75
+ # When journal helpers are loaded, a dead owner may close handoffs whose
76
+ # target is already in HEAD, then continue reclaim in this same acquire.
77
+ # Live owners never reach this function; active sequencers/unmerged paths
78
+ # already failed closed above. Any unresolved handoff still blocks reclaim.
79
+ if command -v vault_sync_op_supersede_stale_review_required >/dev/null 2>&1; then
80
+ vault_sync_op_supersede_stale_review_required \
81
+ "$repo" "vault-sync-managed-lock-reclaim" 2>/dev/null || return 1
82
+ elif command -v vault_sync_op_find_review_required >/dev/null 2>&1; then
78
83
  review_op="$(vault_sync_op_find_review_required "$repo" 2>/dev/null || true)"
79
- if [ -n "$review_op" ]; then
80
- return 1
81
- fi
84
+ [ -z "$review_op" ] || return 1
82
85
  fi
83
86
 
84
87
  return 0
@@ -309,6 +309,18 @@ log() {
309
309
  echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE"
310
310
  }
311
311
 
312
+ # Emit the canonical snapshot-completion terminal record (v0.10.14).
313
+ # One stable machine-parseable line written for both pushed and no-change
314
+ # success outcomes. Failure paths must never call this.
315
+ # SNAPSHOT_COMPLETE schema=v1 outcome=<pushed|no-change> result=success ts=<ISO> head=<oid> origin=<oid|unknown>
316
+ emit_snapshot_complete() {
317
+ local outcome="$1"
318
+ local head_oid origin_oid
319
+ head_oid="$(git -C "$SNAPSHOT_WORKTREE" rev-parse HEAD 2>/dev/null || echo unknown)"
320
+ origin_oid="$(git -C "$SNAPSHOT_WORKTREE" rev-parse --verify -q origin/main 2>/dev/null || echo unknown)"
321
+ log "SNAPSHOT_COMPLETE schema=v1 outcome=${outcome} result=success ts=$(date -u +%Y-%m-%dT%H:%M:%SZ) head=${head_oid} origin=${origin_oid}"
322
+ }
323
+
312
324
  validate_tombstone_prune_cap() {
313
325
  case "$MAX_TOMBSTONE_PRUNES" in
314
326
  ''|*[!0-9]*)
@@ -731,6 +743,7 @@ fi
731
743
  # Check for changes
732
744
  if [ -z "$(git status --porcelain)" ]; then
733
745
  log "No changes to commit"
746
+ emit_snapshot_complete "no-change"
734
747
  exit 0
735
748
  fi
736
749
 
@@ -807,6 +820,7 @@ done
807
820
  if [ "$PUSH_SUCCESS" = true ]; then
808
821
  log "Push successful"
809
822
  log "Status: complete"
823
+ emit_snapshot_complete "pushed"
810
824
  exit 0
811
825
  else
812
826
  log "ERROR: Push failed after $PUSH_RETRIES attempts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.12",
3
+ "version": "0.10.14",
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.12",
3
+ "version": "0.10.14",
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.12",
3
+ "version": "0.10.14",
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.12",
3
+ "version": "0.10.14",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -86,8 +86,9 @@ After upgrading to skillwiki **≥0.10.1** (or when managed write fails):
86
86
 
87
87
  1. Run `skillwiki doctor` — checks `vault_sync_pull_helper` and `vault_sync_review_required_journals`.
88
88
  2. If pull helper is missing: install `skillwiki@0.10.1+` (helper must resolve from `dist/vault-sync/scripts/`) and/or redeploy vault-sync host install. Last-resort override: `SKILLWIKI_VAULT_SYNC_PULL_HELPER` pointing at `wiki-pull-with-auto-resolve.sh` under host vault-sync `bin/` (macOS Application Support or Linux `~/.local/share/vault-sync/bin`).
89
- 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Preflight also auto-supersedes stale handoffs when criteria match; do not force-clear during active rebase/unmerged state.
90
- 4. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
89
+ 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Managed preflight automatically supersedes a handoff when its `target_oid` is already an ancestor of `HEAD` and Git has no active sequencer or unmerged paths; unrelated dirty WIP is preserved. When the same incident also left a dead-owner managed-write lock, that preflight reclaims it with a recovery record in the same invocation. Live owners, active sequencers/unmerged paths, missing/non-ancestor targets, and remaining review-required journals still fail closed.
90
+ 4. **Protected snapshotter stale-journal cleanup (v0.10.14+):** On a known protected snapshotter (sg01) where `sync journal clear-stale` is blocked by `PROTECTED_SNAPSHOTTER_WRITE_BLOCKED`, use the attended one-shot maintenance authority instead: `skillwiki snapshot-maintenance journal clear-stale <snapshot-worktree> --dry-run --reason "<operator reason>"` to produce a state-bound approval ID, then `skillwiki snapshot-maintenance journal clear-stale <snapshot-worktree> --approve <id> --reason "<same reason>"` on an attended TTY to execute. This requires the exact configured snapshot worktree, the production snapshot flock, and recomputes the plan under the flock; it is the only allowlisted mutation across the protected boundary. No generic force flag or env bypass exists.
91
+ 5. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
91
92
 
92
93
  Also mirror these pointers in vault-presync / vault-sync-status skills when operating pull/push.
93
94
 
@@ -86,8 +86,9 @@ After upgrading to skillwiki **≥0.10.1** (or when managed write fails):
86
86
 
87
87
  1. Run `skillwiki doctor` — checks `vault_sync_pull_helper` and `vault_sync_review_required_journals`.
88
88
  2. If pull helper is missing: install `skillwiki@0.10.1+` (helper must resolve from `dist/vault-sync/scripts/`) and/or redeploy vault-sync host install. Last-resort override: `SKILLWIKI_VAULT_SYNC_PULL_HELPER` pointing at `wiki-pull-with-auto-resolve.sh` under host vault-sync `bin/` (macOS Application Support or Linux `~/.local/share/vault-sync/bin`).
89
- 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Preflight also auto-supersedes stale handoffs when criteria match; do not force-clear during active rebase/unmerged state.
90
- 4. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
89
+ 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Managed preflight automatically supersedes a handoff when its `target_oid` is already an ancestor of `HEAD` and Git has no active sequencer or unmerged paths; unrelated dirty WIP is preserved. When the same incident also left a dead-owner managed-write lock, that preflight reclaims it with a recovery record in the same invocation. Live owners, active sequencers/unmerged paths, missing/non-ancestor targets, and remaining review-required journals still fail closed.
90
+ 4. **Protected snapshotter stale-journal cleanup (v0.10.14+):** On a known protected snapshotter (sg01) where `sync journal clear-stale` is blocked by `PROTECTED_SNAPSHOTTER_WRITE_BLOCKED`, use the attended one-shot maintenance authority instead: `skillwiki snapshot-maintenance journal clear-stale <snapshot-worktree> --dry-run --reason "<operator reason>"` to produce a state-bound approval ID, then `skillwiki snapshot-maintenance journal clear-stale <snapshot-worktree> --approve <id> --reason "<same reason>"` on an attended TTY to execute. This requires the exact configured snapshot worktree, the production snapshot flock, and recomputes the plan under the flock; it is the only allowlisted mutation across the protected boundary. No generic force flag or env bypass exists.
91
+ 5. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
91
92
 
92
93
  Also mirror these pointers in vault-presync / vault-sync-status skills when operating pull/push.
93
94