@lmzhen/dsh-evolution-curator 0.1.0-rc.42 → 0.1.0-rc.44

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,14 @@ 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, computeScopeView, 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, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, computeQualityScores, computeScopeView, emptyRecord, evolutionHome, evolutionIoAdapter, loadSuppressedNames, loadUsage, parseCuratorNominations, relatedSkillNames, 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
+ /** Quality-warned skills may turn stale after this many idle days (package-private tunable, P2-8). */
13
+ const DEFAULT_QUALITY_WARN_STALE_AFTER_DAYS = 7;
12
14
  /**
13
15
  * Block LLM-nominated consolidations that would touch a gate-protected name:
14
16
  * exclude / referenced / suppressed skills must never merge (neither as the
@@ -28,7 +30,7 @@ var EvolutionCurator = class extends Service {
28
30
  archiveAfterDays: z.number().default(DEFAULT_ARCHIVE_AFTER_DAYS),
29
31
  llmReview: z.boolean().default(false),
30
32
  curatorProvider: z.string().default("deepseek-official"),
31
- qualityWarnStaleAfterDays: z.number().default(7),
33
+ qualityWarnStaleAfterDays: z.number().default(DEFAULT_QUALITY_WARN_STALE_AFTER_DAYS),
32
34
  minIdleHours: z.number().default(DEFAULT_MIN_IDLE_HOURS),
33
35
  excludeSkillNames: z.array(z.string()).default([]),
34
36
  manageUnmanaged: z.boolean().default(false),
@@ -68,7 +70,7 @@ var EvolutionCurator = class extends Service {
68
70
  this.archiveAfterDays = config.archiveAfterDays ?? DEFAULT_ARCHIVE_AFTER_DAYS;
69
71
  this.llmReview = config.llmReview ?? false;
70
72
  this.curatorProvider = config.curatorProvider ?? "deepseek-official";
71
- this.qualityWarnStaleAfterDays = config.qualityWarnStaleAfterDays ?? 7;
73
+ this.qualityWarnStaleAfterDays = config.qualityWarnStaleAfterDays ?? DEFAULT_QUALITY_WARN_STALE_AFTER_DAYS;
72
74
  this.minIdleHours = config.minIdleHours ?? DEFAULT_MIN_IDLE_HOURS;
73
75
  this.excludeSkillNames = new Set(config.excludeSkillNames ?? []);
74
76
  this.manageUnmanaged = config.manageUnmanaged ?? false;
@@ -109,6 +111,33 @@ var EvolutionCurator = class extends Service {
109
111
  this.bootCheck = void 0;
110
112
  }
111
113
  /**
114
+ * Pause or resume automatic curation (B-line G2, Hermes `set_paused`
115
+ * parity): the flag is persisted on the curator state record and the
116
+ * `run()` paused gate skips automatic passes while it holds. Manual runs
117
+ * (`ignoreGates`) are unaffected — pause is a soft stop for the scheduler,
118
+ * not a lock on the operator.
119
+ *
120
+ * Pausing on a state-less curator state seeds the record with `lastRunAt:
121
+ * now`, so a later resume re-enters through the interval gate and defers a
122
+ * full cycle instead of firing immediately (first-run defer interaction,
123
+ * kept deliberately: an unattended resume must not auto-run mid-boot).
124
+ */
125
+ async setPaused(paused) {
126
+ const stateService = this.curatorStateService();
127
+ const persisted = await stateService?.loadCuratorState() ?? null;
128
+ await stateService?.saveCuratorState({
129
+ schemaVersion: 1,
130
+ lastRunAt: persisted?.lastRunAt ?? Date.now(),
131
+ runCount: persisted?.runCount ?? 0,
132
+ lastSummary: persisted?.lastSummary ?? (paused ? "paused" : "resumed"),
133
+ paused
134
+ });
135
+ }
136
+ /** Current persisted curator state (read-only view for /evolution curator status). */
137
+ async status() {
138
+ return await this.curatorStateService()?.loadCuratorState() ?? null;
139
+ }
140
+ /**
112
141
  * One automatic schedule check: run a pass when the persisted curator state
113
142
  * (falling back to the in-memory clock for state-less compositions) is at
114
143
  * least one interval old. All gates — interval, idle, first-run defer,
@@ -254,7 +283,14 @@ var EvolutionCurator = class extends Service {
254
283
  const runId = randomUUID();
255
284
  const stateService = this.curatorStateService();
256
285
  const lifecycle = this.lifecycle();
257
- const persisted = await stateService?.loadCuratorState();
286
+ const persisted = await stateService?.loadCuratorState() ?? null;
287
+ if (!ignoreGates && persisted?.paused === true) return {
288
+ stale: [],
289
+ archived: [],
290
+ errors: [],
291
+ report: this.skippedReport(runId, startedAt),
292
+ skipped: "paused"
293
+ };
258
294
  if (!ignoreGates && persisted && Date.now() - persisted.lastRunAt < lifecycle.intervalHours * 36e5) return {
259
295
  stale: [],
260
296
  archived: [],
@@ -291,6 +327,7 @@ var EvolutionCurator = class extends Service {
291
327
  const snapshotPath = dryRun ? void 0 : await this.snapshotFull("pre-curator-run");
292
328
  const suppressedNames = new Set(await loadSuppressedNames(root, this.io));
293
329
  const { bundledNames, treeNames } = await this.seedBaseline(usage);
330
+ await this.scoreTree(usage, treeNames);
294
331
  const result = computeLifecycleTransitions(usage, {
295
332
  staleAfterDays: lifecycle.staleAfterDays,
296
333
  archiveAfterDays: lifecycle.archiveAfterDays,
@@ -302,7 +339,6 @@ var EvolutionCurator = class extends Service {
302
339
  suppressedNames,
303
340
  referencedSkillNames: this.referencedSkillNames
304
341
  });
305
- await this.scoreTree(usage, treeNames);
306
342
  const nominations = this.llmReview ? await this.recommend(result.markStale, { dryRun }) : {
307
343
  prunings: [],
308
344
  consolidations: []
@@ -421,14 +457,7 @@ var EvolutionCurator = class extends Service {
421
457
  for (const name of treeNames) {
422
458
  const content = await this.skills.read(name);
423
459
  if (!content) continue;
424
- const parsed = parseFrontmatter(content);
425
- if (!parsed) continue;
426
- const raw = parsed.frontmatter["related_skills"];
427
- if (typeof raw !== "string") continue;
428
- for (const match of Array.from(raw.matchAll(/[a-z0-9][a-z0-9-]*/g))) {
429
- const target = match[0];
430
- if (target && SKILL_NAME_RE.test(target) && target !== name) counts.set(target, (counts.get(target) ?? 0) + 1);
431
- }
460
+ for (const target of relatedSkillNames(content, name)) counts.set(target, (counts.get(target) ?? 0) + 1);
432
461
  }
433
462
  return counts;
434
463
  }
@@ -94,6 +94,21 @@ export declare class EvolutionCurator extends Service {
94
94
  private lifecycle;
95
95
  start(): void;
96
96
  stop(): void;
97
+ /**
98
+ * Pause or resume automatic curation (B-line G2, Hermes `set_paused`
99
+ * parity): the flag is persisted on the curator state record and the
100
+ * `run()` paused gate skips automatic passes while it holds. Manual runs
101
+ * (`ignoreGates`) are unaffected — pause is a soft stop for the scheduler,
102
+ * not a lock on the operator.
103
+ *
104
+ * Pausing on a state-less curator state seeds the record with `lastRunAt:
105
+ * now`, so a later resume re-enters through the interval gate and defers a
106
+ * full cycle instead of firing immediately (first-run defer interaction,
107
+ * kept deliberately: an unattended resume must not auto-run mid-boot).
108
+ */
109
+ setPaused(paused: boolean): Promise<void>;
110
+ /** Current persisted curator state (read-only view for /evolution curator status). */
111
+ status(): Promise<CuratorStateRecordShape | null>;
97
112
  /**
98
113
  * One automatic schedule check: run a pass when the persisted curator state
99
114
  * (falling back to the in-memory clock for state-less compositions) is at
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.42",
4
+ "version": "0.1.0-rc.44",
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.42"
36
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.44"
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.42",
43
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.42"
42
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.44",
43
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.44"
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.42",
49
- "@lmzhen/dsh-evolution-io": "^0.1.0-rc.42",
50
- "@lmzhen/dsh-evolution-state": "^0.1.0-rc.42"
48
+ "@lmzhen/dsh-evolution-core": "^0.1.0-rc.44",
49
+ "@lmzhen/dsh-evolution-io": "^0.1.0-rc.44",
50
+ "@lmzhen/dsh-evolution-state": "^0.1.0-rc.44"
51
51
  }
52
52
  }