skillwiki 0.10.88 → 0.10.90

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.
@@ -4512,10 +4512,16 @@ function isHygieneCommand(command) {
4512
4512
  }
4513
4513
  return false;
4514
4514
  }
4515
+ function isEventLedgerPath(rel) {
4516
+ const n = rel.replace(/\\/g, "/").replace(/^\.\//, "");
4517
+ return n === "meta/log-events" || n.startsWith("meta/log-events/");
4518
+ }
4515
4519
  function measureDirtyVolume(vault) {
4516
4520
  const empty = (extra = {}) => ({
4517
4521
  porcelain_lines: 0,
4518
4522
  expanded_files: 0,
4523
+ content_files: 0,
4524
+ ledger_files: 0,
4519
4525
  modified: 0,
4520
4526
  untracked: 0,
4521
4527
  buckets: [],
@@ -4545,7 +4551,13 @@ function measureDirtyVolume(vault) {
4545
4551
  let modified = 0;
4546
4552
  let untracked = 0;
4547
4553
  let expanded = 0;
4554
+ let contentFiles = 0;
4555
+ let ledgerFiles = 0;
4548
4556
  const bucketMap = /* @__PURE__ */ new Map();
4557
+ const classify = (rel, n) => {
4558
+ if (isEventLedgerPath(rel)) ledgerFiles += n;
4559
+ else contentFiles += n;
4560
+ };
4549
4561
  const addBucket = (rel, n) => {
4550
4562
  const top = rel.split(/[/\\]/)[0] || ".";
4551
4563
  bucketMap.set(top, (bucketMap.get(top) ?? 0) + n);
@@ -4567,20 +4579,27 @@ function measureDirtyVolume(vault) {
4567
4579
  const files = listFilesRecursive(abs);
4568
4580
  expanded += files.length;
4569
4581
  addBucket(rel, files.length);
4582
+ for (const file of files) {
4583
+ classify(relative2(vault, file).replace(/\\/g, "/"), 1);
4584
+ }
4570
4585
  } else {
4571
4586
  expanded += 1;
4572
4587
  addBucket(rel, 1);
4588
+ classify(rel, 1);
4573
4589
  }
4574
4590
  } else {
4575
4591
  modified += 1;
4576
4592
  expanded += 1;
4577
4593
  addBucket(rel, 1);
4594
+ classify(rel, 1);
4578
4595
  }
4579
4596
  }
4580
4597
  const buckets = [...bucketMap.entries()].map(([bucket, count]) => ({ bucket, count })).sort((a, b) => b.count - a.count);
4581
4598
  return {
4582
4599
  porcelain_lines: lines.length,
4583
4600
  expanded_files: expanded,
4601
+ content_files: contentFiles,
4602
+ ledger_files: ledgerFiles,
4584
4603
  modified,
4585
4604
  untracked,
4586
4605
  buckets,
@@ -4613,7 +4632,7 @@ function evaluateDirtyVolumeGate(input) {
4613
4632
  const threshold = input.threshold ?? DEFAULT_DIRTY_VOLUME_THRESHOLD;
4614
4633
  const report = measureDirtyVolume(input.vault);
4615
4634
  report.threshold = threshold;
4616
- report.over_threshold = report.is_git_repo && report.expanded_files > threshold;
4635
+ report.over_threshold = report.is_git_repo && report.content_files > threshold;
4617
4636
  if (input.skip) {
4618
4637
  return { allowed: true, reason: "skipped", report };
4619
4638
  }
@@ -4632,7 +4651,7 @@ function evaluateDirtyVolumeGate(input) {
4632
4651
  reason: "over_threshold",
4633
4652
  code: GateError.VAULT_DIRTY_BACKLOG,
4634
4653
  report,
4635
- humanHint: `Vault dirty volume ${report.expanded_files} exceeds threshold ${threshold} (porcelain ${report.porcelain_lines}; buckets: ${top || "none"}). Triage/commit keep-set before more non-hygiene writes. Hygiene commands still allowed.`
4654
+ humanHint: `Vault content dirty volume ${report.content_files} exceeds threshold ${threshold} (event-ledger ${report.ledger_files} excluded; porcelain ${report.porcelain_lines}; buckets: ${top || "none"}). Triage/commit keep-set before more non-hygiene writes. Do not git add meta/log-events. Hygiene commands still allowed.`
4636
4655
  };
4637
4656
  }
4638
4657
  var SATURATION_PATTERNS = [
@@ -7828,6 +7847,7 @@ export {
7828
7847
  DEFAULT_CAPTURE_BUDGET,
7829
7848
  DEFAULT_NO_DECISION_STREAK,
7830
7849
  GateError,
7850
+ measureDirtyVolume,
7831
7851
  evaluateDirtyVolumeGate,
7832
7852
  CAPTURE_HYGIENE_CONTRACT,
7833
7853
  runWritePreflight,
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  findPlugin,
16
16
  guardProtectedVaultWrite,
17
17
  isFailedRunStatus,
18
+ measureDirtyVolume,
18
19
  memoryAuthorityTiersRank,
19
20
  pruneVectorIndex,
20
21
  readCliPackageJson,
@@ -52,7 +53,7 @@ import {
52
53
  snapshotterHealthChecks,
53
54
  upsertIndexEntry,
54
55
  vectorIndexStatus
55
- } from "./chunk-5QFHISNH.js";
56
+ } from "./chunk-ZYVP52PM.js";
56
57
  import {
57
58
  normalizeDistTag,
58
59
  readCache,
@@ -9348,57 +9349,83 @@ function githubRecord(p) {
9348
9349
  if (p.ageHours !== void 0) rec.age_hours = p.ageHours;
9349
9350
  return rec;
9350
9351
  }
9352
+ function withDirty(rec, p) {
9353
+ if (p.dirty !== void 0) rec.dirty = p.dirty;
9354
+ if (p.untracked !== void 0) rec.untracked = p.untracked;
9355
+ if (p.ledger_untracked !== void 0) rec.ledger_untracked = p.ledger_untracked;
9356
+ if (p.content_untracked !== void 0) rec.content_untracked = p.content_untracked;
9357
+ return rec;
9358
+ }
9351
9359
  function localRecord(p, githubOid) {
9352
9360
  if (p.blockedReason) {
9353
- return {
9354
- state: "blocked",
9355
- oid: p.head,
9356
- behind: p.behind,
9357
- blocked_reason: p.blockedReason,
9358
- detail: p.detail ?? p.blockedReason
9359
- };
9361
+ return withDirty(
9362
+ {
9363
+ state: "blocked",
9364
+ oid: p.head,
9365
+ behind: p.behind,
9366
+ blocked_reason: p.blockedReason,
9367
+ detail: p.detail ?? p.blockedReason
9368
+ },
9369
+ p
9370
+ );
9360
9371
  }
9361
9372
  if (p.head && githubOid && p.head === githubOid && (p.behind === void 0 || p.behind === 0)) {
9362
- return {
9363
- state: "ok",
9364
- oid: p.head,
9365
- behind: p.behind,
9366
- detail: p.detail ?? "HEAD matches GitHub"
9367
- };
9373
+ return withDirty(
9374
+ {
9375
+ state: "ok",
9376
+ oid: p.head,
9377
+ behind: p.behind,
9378
+ detail: p.detail ?? "HEAD matches GitHub"
9379
+ },
9380
+ p
9381
+ );
9368
9382
  }
9369
9383
  if (p.behind !== void 0 && p.behind > 0) {
9370
- return {
9371
- state: "stale",
9372
- oid: p.head,
9373
- behind: p.behind,
9374
- detail: p.detail ?? `behind origin/main by ${p.behind}`
9375
- };
9384
+ return withDirty(
9385
+ {
9386
+ state: "stale",
9387
+ oid: p.head,
9388
+ behind: p.behind,
9389
+ detail: p.detail ?? `behind origin/main by ${p.behind}`
9390
+ },
9391
+ p
9392
+ );
9376
9393
  }
9377
9394
  if (p.head && githubOid && p.head !== githubOid) {
9378
- return {
9379
- state: "stale",
9380
- oid: p.head,
9381
- behind: p.behind,
9382
- detail: p.detail ?? "HEAD differs from GitHub"
9383
- };
9395
+ return withDirty(
9396
+ {
9397
+ state: "stale",
9398
+ oid: p.head,
9399
+ behind: p.behind,
9400
+ detail: p.detail ?? "HEAD differs from GitHub"
9401
+ },
9402
+ p
9403
+ );
9384
9404
  }
9385
9405
  if (p.head) {
9386
- return {
9387
- state: "ok",
9388
- oid: p.head,
9389
- behind: p.behind,
9390
- detail: p.detail ?? "HEAD present"
9391
- };
9406
+ return withDirty(
9407
+ {
9408
+ state: "ok",
9409
+ oid: p.head,
9410
+ behind: p.behind,
9411
+ detail: p.detail ?? "HEAD present"
9412
+ },
9413
+ p
9414
+ );
9392
9415
  }
9393
- return { state: "unknown", detail: p.detail ?? "leaf clone unmeasured" };
9416
+ return withDirty({ state: "unknown", detail: p.detail ?? "leaf clone unmeasured" }, p);
9394
9417
  }
9395
9418
  function formatPlane(name, rec) {
9396
9419
  const parts = [`${name}: ${rec.state}`];
9397
9420
  if (rec.oid) parts.push(`oid=${rec.oid.slice(0, 12)}`);
9398
9421
  if (rec.behind !== void 0) parts.push(`behind=${rec.behind}`);
9399
9422
  if (rec.age_hours !== void 0) parts.push(`age_hours=${rec.age_hours}`);
9423
+ if (rec.ledger_untracked !== void 0) parts.push(`ledger_untracked=${rec.ledger_untracked}`);
9424
+ if (rec.content_untracked !== void 0) parts.push(`content_untracked=${rec.content_untracked}`);
9425
+ if (rec.ledger_untracked === void 0 && rec.dirty !== void 0) parts.push(`dirty=${rec.dirty}`);
9426
+ if (rec.ledger_untracked === void 0 && rec.untracked !== void 0) parts.push(`untracked=${rec.untracked}`);
9400
9427
  if (rec.blocked_reason) parts.push(rec.blocked_reason);
9401
- if (rec.detail) parts.push(rec.detail);
9428
+ if (rec.detail && rec.detail !== rec.blocked_reason) parts.push(rec.detail);
9402
9429
  return parts.join(" ");
9403
9430
  }
9404
9431
  function composeCopyStatus(input) {
@@ -9466,11 +9493,21 @@ function defaultCopyStatusDeps(input) {
9466
9493
  const behind = behindRaw === "" ? void 0 : Number.parseInt(behindRaw, 10);
9467
9494
  const review = listReviewRequiredOps(input.vault)[0];
9468
9495
  const blockedReason = review ? `review-required:${review.opId}` : void 0;
9496
+ const dirty = measureDirtyVolume(input.vault);
9497
+ const dirtyCount = dirty.is_git_repo ? dirty.expanded_files : void 0;
9498
+ const untrackedCount = dirty.is_git_repo ? dirty.untracked : void 0;
9499
+ const ledgerUntracked = dirty.is_git_repo ? dirty.ledger_files : void 0;
9500
+ const contentUntracked = dirty.is_git_repo ? dirty.content_files : void 0;
9501
+ const dirtyHint = dirtyCount && dirtyCount > 0 ? ledgerUntracked && ledgerUntracked > 0 ? `event-ledger live-ahead of GitHub; do not git add` : `live-ahead of GitHub; do not git add` : void 0;
9469
9502
  return {
9470
9503
  head,
9471
9504
  behind: Number.isFinite(behind) ? behind : void 0,
9472
9505
  blockedReason,
9473
- detail: blockedReason
9506
+ dirty: dirtyCount,
9507
+ untracked: untrackedCount,
9508
+ ledger_untracked: dirtyCount && dirtyCount > 0 ? ledgerUntracked : void 0,
9509
+ content_untracked: dirtyCount && dirtyCount > 0 ? contentUntracked : void 0,
9510
+ detail: dirtyHint ?? blockedReason
9474
9511
  };
9475
9512
  }
9476
9513
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-5QFHISNH.js";
4
+ } from "./chunk-ZYVP52PM.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
6
  import "./chunk-ZUHA2EXE.js";
7
7
  import "./chunk-6WE7E6VB.js";
@@ -72,7 +72,17 @@ log() {
72
72
  }
73
73
 
74
74
  handle_existing_handoff() {
75
- local blocker reason op identity previous_identity notified_at now
75
+ local blocker reason op identity previous_identity notified_at now before after
76
+ # Same altitude as managed-write preflight: a review-required whose target_oid
77
+ # is already in HEAD is obsolete. Preserve dirty live-ahead WIP; only real
78
+ # unresolved handoffs (target still ahead / sequencer / unmerged) skip pull.
79
+ before="$(vault_sync_op_find_review_required "$WIKI_DIR" 2>/dev/null || true)"
80
+ if vault_sync_op_supersede_stale_review_required "$WIKI_DIR" "wiki-fetch-notify" 2>/dev/null; then
81
+ after="$(vault_sync_op_find_review_required "$WIKI_DIR" 2>/dev/null || true)"
82
+ if [ -n "$before" ] && [ -z "$after" ]; then
83
+ log "OK superseded stale review-required handoff(s); fetch pull may proceed"
84
+ fi
85
+ fi
76
86
  blocker="$(vault_sync_op_preflight_blocker "$WIKI_DIR" 2>/dev/null || true)"
77
87
  [ -n "$blocker" ] || { rm -f "$HANDOFF_STATE_FILE" "$FETCH_HARD_PAUSE_COUNTER_FILE" 2>/dev/null || true; return 1; }
78
88
 
@@ -100,6 +100,26 @@ CLOUD_REMOTE="${CLOUD_REMOTE:-cloud:cloud/wiki}"
100
100
  REPAIR_SCRIPT="${WIKI_GIT_REPAIR_SCRIPT:-$SCRIPT_DIR/wiki-git-repair-v3.sh}"
101
101
  MAX_S3_ONLY_NOTES="${WIKI_SNAPSHOT_MAX_S3_ONLY_NOTES:-200}"
102
102
  MAX_TOMBSTONE_PRUNES="${WIKI_SNAPSHOT_MAX_TOMBSTONE_PRUNES:-10}"
103
+
104
+ # Classified inventory: event ledger + local scratch are not GitHub-promotable notes.
105
+ # Cap and rclone must share this class. Do not raise MAX_S3_ONLY_NOTES instead.
106
+ # meta/log-events stay S3-authoritative; snapshot copies them separately for --events-from.
107
+ snapshot_non_promotable_path() {
108
+ local p="${1#./}"
109
+ case "$p" in
110
+ .skillwiki/*|.claude/*|.obsidian/*|.antigravitycli/*|.playwright-cli/*|.superpowers/*|.snapshots/*|.git/*|.drafts/*)
111
+ return 0 ;;
112
+ tmp/*|logs|logs/*|meta/log-events|meta/log-events/*)
113
+ return 0 ;;
114
+ raw/._.DS_Store|._.DS_Store)
115
+ return 0 ;;
116
+ ._*)
117
+ return 0 ;;
118
+ .conflict*|*.conflict-*)
119
+ return 0 ;;
120
+ esac
121
+ return 1
122
+ }
103
123
  PROJECTION_PARITY_TIMEOUT_SECONDS="${WIKI_SNAPSHOT_PROJECTION_PARITY_TIMEOUT_SECONDS:-120}"
104
124
  PROJECTION_PARITY_POLL_SECONDS="${WIKI_SNAPSHOT_PROJECTION_PARITY_POLL_SECONDS:-2}"
105
125
  PROJECTION_READ_TIMEOUT_SECONDS="${WIKI_SNAPSHOT_PROJECTION_READ_TIMEOUT_SECONDS:-15}"
@@ -185,7 +205,14 @@ snapshot_direct_s3_preflight() {
185
205
  fi
186
206
  SNAPSHOT_REMOTE_INVENTORY_READY=1
187
207
 
188
- grep -vE '^(\.skillwiki/|\.claude/|\.obsidian/|\.antigravitycli/|\.playwright-cli/|\.superpowers/|raw/\._\.DS_Store$|\._\.DS_Store$)' "$direct_paths" | LC_ALL=C sort -u > "$direct_notes" || true
208
+ : > "$direct_notes"
209
+ while IFS= read -r snapshot_inv_path || [ -n "$snapshot_inv_path" ]; do
210
+ [ -n "$snapshot_inv_path" ] || continue
211
+ if snapshot_non_promotable_path "$snapshot_inv_path"; then
212
+ continue
213
+ fi
214
+ printf '%s\n' "$snapshot_inv_path"
215
+ done < "$direct_paths" | LC_ALL=C sort -u > "$direct_notes" || true
189
216
  (
190
217
  cd "$SNAPSHOT_WORKTREE" || exit 1
191
218
  if git rev-parse --verify --quiet origin/main >/dev/null 2>&1; then
@@ -939,6 +966,11 @@ RCLONE_OPTS=(
939
966
  --exclude ".antigravitycli/**"
940
967
  --exclude ".playwright-cli/**"
941
968
  --exclude ".superpowers/**"
969
+ --exclude ".drafts/**"
970
+ --exclude "tmp/**"
971
+ --exclude "logs"
972
+ --exclude "logs/**"
973
+ --exclude "meta/log-events/**"
942
974
  --exclude "._*"
943
975
  --exclude ".conflict*"
944
976
  --exclude "*.conflict-*"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.88",
3
+ "version": "0.10.90",
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.88",
3
+ "version": "0.10.90",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.88",
3
+ "version": "0.10.90",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 21 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",
3
- "version": "0.10.88",
3
+ "version": "0.10.90",
4
4
  "description": "Project-aware Karpathy-style knowledge base: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by HTTP MCP wiki_capture.",
5
5
  "author": {
6
6
  "name": "karlorz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.10.88",
3
+ "version": "0.10.90",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",