@lmzhen/dsh-evolution-skill-catalog 0.3.62 → 0.3.64

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/README.md CHANGED
@@ -23,5 +23,8 @@ Independent of request-prefix construction. This package does not alter the asse
23
23
 
24
24
 
25
25
  - Read-only native `ctx.skills` catalog. Skill mutations still happen through `tool-skill-manage`.
26
- - **Out-of-band edits need a refresh (0.3.18, E-71).** The catalog invalidates on the in-band `evolution/skill-mutated` event (all writes through SkillLibrary). Edits made outside the family — manual file edit, a git pull, another process — bypass that event and cannot be auto-detected (decision C: no filesystem watcher). A root-mtime probe re-stamps the summaries cache when the provider is re-queried after a structural change (directory add/remove/rename), but any out-of-band change is only guaranteed to be visible after `/evolution skills refresh` runs (or the process restarts).
26
+ - **Out-of-band edits need a refresh (0.3.18, E-71).** The catalog invalidates on the in-band `evolution/skill-mutated` event (all writes through SkillLibrary). Edits made outside the family — manual file edit, a git pull, another process — bypass that event and cannot be auto-detected (decision C: no filesystem watcher). A root-mtime probe re-stamps the summaries cache when the provider is re-queried after a structural change (directory add/remove/rename), but the probe only runs when the upstream skill registry actually consults this provider — between refreshes the registry's collect cache answers `list`/`snapshot` without calling providers (the real reason E-71's out-of-band skill stays invisible), while `get` of an already-indexed name still reaches the provider, so an out-of-band CONTENT edit of an existing skill shows fresh content there (description stays stale until refresh). Any out-of-band change is only guaranteed to be visible after `/evolution skills refresh` runs (or the process restarts).
27
+ - **Upstream-invalid entries are filtered, not published (P1-1, v18).** `SkillLibrary.list()` reports an empty description for a 0-byte/malformed SKILL.md (C-14 keeps it visible to the curator), and a tree entry created before the 0.3.64 name tightening can still carry a trailing/consecutive hyphen. Upstream `validateCandidate` throws on either shape, and that throw aborts the whole `ctx.skills` collection (breaking `agent/pre-step` and the `skill` tool for the session). This provider therefore skips (and warns once about) any candidate whose name is not upstream `SKILL_NAME` or whose description is empty. The entry stays visible to the curator/lifecycle; fix the SKILL.md frontmatter (or the directory name) to publish it.
28
+ - **`whenToUse` is published, `metadata` is not (E-11, v18).** A non-empty single-line `whenToUse` from the frontmatter is forwarded to the platform catalog (the upstream filesystem provider does the same, and the host/UI read it for routing hints). `metadata` needs a real YAML parse and stays unpublished by this provider.
29
+ - **Protection markers are best-effort per entry (A1-17, v18).** When the directory listing AND the per-marker probes fail, `SkillSummary.protectionUnknown` is true and consumers (curator, maintenance) treat the entry as protected rather than unprotected.
27
30
 
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
- import { SkillLibrary, evolutionIoAdapter, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
2
+ import { SKILL_NAME_RE, SkillLibrary, evolutionIoAdapter, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
3
3
  import { join } from "node:path";
4
4
  //#region lib/types/index.js
5
5
  /**
@@ -39,6 +39,22 @@ const Config = z.object({
39
39
  * pins the "lower rank wins" resolution on the registry side.
40
40
  */
41
41
  const EVOLUTION_SKILL_RANK = 390;
42
+ /**
43
+ * The mirror's `SKILL_NAME_RE` now carries the same shape (计划 B-4, v18), but
44
+ * this provider still filters: an EXISTING tree entry created before the
45
+ * tightening (trailing/consecutive hyphen) must not be forwarded, because
46
+ * upstream `validateCandidate` throws on it and that throw aborts the WHOLE
47
+ * `ctx.skills` collection (the caller is outside the provider try/catch),
48
+ * taking down `agent/pre-step` and the `skill` tool for the session.
49
+ */
50
+ const UPSTREAM_SKILL_NAME_RE = SKILL_NAME_RE;
51
+ /** Upstream `validateCandidate` also refuses an empty description; the
52
+ * mirror's `SkillLibrary.list()` legitimately reports `''` for a 0-byte or
53
+ * malformed SKILL.md (C-14 keeps it visible to the curator). Such an entry
54
+ * must not reach the platform registry. */
55
+ function publishableSkill(name, description) {
56
+ return UPSTREAM_SKILL_NAME_RE.test(name) && description.trim().length > 0;
57
+ }
42
58
  function apply(ctx, rawConfig = {}) {
43
59
  const invocation = {
44
60
  modelInvocable: rawConfig.modelInvocable ?? true,
@@ -49,6 +65,12 @@ function apply(ctx, rawConfig = {}) {
49
65
  const included = new Set(rawConfig.includeSkillNames ?? []);
50
66
  const excluded = new Set(rawConfig.excludeSkillNames ?? []);
51
67
  const visible = (name) => (included.size === 0 || included.has(name)) && !excluded.has(name);
68
+ const warnedUnpublishable = /* @__PURE__ */ new Set();
69
+ const warnUnpublishable = (name, description) => {
70
+ if (warnedUnpublishable.has(name)) return;
71
+ warnedUnpublishable.add(name);
72
+ ctx.logger.warn(`evolution-skill-catalog: not publishing "${name}" — ${description.trim().length === 0 ? "empty description" : "name is not accepted by the upstream skill registry"} (fix the SKILL.md frontmatter; the curator still sees the entry)`);
73
+ };
52
74
  let control;
53
75
  let summariesCache = null;
54
76
  let summariesStamp = null;
@@ -66,10 +88,21 @@ function apply(ctx, rawConfig = {}) {
66
88
  };
67
89
  const provider = {
68
90
  name: "dsh-evolution",
69
- async list(_options) {
70
- return (await summaries()).filter((summary) => visible(summary.name)).map((summary) => ({
91
+ async list(options) {
92
+ options.signal?.throwIfAborted();
93
+ const all = await summaries();
94
+ options.signal?.throwIfAborted();
95
+ return all.filter((summary) => {
96
+ if (!visible(summary.name)) return false;
97
+ if (!publishableSkill(summary.name, summary.description)) {
98
+ warnUnpublishable(summary.name, summary.description);
99
+ return false;
100
+ }
101
+ return true;
102
+ }).map((summary) => ({
71
103
  name: summary.name,
72
104
  description: summary.description,
105
+ ...summary.whenToUse !== void 0 ? { whenToUse: summary.whenToUse } : {},
73
106
  invocation,
74
107
  source: "user-dsh",
75
108
  provider: "dsh-evolution",
@@ -82,16 +115,22 @@ function apply(ctx, rawConfig = {}) {
82
115
  }
83
116
  }));
84
117
  },
85
- async get(candidate) {
118
+ async get(candidate, options) {
119
+ options?.signal?.throwIfAborted();
86
120
  const name = candidate.name;
87
121
  if (!visible(name)) return void 0;
88
122
  const summary = (await summaries()).find((item) => item.name === name);
89
123
  if (!summary) return void 0;
124
+ if (!publishableSkill(summary.name, summary.description)) {
125
+ warnUnpublishable(summary.name, summary.description);
126
+ return;
127
+ }
90
128
  const content = await library.read(name);
91
129
  if (content === null) return void 0;
92
130
  return {
93
131
  name,
94
132
  description: summary.description,
133
+ ...summary.whenToUse !== void 0 ? { whenToUse: summary.whenToUse } : {},
95
134
  invocation,
96
135
  source: "user-dsh",
97
136
  provider: "dsh-evolution",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-skill-catalog",
3
3
  "description": "Native ctx.skills provider for the evolution-managed skill tree (community build)",
4
- "version": "0.3.62",
4
+ "version": "0.3.64",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,18 +31,18 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-core": "^0.3.62"
34
+ "@lmzhen/dsh-evolution-core": "^0.3.64"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@deepseek-ai/cordis": "^4.0.1",
38
38
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
39
39
  "@deepseek-ai/dsh-skill": "^0.1.1-rc.2",
40
- "@lmzhen/dsh-evolution-io": "^0.3.62"
40
+ "@lmzhen/dsh-evolution-io": "^0.3.64"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
44
44
  "@deepseek-ai/dsh-skill": "^0.1.1-rc.2",
45
- "@lmzhen/dsh-evolution-core": "^0.3.62",
46
- "@lmzhen/dsh-evolution-io": "^0.3.62"
45
+ "@lmzhen/dsh-evolution-core": "^0.3.64",
46
+ "@lmzhen/dsh-evolution-io": "^0.3.64"
47
47
  }
48
48
  }