skillwiki 0.9.55 → 0.9.57

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.
package/dist/cli.js CHANGED
@@ -66,6 +66,7 @@ import {
66
66
  runQuery,
67
67
  runSkillwikiMcpStdio,
68
68
  runStale,
69
+ runSyncLintDelta,
69
70
  runTagAudit,
70
71
  runTopicMapCheck,
71
72
  runValidate,
@@ -75,13 +76,15 @@ import {
75
76
  scanVault,
76
77
  snapshotterAliasForLocalHost,
77
78
  splitFrontmatter,
78
- triggerAutoUpdate,
79
- writeCache,
80
79
  writeDotenv
81
- } from "./chunk-4BT3HY4K.js";
80
+ } from "./chunk-NIPZVIHT.js";
82
81
  import {
83
- normalizeDistTag
84
- } from "./chunk-E6UWZ3S3.js";
82
+ normalizeDistTag,
83
+ readCache,
84
+ resolveAutoApplyAt,
85
+ triggerAutoUpdate,
86
+ writeCache
87
+ } from "./chunk-7I2TPIV5.js";
85
88
 
86
89
  // src/cli.ts
87
90
  import { join as join26 } from "path";
@@ -1956,11 +1959,14 @@ async function runUpdate(input) {
1956
1959
  result: err("PREFLIGHT_FAILED", { message: `Failed to query npm registry: ${String(e)}` })
1957
1960
  };
1958
1961
  }
1962
+ const { firstSeenAt, autoApplyAt } = resolveAutoApplyAt(readCache(input.home).cache, latest);
1959
1963
  const cache = {
1960
1964
  lastCheck: Date.now(),
1961
1965
  latestVersion: latest,
1962
1966
  currentVersion,
1963
- distTag: tag
1967
+ distTag: tag,
1968
+ firstSeenAt,
1969
+ autoApplyAt
1964
1970
  };
1965
1971
  if (latest === currentVersion) {
1966
1972
  writeCache(input.home, cache);
@@ -3720,15 +3726,51 @@ async function runSyncPush(input) {
3720
3726
  })
3721
3727
  };
3722
3728
  }
3723
- const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
3724
- if (lintResult.result.ok && lintResult.result.data.summary.errors > 0) {
3725
- return {
3726
- exitCode: ExitCode.LINT_HAS_ERRORS,
3727
- result: err("LINT_ERRORS_BLOCK_PUSH", {
3728
- errors: lintResult.result.data.summary.errors,
3729
- buckets: lintResult.result.data.by_severity.error
3730
- })
3731
- };
3729
+ let delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
3730
+ const preferredBase = git(vault, ["rev-parse", "--verify", "origin/main"]) ? "origin/main" : git(vault, ["rev-parse", "--verify", "origin/HEAD"]) ? "origin/HEAD" : "";
3731
+ if (preferredBase) {
3732
+ const deltaResult = await runSyncLintDelta({ vault, baseRef: preferredBase });
3733
+ if (!deltaResult.result.ok) {
3734
+ return {
3735
+ exitCode: ExitCode.LINT_HAS_ERRORS,
3736
+ result: err("LINT_DELTA_UNAVAILABLE", {
3737
+ message: "lint-delta evidence missing or failed \u2014 fail closed",
3738
+ detail: deltaResult.result
3739
+ })
3740
+ };
3741
+ }
3742
+ delta = deltaResult.result.data;
3743
+ if (delta.new_errors > 0) {
3744
+ return {
3745
+ exitCode: ExitCode.LINT_HAS_ERRORS,
3746
+ result: err("LINT_NEW_ERRORS_BLOCK_PUSH", {
3747
+ full_errors: delta.full_errors,
3748
+ base_errors: delta.base_errors,
3749
+ new_errors: delta.new_errors,
3750
+ resolved_errors: delta.resolved_errors,
3751
+ new_fingerprints: deltaResult.result.data.new_fingerprints
3752
+ })
3753
+ };
3754
+ }
3755
+ } else {
3756
+ const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
3757
+ if (lintResult.result.ok) {
3758
+ const fullErrors = lintResult.result.data.summary.errors;
3759
+ delta = { full_errors: fullErrors, base_errors: 0, new_errors: fullErrors, resolved_errors: 0 };
3760
+ if (fullErrors > 0) {
3761
+ const buckets = "by_severity" in lintResult.result.data ? lintResult.result.data.by_severity.error : [];
3762
+ return {
3763
+ exitCode: ExitCode.LINT_HAS_ERRORS,
3764
+ result: err("LINT_ERRORS_BLOCK_PUSH", {
3765
+ errors: fullErrors,
3766
+ buckets,
3767
+ message: "no origin base ref for delta; absolute lint errors block push"
3768
+ })
3769
+ };
3770
+ }
3771
+ } else {
3772
+ delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
3773
+ }
3732
3774
  }
3733
3775
  try {
3734
3776
  stageVaultContentChanges(vault);
@@ -3771,6 +3813,7 @@ async function runSyncPush(input) {
3771
3813
  })
3772
3814
  };
3773
3815
  }
3816
+ const inheritedNote = delta.full_errors > 0 ? `; lint full=${delta.full_errors} base=${delta.base_errors} new=${delta.new_errors} resolved=${delta.resolved_errors} (inherited debt only)` : `; lint full=0 new=0`;
3774
3817
  return {
3775
3818
  exitCode: ExitCode.OK,
3776
3819
  result: ok({
@@ -3778,7 +3821,11 @@ async function runSyncPush(input) {
3778
3821
  commit_message: commitMessage,
3779
3822
  pushed,
3780
3823
  path_fixes: pathFixes,
3781
- humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}`
3824
+ lint_full_errors: delta.full_errors,
3825
+ lint_base_errors: delta.base_errors,
3826
+ lint_new_errors: delta.new_errors,
3827
+ lint_resolved_errors: delta.resolved_errors,
3828
+ humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}${inheritedNote}`
3782
3829
  })
3783
3830
  };
3784
3831
  }
@@ -5493,6 +5540,11 @@ syncCmd.command("peers [vault]").description("list active locks and recent wiki-
5493
5540
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5494
5541
  else emit(runSyncPeers({ vault: v.vault, sessionId: getCliSessionId() }));
5495
5542
  });
5543
+ syncCmd.command("lint-delta [vault]").description("compare lint errors against a base ref; block only on new errors").option("--base-ref <ref>", "base git ref to compare against", "origin/main").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5544
+ const v = await resolveVaultArg(vault, opts.wiki);
5545
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5546
+ else emit(await runSyncLintDelta({ vault: v.vault, baseRef: opts.baseRef }));
5547
+ });
5496
5548
  var backupCmd = program.command("backup").description("manage S3-compatible remote backup");
5497
5549
  backupCmd.command("sync [vault]").description("sync vault to S3-compatible remote backup").option("--dry-run", "list actions without executing").option("--bucket <name>", "S3 bucket name").option("--endpoint <url>", "S3 endpoint URL").option("--region <region>", "S3 region", "us-east-1").option("--prune", "delete orphaned S3 objects not in vault", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5498
5550
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-4BT3HY4K.js";
5
- import "./chunk-E6UWZ3S3.js";
4
+ } from "./chunk-NIPZVIHT.js";
5
+ import "./chunk-7I2TPIV5.js";
6
6
 
7
7
  // src/mcp-entry.ts
8
8
  runSkillwikiMcpStdio().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.55",
3
+ "version": "0.9.57",
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.9.55",
3
+ "version": "0.9.57",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 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.9.55",
3
+ "version": "0.9.57",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 18 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
package/skills/README.md CHANGED
@@ -1,11 +1,22 @@
1
1
  # @skillwiki/skills
2
2
 
3
- Prompt-only Markdown skills for Claude Code. Installed via `skillwiki install`.
3
+ Prompt-only Markdown skills for Claude Code. Installed via `skillwiki install`
4
+ or the Claude/Codex/Antigravity plugin packaging paths.
5
+
6
+ Current package inventory: **18 skills**.
4
7
 
5
8
  | Namespace | Skills |
6
9
  |---|---|
7
- | `wiki-*` | `wiki-init`, `wiki-ingest`, `wiki-query`, `wiki-lint`, `wiki-crystallize`, `wiki-audit` |
10
+ | `wiki-*` | `wiki-init`, `wiki-ingest`, `wiki-query`, `wiki-lint`, `wiki-crystallize`, `wiki-audit`, `wiki-archive`, `wiki-reingest`, `wiki-adapter-prd`, `wiki-add-task`, `wiki-sync`, `wiki-canvas`, `wiki-gate-plan-mode` |
8
11
  | `proj-*` | `proj-init`, `proj-work`, `proj-distill`, `proj-decide` |
12
+ | onboarding | `using-skillwiki` |
13
+
14
+ Verify the live inventory from source:
15
+
16
+ ```bash
17
+ find packages/skills -mindepth 2 -maxdepth 2 -name SKILL.md -print | sort
18
+ bash scripts/verify-manifests.sh
19
+ ```
9
20
 
10
21
  Each top-level skill subdirectory holds one canonical `SKILL.md`. The nested
11
22
  `skills/<skill>/SKILL.md` tree mirrors those files for Codex plugin discovery;
@@ -20,3 +31,6 @@ Codex-specific hook files. That root exposes `hooks/hooks-codex.json` and
20
31
  Run `npm run materialize:plugins` from the repository root after changing
21
32
  canonical skill, agent, or hook assets. Run
22
33
  `npm run materialize:plugins:check` for read-only drift detection.
34
+
35
+ The sibling `vault-sync` plugin ships six additional operational skills under
36
+ `packages/vault-sync/skills/` and is packaged separately from this skill set.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.9.55",
3
+ "version": "0.9.57",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -44,8 +44,8 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
44
44
 
45
45
  ### Push workflow
46
46
  4. If vault is dirty, ask the user to review uncommitted changes before proceeding.
47
- 5. Run `skillwiki lint <vault>`. If errors exist, stop and report — do not push lint errors to remote.
48
- 6. If lint passes (errors = 0), stage and commit:
47
+ 5. Run `skillwiki sync lint-delta <vault> --base-ref origin/main`. Block only when `new_errors > 0`. Report full/base/new/resolved. Malformed delta evidence fails closed — do not push.
48
+ 6. If lint-delta allows (new_errors = 0), stage and commit:
49
49
  - `git -C <vault> add -A`
50
50
  - `git -C <vault> commit -m "sync: vault update $(date -u +%Y-%m-%dT%H:%MZ)"`
51
51
  7. Run `git -C <vault> push origin HEAD`. Report result.
@@ -62,7 +62,7 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
62
62
  MSG="wiki-sync:${SESSION_ID}:${CWD_HASH}:${ISO_TS}:pre-pull"
63
63
  git -C "$VAULT" stash push -m "$MSG"
64
64
  ```
65
- 11. Run `git -C <vault> pull --rebase origin HEAD`. Report result.
65
+ 11. Prefer the canonical pull helper (`wiki-pull-with-auto-resolve.sh` / vault-presync `--execute`) so stale-clean rebase state uses recovery-ref + `rebase --quit`, active rebases fail closed, and only fully materialized commits are dropped. If invoking git directly: `git -C <vault> pull --rebase origin HEAD`. Report result.
66
66
  12. If a stash was created, pop it: `git -C <vault> stash pop`.
67
67
  13. If conflicts occur during stash pop, identify them and present to the user for resolution (see Conflict Resolution below).
68
68
  14. Run `skillwiki lint <vault>` after pull to verify vault integrity.
@@ -240,3 +240,20 @@ bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
240
240
  - Modifying files in `raw/` to resolve conflicts (N9 — archive and re-ingest instead).
241
241
  - Stashing without the `wiki-sync:...` name format (breaks peer detection).
242
242
  - Force-deleting a peer's lockfile (use `--force` only if peer is confirmed dead).
243
+
244
+ ## Convergence safeguards (2026-07-11)
245
+
246
+ ### Rebase-state classification
247
+ - `stale-clean`: recovery ref at tip + `git rebase --quit` (preserves advanced tip; never abort-reset to orig-head).
248
+ - `active` (REBASE_HEAD / UU paths): leave untouched; fail closed.
249
+ - Recovery refs live under `refs/vault-sync/recovery/<UTC timestamp>`.
250
+
251
+ ### Materialized-commit proof
252
+ Drop a local commit from rebase only when every path is proven present on the target ref (exact blobs; byte-identical added `## ` log sections). Partial/raw/rename mismatches retain or stop.
253
+
254
+ ### Lint-delta fail-closed
255
+ - Fingerprints: `<bucket>\0<page>\0<normalized-detail>`
256
+ - CLI: `skillwiki sync lint-delta <vault> --base-ref origin/main`
257
+ - Block publication only when `new_errors > 0`; inherited full debt remains visible.
258
+ - Missing/malformed delta evidence blocks (never silent lint skip).
259
+
@@ -44,8 +44,8 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
44
44
 
45
45
  ### Push workflow
46
46
  4. If vault is dirty, ask the user to review uncommitted changes before proceeding.
47
- 5. Run `skillwiki lint <vault>`. If errors exist, stop and report — do not push lint errors to remote.
48
- 6. If lint passes (errors = 0), stage and commit:
47
+ 5. Run `skillwiki sync lint-delta <vault> --base-ref origin/main`. Block only when `new_errors > 0`. Report full/base/new/resolved. Malformed delta evidence fails closed — do not push.
48
+ 6. If lint-delta allows (new_errors = 0), stage and commit:
49
49
  - `git -C <vault> add -A`
50
50
  - `git -C <vault> commit -m "sync: vault update $(date -u +%Y-%m-%dT%H:%MZ)"`
51
51
  7. Run `git -C <vault> push origin HEAD`. Report result.
@@ -62,7 +62,7 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
62
62
  MSG="wiki-sync:${SESSION_ID}:${CWD_HASH}:${ISO_TS}:pre-pull"
63
63
  git -C "$VAULT" stash push -m "$MSG"
64
64
  ```
65
- 11. Run `git -C <vault> pull --rebase origin HEAD`. Report result.
65
+ 11. Prefer the canonical pull helper (`wiki-pull-with-auto-resolve.sh` / vault-presync `--execute`) so stale-clean rebase state uses recovery-ref + `rebase --quit`, active rebases fail closed, and only fully materialized commits are dropped. If invoking git directly: `git -C <vault> pull --rebase origin HEAD`. Report result.
66
66
  12. If a stash was created, pop it: `git -C <vault> stash pop`.
67
67
  13. If conflicts occur during stash pop, identify them and present to the user for resolution (see Conflict Resolution below).
68
68
  14. Run `skillwiki lint <vault>` after pull to verify vault integrity.
@@ -240,3 +240,20 @@ bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
240
240
  - Modifying files in `raw/` to resolve conflicts (N9 — archive and re-ingest instead).
241
241
  - Stashing without the `wiki-sync:...` name format (breaks peer detection).
242
242
  - Force-deleting a peer's lockfile (use `--force` only if peer is confirmed dead).
243
+
244
+ ## Convergence safeguards (2026-07-11)
245
+
246
+ ### Rebase-state classification
247
+ - `stale-clean`: recovery ref at tip + `git rebase --quit` (preserves advanced tip; never abort-reset to orig-head).
248
+ - `active` (REBASE_HEAD / UU paths): leave untouched; fail closed.
249
+ - Recovery refs live under `refs/vault-sync/recovery/<UTC timestamp>`.
250
+
251
+ ### Materialized-commit proof
252
+ Drop a local commit from rebase only when every path is proven present on the target ref (exact blobs; byte-identical added `## ` log sections). Partial/raw/rename mismatches retain or stop.
253
+
254
+ ### Lint-delta fail-closed
255
+ - Fingerprints: `<bucket>\0<page>\0<normalized-detail>`
256
+ - CLI: `skillwiki sync lint-delta <vault> --base-ref origin/main`
257
+ - Block publication only when `new_errors > 0`; inherited full debt remains visible.
258
+ - Missing/malformed delta evidence blocks (never silent lint skip).
259
+
@@ -1,62 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/utils/semver.ts
4
- function semverGt(a, b) {
5
- const pa = parseSemver(a);
6
- const pb = parseSemver(b);
7
- if (!pa || !pb) return a > b;
8
- if (pa.major !== pb.major) return pa.major > pb.major;
9
- if (pa.minor !== pb.minor) return pa.minor > pb.minor;
10
- if (pa.patch !== pb.patch) return pa.patch > pb.patch;
11
- if (!pa.pre && pb.pre) return true;
12
- if (pa.pre && !pb.pre) return false;
13
- if (!pa.pre && !pb.pre) return false;
14
- const aParts = pa.pre.split(".");
15
- const bParts = pb.pre.split(".");
16
- const len = Math.max(aParts.length, bParts.length);
17
- for (let i = 0; i < len; i++) {
18
- const ai = aParts[i];
19
- const bi = bParts[i];
20
- if (ai === void 0) return false;
21
- if (bi === void 0) return true;
22
- const aNum = parseInt(ai, 10);
23
- const bNum = parseInt(bi, 10);
24
- if (!isNaN(aNum) && !isNaN(bNum)) {
25
- if (aNum !== bNum) return aNum > bNum;
26
- } else {
27
- if (ai !== bi) return ai > bi;
28
- }
29
- }
30
- return false;
31
- }
32
- function parseSemver(version) {
33
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
34
- if (!match) return null;
35
- return {
36
- major: parseInt(match[1], 10),
37
- minor: parseInt(match[2], 10),
38
- patch: parseInt(match[3], 10),
39
- pre: match[4] ?? null
40
- };
41
- }
42
-
43
- // src/utils/update-consts.ts
44
- var DIST_TAG = "latest";
45
- var CACHE_FILENAME = ".update-cache.json";
46
- var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
47
- var ENV_DISABLE_KEY = "NO_UPDATE_NOTIFIER";
48
- var CLI_DISABLE_FLAG = "--no-update-notifier";
49
- function normalizeDistTag(tag) {
50
- const value = (tag ?? DIST_TAG).trim();
51
- return /^[A-Za-z0-9._-]+$/.test(value) ? value : DIST_TAG;
52
- }
53
-
54
- export {
55
- semverGt,
56
- DIST_TAG,
57
- CACHE_FILENAME,
58
- CHECK_INTERVAL_MS,
59
- ENV_DISABLE_KEY,
60
- CLI_DISABLE_FLAG,
61
- normalizeDistTag
62
- };