@lmzhen/dsh-evolution-curator 0.1.0-rc.26 → 0.1.0-rc.28

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/lib/index.js CHANGED
@@ -3,12 +3,22 @@ import { randomUUID } from "node:crypto";
3
3
  import { join } from "node:path";
4
4
  import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
5
5
  import z from "@deepseek-ai/schemastery";
6
- import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_STALE_AFTER_DAYS, SKILL_NAME_RE, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, computeQualityScores, emptyRecord, evolutionHome, evolutionIoAdapter, loadSuppressedNames, loadUsage, parseCuratorNominations, parseFrontmatter, saveSuppressedNames, saveUsage } from "@lmzhen/dsh-evolution-core";
6
+ import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_STALE_AFTER_DAYS, SKILL_NAME_RE, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, computeQualityScores, computeScopeView, emptyRecord, evolutionHome, evolutionIoAdapter, loadSuppressedNames, loadUsage, parseCuratorNominations, parseFrontmatter, saveSuppressedNames, saveUsage } from "@lmzhen/dsh-evolution-core";
7
7
  //#region lib/types/index.js
8
8
  /**
9
9
  * Deterministic skill lifecycle curator with interval gate and archive.
10
10
  * @module @lmzhen/dsh-evolution-curator
11
11
  */
12
+ /**
13
+ * Block LLM-nominated consolidations that would touch a gate-protected name:
14
+ * exclude / referenced / suppressed skills must never merge (neither as the
15
+ * source being archived nor as the umbrella being edited). Mirrors the control
16
+ * plane's `consolidate()` guard; automatic nominations must pass the same gate.
17
+ */
18
+ function gateConsolidations(consolidations, gates) {
19
+ const blocked = (name) => gates.exclude?.has(name) === true || gates.referenced?.has(name) === true || gates.suppressed?.has(name) === true;
20
+ return consolidations.filter((n) => !blocked(n.from) && !blocked(n.into));
21
+ }
12
22
  var EvolutionCurator = class extends Service {
13
23
  static inject = ["evolutionIo"];
14
24
  static Config = z.object({
@@ -219,12 +229,20 @@ var EvolutionCurator = class extends Service {
219
229
  prunings: [],
220
230
  consolidations: []
221
231
  };
222
- const llmNominations = nominations.prunings;
232
+ const gatedNominations = {
233
+ ...nominations,
234
+ consolidations: gateConsolidations(nominations.consolidations, {
235
+ exclude: this.excludeSkillNames,
236
+ referenced: this.referencedSkillNames,
237
+ suppressed: suppressedNames
238
+ })
239
+ };
240
+ const llmNominations = gatedNominations.prunings;
223
241
  const archiveCandidates = [...new Set([...result.archive, ...llmNominations])];
224
242
  const { archivedSkills, errors } = await this.applyMutations({
225
243
  dryRun,
226
244
  archiveCandidates,
227
- nominations,
245
+ nominations: gatedNominations,
228
246
  treeNames,
229
247
  usage,
230
248
  bundledNames,
@@ -240,7 +258,7 @@ var EvolutionCurator = class extends Service {
240
258
  llmNominations,
241
259
  archiveCandidates,
242
260
  archived: archivedSkills,
243
- failed: [...new Set([...archiveCandidates, ...nominations.consolidations.map((item) => item.from)])].filter((name) => errors.some((error) => error.startsWith(`${name}:`))).map((name) => {
261
+ failed: [...new Set([...archiveCandidates, ...gatedNominations.consolidations.map((item) => item.from)])].filter((name) => errors.some((error) => error.startsWith(`${name}:`))).map((name) => {
244
262
  return {
245
263
  name,
246
264
  reason: errors.find((item) => item.startsWith(`${name}:`))?.slice(name.length + 2) ?? "unknown"
@@ -257,7 +275,7 @@ var EvolutionCurator = class extends Service {
257
275
  this.ctx.logger.warn(error);
258
276
  }
259
277
  const llmHint = !this.llmReview && result.markStale.length > 0 ? " (llmReview: off - deterministic archive only; set llmReview: true for the LLM merge channel)" : "";
260
- const summary = `${dryRun ? "dry-run" : "auto"}: stale:${result.markStale.length} archived:${archivedSkills.length} consolidated:${nominations.consolidations.length}${llmHint}`;
278
+ const summary = `${dryRun ? "dry-run" : "auto"}: stale:${result.markStale.length} archived:${archivedSkills.length} consolidated:${gatedNominations.consolidations.length}${llmHint}`;
261
279
  await stateService?.saveCuratorState({
262
280
  schemaVersion: 1,
263
281
  lastRunAt: dryRun ? persisted?.lastRunAt ?? this.lastRun : this.lastRun,
@@ -270,7 +288,7 @@ var EvolutionCurator = class extends Service {
270
288
  archived: archivedSkills.map((item) => item.name),
271
289
  errors,
272
290
  report,
273
- ...this.llmReview ? { nominations } : {}
291
+ ...this.llmReview ? { nominations: gatedNominations } : {}
274
292
  };
275
293
  }
276
294
  /**
@@ -286,6 +304,8 @@ var EvolutionCurator = class extends Service {
286
304
  treeNames.add(summary.name);
287
305
  if (!usage.has(summary.name)) usage.set(summary.name, emptyRecord());
288
306
  if (await this.skills.isBundled(summary.name)) bundledNames.add(summary.name);
307
+ const record = usage.get(summary.name);
308
+ if (record) record.pinned = await this.skills.isPinned(summary.name);
289
309
  }
290
310
  return {
291
311
  bundledNames,
@@ -433,6 +453,27 @@ var EvolutionCurator = class extends Service {
433
453
  }
434
454
  }
435
455
  /**
456
+ * Read-only lifecycle scope classification: which skills are in scope,
457
+ * watched (stale/quality-warned), exempted, or protected. Uses the same
458
+ * candidate gate as `run()` (`computeScopeView` / `lifecycleCandidate`),
459
+ * so the view always predicts what a curator pass may touch.
460
+ */
461
+ async scopeView() {
462
+ const root = this.skills.root;
463
+ const usage = await loadUsage(root, this.io);
464
+ const { bundledNames } = await this.seedBaseline(usage);
465
+ return computeScopeView(usage, {
466
+ staleAfterDays: this.lifecycle().staleAfterDays,
467
+ archiveAfterDays: this.lifecycle().archiveAfterDays,
468
+ excludeSkillNames: this.excludeSkillNames,
469
+ referencedSkillNames: this.referencedSkillNames,
470
+ suppressedNames: new Set(await loadSuppressedNames(root, this.io)),
471
+ manageUnmanaged: this.manageUnmanaged,
472
+ pruneBuiltins: this.pruneBuiltins,
473
+ bundledNames
474
+ });
475
+ }
476
+ /**
436
477
  * Control-plane consolidation: merge source skill bodies into `target`,
437
478
  * archive the sources with an absorbed-into marker, and fold their usage
438
479
  * records into `archived` state. Snapshot-then-mutate, never a hard delete.
@@ -476,4 +517,4 @@ var EvolutionCurator = class extends Service {
476
517
  }
477
518
  };
478
519
  //#endregion
479
- export { EvolutionCurator, EvolutionCurator as default };
520
+ export { EvolutionCurator, EvolutionCurator as default, gateConsolidations };
@@ -5,7 +5,7 @@
5
5
  import { Context, Service } from '@deepseek-ai/cordis';
6
6
  import type Schema from '@deepseek-ai/schemastery';
7
7
  import { SkillLibrary } from '@deepseek-ai/dsh-evolution-core';
8
- import { type CuratorNominations, type CuratorRunReport, type SkillActionResult } from '@deepseek-ai/dsh-evolution-core';
8
+ import { type CuratorConsolidation, type CuratorNominations, type CuratorRunReport, type ScopeView, type SkillActionResult } from '@deepseek-ai/dsh-evolution-core';
9
9
  declare module '@deepseek-ai/cordis' {
10
10
  interface Context {
11
11
  evolutionCurator: EvolutionCurator;
@@ -54,6 +54,17 @@ export interface CuratorStateRecordShape {
54
54
  lastSummary: string;
55
55
  paused: boolean;
56
56
  }
57
+ /**
58
+ * Block LLM-nominated consolidations that would touch a gate-protected name:
59
+ * exclude / referenced / suppressed skills must never merge (neither as the
60
+ * source being archived nor as the umbrella being edited). Mirrors the control
61
+ * plane's `consolidate()` guard; automatic nominations must pass the same gate.
62
+ */
63
+ export declare function gateConsolidations(consolidations: CuratorConsolidation[], gates: {
64
+ exclude?: ReadonlySet<string>;
65
+ referenced?: ReadonlySet<string>;
66
+ suppressed?: ReadonlySet<string>;
67
+ }): CuratorConsolidation[];
57
68
  export declare class EvolutionCurator extends Service {
58
69
  static inject: string[];
59
70
  static Config: Schema<Config>;
@@ -124,6 +135,13 @@ export declare class EvolutionCurator extends Service {
124
135
  private applyMutations;
125
136
  private recentSessionActive;
126
137
  latestReport(): Promise<CuratorRunReport | null>;
138
+ /**
139
+ * Read-only lifecycle scope classification: which skills are in scope,
140
+ * watched (stale/quality-warned), exempted, or protected. Uses the same
141
+ * candidate gate as `run()` (`computeScopeView` / `lifecycleCandidate`),
142
+ * so the view always predicts what a curator pass may touch.
143
+ */
144
+ scopeView(): Promise<ScopeView>;
127
145
  /**
128
146
  * Control-plane consolidation: merge source skill bodies into `target`,
129
147
  * archive the sources with an absorbed-into marker, and fold their usage
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-curator",
3
3
  "description": "Deterministic skill lifecycle and recovery (community build)",
4
- "version": "0.1.0-rc.26",
4
+ "version": "0.1.0-rc.28",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -33,20 +33,20 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "@deepseek-ai/schemastery": "^3.18.1",
36
- "@lmzhen/dsh-evolution-core": "^0.1.0-rc.26"
36
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.28"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@deepseek-ai/cordis": "^4.0.1",
40
40
  "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
41
41
  "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
42
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.26",
43
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.26"
42
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.28",
43
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.28"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
47
47
  "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
48
- "@lmzhen/dsh-evolution-core": "^0.1.0-rc.26",
49
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.26",
50
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.26"
48
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.28",
49
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.28",
50
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.28"
51
51
  }
52
52
  }