@lmzhen/dsh-evolution-core 0.1.0-rc.47 → 0.1.0-rc.49

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
@@ -386,6 +386,39 @@ function buildCuratorRunReport(input) {
386
386
  ...input.llmReviewEnabled === void 0 ? {} : { llmReviewEnabled: input.llmReviewEnabled }
387
387
  };
388
388
  }
389
+ /**
390
+ * Render a curator run report as a compact human-readable markdown digest
391
+ * (G6): run metadata first, then the notable sections (archived / failed /
392
+ * stale candidates / LLM nominations).
393
+ */
394
+ function renderCuratorReportMarkdown(report) {
395
+ const lines = [
396
+ `# Curator run ${report.runId}`,
397
+ "",
398
+ `- **Started** ${report.startedAt}`,
399
+ `- **Finished** ${report.finishedAt}`,
400
+ `- **Stale candidates**: ${report.staleCandidates.length}`,
401
+ `- **LLM nominations**: ${report.llmNominations.length}`,
402
+ `- **Archived**: ${report.archived.length}`,
403
+ `- **Failed**: ${report.failed.length}`,
404
+ ...report.snapshotPath === void 0 ? [] : [`- **Snapshot**: ${report.snapshotPath}`],
405
+ ...report.llmReviewEnabled === void 0 ? [] : [`- **llmReview**: ${report.llmReviewEnabled}`]
406
+ ];
407
+ const section = (title, items) => items.length === 0 ? [] : [
408
+ "",
409
+ `## ${title}`,
410
+ "",
411
+ ...items.map((item) => `- ${item}`)
412
+ ];
413
+ return [
414
+ ...lines,
415
+ ...section("Archived", report.archived.map((item) => `${item.name} (${item.reason})`)),
416
+ ...section("Failed", report.failed.map((item) => `${item.name}: ${item.reason}`)),
417
+ ...section("Stale candidates", report.staleCandidates),
418
+ ...section("LLM nominations", report.llmNominations),
419
+ ""
420
+ ].join("\n");
421
+ }
389
422
  const NOMINATION_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
390
423
  /**
391
424
  * Parse the curator LLM's YAML nomination block (consolidations + prunings).
@@ -1897,10 +1930,18 @@ var SkillLibrary = class {
1897
1930
  root;
1898
1931
  limits;
1899
1932
  io;
1900
- constructor(root = skillsRoot(), io = nodeEvolutionIo(), limits = DEFAULT_SKILL_LIMITS) {
1933
+ onMutation;
1934
+ constructor(root = skillsRoot(), io = nodeEvolutionIo(), limits = DEFAULT_SKILL_LIMITS, onMutation) {
1901
1935
  this.root = root;
1902
1936
  this.io = io;
1903
1937
  this.limits = limits;
1938
+ this.onMutation = onMutation;
1939
+ }
1940
+ /** Notify the mutation observer after a successful write; observers must never fail the mutation. */
1941
+ notifyMutation(event) {
1942
+ try {
1943
+ this.onMutation?.(event);
1944
+ } catch {}
1904
1945
  }
1905
1946
  async list() {
1906
1947
  const summaries = [];
@@ -1909,14 +1950,18 @@ var SkillLibrary = class {
1909
1950
  const md = await this.io.readText(join(dir, "SKILL.md"));
1910
1951
  if (!md) continue;
1911
1952
  const parsed = parseFrontmatter(md);
1912
- const protectedBy = await this.deleteProtection(name);
1913
- const managed = await this.io.exists(markerPath(dir, "hermes-managed"));
1953
+ let entries = [];
1954
+ try {
1955
+ entries = await this.io.list(dir);
1956
+ } catch {}
1957
+ const has = (marker) => entries.includes(marker);
1958
+ const protectedBy = has("bundled") ? "bundled" : has("hub-installed") ? "hub-installed" : has("pinned") ? "pinned" : null;
1914
1959
  summaries.push({
1915
1960
  name,
1916
1961
  description: parsed?.frontmatter.description ?? "",
1917
1962
  path: dir,
1918
1963
  protectedBy,
1919
- managed,
1964
+ managed: has("hermes-managed"),
1920
1965
  archived: false
1921
1966
  });
1922
1967
  }
@@ -2087,6 +2132,11 @@ var SkillLibrary = class {
2087
2132
  await this.io.writeText(join(dir, "SKILL.md"), content.trimEnd() + "\n");
2088
2133
  if (origin !== "foreground") await this.io.writeText(markerPath(dir, "hermes-managed"), "");
2089
2134
  await this.audit(normalized, "create", null, content, "created");
2135
+ this.notifyMutation({
2136
+ action: "create",
2137
+ name: normalized,
2138
+ filePath: dir
2139
+ });
2090
2140
  return {
2091
2141
  ok: true,
2092
2142
  message: `Skill "${normalized}" created.`,
@@ -2123,6 +2173,11 @@ var SkillLibrary = class {
2123
2173
  };
2124
2174
  await this.io.writeText(join(dir, "SKILL.md"), content.trimEnd() + "\n");
2125
2175
  await this.audit(name, "update", md, content, "updated");
2176
+ this.notifyMutation({
2177
+ action: "update",
2178
+ name,
2179
+ filePath: dir
2180
+ });
2126
2181
  return {
2127
2182
  ok: true,
2128
2183
  message: `Skill "${name}" updated.`,
@@ -2190,6 +2245,11 @@ var SkillLibrary = class {
2190
2245
  };
2191
2246
  await this.io.writeText(target, patched.trimEnd() + "\n");
2192
2247
  await this.audit(name, "patch", md, patched, `patched ${patchLabel}`);
2248
+ this.notifyMutation({
2249
+ action: "patch",
2250
+ name,
2251
+ filePath: dir
2252
+ });
2193
2253
  return {
2194
2254
  ok: true,
2195
2255
  message: `Skill "${name}" patched (${patchLabel}).`,
@@ -2235,6 +2295,11 @@ var SkillLibrary = class {
2235
2295
  const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
2236
2296
  await this.io.writeText(join(dest, ".archive-reason"), `${(/* @__PURE__ */ new Date()).toISOString()}: ${reason}\n`);
2237
2297
  await this.audit(name, "archive", md, null, reason);
2298
+ this.notifyMutation({
2299
+ action: "archive",
2300
+ name,
2301
+ archivedPath: dest
2302
+ });
2238
2303
  return {
2239
2304
  ok: true,
2240
2305
  message: `Skill "${name}" archived to .archive.`,
@@ -2314,6 +2379,11 @@ var SkillLibrary = class {
2314
2379
  message: `Consolidation failed and was rolled back: ${error instanceof Error ? error.message : String(error)}`
2315
2380
  };
2316
2381
  }
2382
+ this.notifyMutation({
2383
+ action: "consolidate",
2384
+ name: targetName,
2385
+ filePath: targetDir
2386
+ });
2317
2387
  return {
2318
2388
  ok: true,
2319
2389
  message: `Consolidated ${normalizedSources.join(", ")} into "${targetName}".`,
@@ -2359,6 +2429,11 @@ var SkillLibrary = class {
2359
2429
  await this.io.remove(source);
2360
2430
  }
2361
2431
  if (await this.io.exists(join(dest, ".archive-reason"))) await this.io.remove(join(dest, ".archive-reason"));
2432
+ this.notifyMutation({
2433
+ action: "restore",
2434
+ name,
2435
+ filePath: dest
2436
+ });
2362
2437
  return {
2363
2438
  ok: true,
2364
2439
  message: `Skill "${name}" restored from .archive.`,
@@ -2400,6 +2475,11 @@ var SkillLibrary = class {
2400
2475
  const existing = await this.io.readText(target).catch(() => null);
2401
2476
  await this.io.writeText(target, content);
2402
2477
  await this.audit(name, "write_file", existing, content, `wrote ${filePath}`);
2478
+ this.notifyMutation({
2479
+ action: "write_file",
2480
+ name,
2481
+ filePath: target
2482
+ });
2403
2483
  return {
2404
2484
  ok: true,
2405
2485
  message: `Support file "${filePath}" written to "${name}".`,
@@ -2436,6 +2516,11 @@ var SkillLibrary = class {
2436
2516
  const before = await this.io.readText(target).catch(() => null);
2437
2517
  await this.io.remove(target);
2438
2518
  await this.audit(name, "remove_file", before, null, `removed ${filePath}`);
2519
+ this.notifyMutation({
2520
+ action: "remove_file",
2521
+ name,
2522
+ filePath: target
2523
+ });
2439
2524
  return {
2440
2525
  ok: true,
2441
2526
  message: `Support file "${filePath}" removed from "${name}".`,
@@ -2454,7 +2539,9 @@ var SkillLibrary = class {
2454
2539
  let dest = join(backupRoot, `skills-${stamp}`);
2455
2540
  while (await this.io.exists(dest)) dest = join(backupRoot, `skills-${stamp}-${Math.random().toString(36).slice(2, 8)}`);
2456
2541
  const names = await listNames(this.root, this.io);
2457
- for (const name of names) await this.io.copy(this.dirOf(name), join(dest, name));
2542
+ await Promise.all(names.map(async (name) => {
2543
+ await this.io.copy(this.dirOf(name), join(dest, name));
2544
+ }));
2458
2545
  const sidecars = [];
2459
2546
  for (const sidecar of [usageFile(this.root), suppressedFile(this.root)]) if (await this.io.exists(sidecar)) {
2460
2547
  const name = basename(sidecar);
@@ -2467,12 +2554,11 @@ var SkillLibrary = class {
2467
2554
  await this.io.copy(archiveRoot, join(dest, ".archive"));
2468
2555
  hasArchive = true;
2469
2556
  }
2470
- const extraNames = [];
2471
- for (const extra of extras) {
2472
- if (!SNAPSHOT_EXTRA_NAME_RE.test(extra.name)) continue;
2557
+ const validExtras = extras.filter((extra) => SNAPSHOT_EXTRA_NAME_RE.test(extra.name));
2558
+ const extraNames = validExtras.map((extra) => extra.name);
2559
+ await Promise.all(validExtras.map(async (extra) => {
2473
2560
  await this.io.writeText(join(dest, "extras", extra.name), extra.content);
2474
- extraNames.push(extra.name);
2475
- }
2561
+ }));
2476
2562
  await this.io.writeText(join(dest, "manifest.json"), JSON.stringify({
2477
2563
  reason,
2478
2564
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2578,6 +2664,10 @@ var SkillLibrary = class {
2578
2664
  } else if (manifest.hasArchive === false) await this.io.remove(archiveRoot);
2579
2665
  }
2580
2666
  const snapshotExtras = await this.readSnapshotExtras(latest.path);
2667
+ this.notifyMutation({
2668
+ action: "restore",
2669
+ name: "snapshot"
2670
+ });
2581
2671
  return {
2582
2672
  ok: true,
2583
2673
  message: `Restored skill tree from ${latest.path}`,
@@ -2652,4 +2742,4 @@ var JsonState = class JsonState {
2652
2742
  }
2653
2743
  };
2654
2744
  //#endregion
2655
- export { COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EvolutionGateSet, JsonState, LOW_QUALITY_THRESHOLD, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MUTATIONS_FILE_VERSION, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, SKILL_NAME_RE, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, computeDedupGroups, computeLifecycleTransitions, computeQualityScores, computeScopeView, contentHash, createGateSet, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutationsFile, nodeEvolutionIo, normalizeUsageRecord, observeEvent, parseCuratorNominations, parseFrontmatter, recordMutation, relatedSkillNames, resolveOrigins, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, usageFile, validateFrontmatter, verifyPromptBundle };
2745
+ export { COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EvolutionGateSet, JsonState, LOW_QUALITY_THRESHOLD, MAX_DESCRIPTION_LENGTH, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MUTATIONS_FILE_VERSION, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, SKILL_NAME_RE, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, computeDedupGroups, computeLifecycleTransitions, computeQualityScores, computeScopeView, contentHash, createGateSet, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutationsFile, nodeEvolutionIo, normalizeUsageRecord, observeEvent, parseCuratorNominations, parseFrontmatter, recordMutation, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, usageFile, validateFrontmatter, verifyPromptBundle };
@@ -75,6 +75,12 @@ export interface CuratorReportInput {
75
75
  llmReviewEnabled?: boolean;
76
76
  }
77
77
  export declare function buildCuratorRunReport(input: CuratorReportInput): CuratorRunReport;
78
+ /**
79
+ * Render a curator run report as a compact human-readable markdown digest
80
+ * (G6): run metadata first, then the notable sections (archived / failed /
81
+ * stale candidates / LLM nominations).
82
+ */
83
+ export declare function renderCuratorReportMarkdown(report: CuratorRunReport): string;
78
84
  /** One LLM-nominated consolidation: `from` merges into the umbrella `into`. */
79
85
  export interface CuratorConsolidation {
80
86
  from: string;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { type EvolutionIoLike } from './io.ts';
10
10
  import { type MutationRecord } from './mutations.ts';
11
+ import type { EvolutionSkillMutatedEvent } from './events.ts';
11
12
  export interface SkillLimits {
12
13
  maxNameLength: number;
13
14
  maxDescriptionLength: number;
@@ -97,7 +98,10 @@ export declare class SkillLibrary {
97
98
  readonly root: string;
98
99
  readonly limits: SkillLimits;
99
100
  private readonly io;
100
- constructor(root?: string, io?: EvolutionIoLike, limits?: SkillLimits);
101
+ private readonly onMutation;
102
+ constructor(root?: string, io?: EvolutionIoLike, limits?: SkillLimits, onMutation?: (event: EvolutionSkillMutatedEvent) => void);
103
+ /** Notify the mutation observer after a successful write; observers must never fail the mutation. */
104
+ private notifyMutation;
101
105
  list(): Promise<SkillSummary[]>;
102
106
  read(rawName: string): Promise<string | null>;
103
107
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.1.0-rc.47",
4
+ "version": "0.1.0-rc.49",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },