@lmzhen/dsh-evolution-core 0.3.17 → 0.3.18

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
@@ -45,6 +45,10 @@ function evolutionIoAdapter(provider) {
45
45
  isSymlink: (path) => {
46
46
  const io = provider();
47
47
  return io.isSymlink ? io.isSymlink(path) : Promise.resolve(null);
48
+ },
49
+ mtime: (path) => {
50
+ const io = provider();
51
+ return io.mtime ? io.mtime(path) : Promise.resolve(null);
48
52
  }
49
53
  };
50
54
  }
@@ -226,6 +230,14 @@ function nodeEvolutionIo() {
226
230
  } catch {
227
231
  return null;
228
232
  }
233
+ },
234
+ async mtime(path) {
235
+ try {
236
+ return (await stat(path)).mtimeMs;
237
+ } catch (error) {
238
+ if (isMissing(error)) return null;
239
+ throw error;
240
+ }
229
241
  }
230
242
  };
231
243
  }
@@ -2469,7 +2481,7 @@ function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS)
2469
2481
  const needs = snapshot.bodyChars >= thresholds.softBodyChars * 2;
2470
2482
  if (needs) reasons.push(`body ${snapshot.bodyChars} chars is >= 2x the soft limit (${thresholds.softBodyChars}) — consider splitting or offloading`);
2471
2483
  else if (snapshot.bodyChars >= thresholds.softBodyChars) reasons.push(`body ${snapshot.bodyChars} chars above the soft limit (${thresholds.softBodyChars})`);
2472
- if (snapshot.bodyText && snapshot.bodyChars >= MIN_STAMP_BODY_CHARS) {
2484
+ if (snapshot.bodyText && snapshot.bodyChars >= 2e3) {
2473
2485
  const kb = Math.max(1, snapshot.bodyChars / 1024);
2474
2486
  dims.stampDensityPerKb = (snapshot.bodyText.match(HEALTH_STAMP_RE) ?? []).length / kb;
2475
2487
  if (dims.stampDensityPerKb >= thresholds.stampDensityPerKb) reasons.push(`stamp density ${dims.stampDensityPerKb.toFixed(1)}/KB (rc/sha/date lines — log-like content in the body)`);
@@ -2752,6 +2764,14 @@ const SNAPSHOT_EXTRA_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
2752
2764
  function skillsRoot(env = process.env) {
2753
2765
  return join(env.DSH_HOME || join(homedir(), ".dsh"), "skills");
2754
2766
  }
2767
+ /** 0.3.18 (S4.1, E-30): the ONE root resolution for every member that reads
2768
+ * the skills tree — tool-skill-manage / evolution-skill-catalog / skill-usage
2769
+ * / evolution-learning-graph used to each resolve `config.root || skillsRoot()`
2770
+ * (and the graph ignored config entirely). Empty/whitespace config falls
2771
+ * through to the default; callers pass their raw Config. */
2772
+ function resolveSkillsRoot(config = {}) {
2773
+ return (config.root ?? "").trim() || skillsRoot();
2774
+ }
2755
2775
  /**
2756
2776
  * Map a requesting session onto the two origin surfaces (rc.44 plan M2-2.3):
2757
2777
  * the APPROVAL surface treats every delegated subagent as the autonomous
@@ -3580,6 +3600,12 @@ var SkillLibrary = class {
3580
3600
  ok: false,
3581
3601
  message: threat
3582
3602
  };
3603
+ if (writeContent.trimEnd() + "\n" === md) return {
3604
+ ok: true,
3605
+ message: `Skill "${name}" unchanged: old_string already equals the replacement (${patchLabel}); nothing written.`,
3606
+ noop: true,
3607
+ path: dir
3608
+ };
3583
3609
  await this.io.writeText(target, writeContent.trimEnd() + "\n");
3584
3610
  await this.audit(name, "patch", md, writeContent, `patched ${patchLabel}`);
3585
3611
  this.notifyMutation({
@@ -3613,6 +3639,10 @@ var SkillLibrary = class {
3613
3639
  message: `Skill "${name}" is protected (${protection}).`
3614
3640
  };
3615
3641
  if (options.absorbedInto) {
3642
+ if (options.absorbedInto.trim() === name) return {
3643
+ ok: false,
3644
+ message: "absorbed_into cannot be the skill being archived (cannot absorb into itself)."
3645
+ };
3616
3646
  if (!await this.io.readText(join(this.dirOf(options.absorbedInto), "SKILL.md"))) return {
3617
3647
  ok: false,
3618
3648
  message: `absorbed_into="${options.absorbedInto}" does not exist.`
@@ -4370,4 +4400,4 @@ function evolutionHome(env = process.env) {
4370
4400
  return join(env.DSH_HOME || join(homedir(), ".dsh"), "evolution");
4371
4401
  }
4372
4402
  //#endregion
4373
- export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, 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_HEALTH_THRESHOLDS, 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, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, LOW_QUALITY_THRESHOLD, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, 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, RESTRUCTURE_TARGET_RE, SKILLS_GUIDANCE, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, appendEvolutionEvent, applyCuratorFields, applyCuratorLifecycleFields, applyCuratorMetaFields, assessStructureHealth, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, duplicateHeadings, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, findDriftSignal, foldCuratorFields, foldTurn, frontmatterBlock, frontmatterYamlUnsafeValues, getRecord, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadSuppressedNames, loadUsage, makeSerialQueue, markAgentCreated, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, overlongLines, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, readEvolutionEvents, readEvolutionTimeline, recordMutation, redactSecrets, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, retainEventArchives, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, verifyPromptBundle, yamlPlainScalarNeedsQuotes };
4403
+ export { AUTHORING_DESCRIPTION_BAR, COMBINED_REVIEW_PLAN_PROMPT, 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_HEALTH_THRESHOLDS, 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, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FORBIDDEN_CONTROL_KEYS, HEALTH_STAMP_RE, LOW_QUALITY_THRESHOLD, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, QUALITY_WEIGHTS, RESTRUCTURE_TARGET_RE, SKILLS_GUIDANCE, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, appendEvolutionEvent, applyCuratorFields, applyCuratorLifecycleFields, applyCuratorMetaFields, assessStructureHealth, authoringFeedback, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, composePresetComposition, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, createGateSet, duplicateHeadings, emptyRecord, evaluateThreat, eventsFile, evolutionHome, evolutionIoAdapter, findDriftSignal, foldCuratorFields, foldTurn, frontmatterBlock, frontmatterYamlUnsafeValues, getRecord, latestActivityAt, lifecycleCandidate, listEventArchives, loadMutations, loadSuppressedNames, loadUsage, makeSerialQueue, markAgentCreated, memoryRoot, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, observeEvent, overlongLines, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, readEvolutionEvents, readEvolutionTimeline, recordMutation, redactSecrets, relatedSkillNames, renderCuratorReportMarkdown, resolveOrigins, resolveSkillsRoot, retainEventArchives, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, transactIo, updateSuppressedNames, usageFile, usageObserved, validateFrontmatter, verifyPromptBundle, yamlPlainScalarNeedsQuotes };
@@ -42,11 +42,23 @@ export interface EvolutionSkillMutatedEvent {
42
42
  file?: string;
43
43
  archivedPath?: string;
44
44
  }
45
+ /** 0.3.18 (E-6): a turn-end review pipeline failure was caught (never an
46
+ * unhandled rejection); this event lets operators/observability see it. The
47
+ * reason is already logged by the emitter — the event is a timestamped signal. */
48
+ export interface EvolutionReviewErrorEvent {
49
+ sessionId: string;
50
+ }
45
51
  declare module '@deepseek-ai/cordis' {
46
52
  interface Events {
47
53
  'evolution/review-scheduled'(event: EvolutionReviewScheduledEvent): void;
48
54
  'evolution/plan-applied'(event: EvolutionPlanAppliedEvent): void;
49
55
  'evolution/skill-mutated'(event: EvolutionSkillMutatedEvent): void;
56
+ /** 0.3.18 (E-71): explicit catalog refresh request (`/evolution skills
57
+ * refresh`). Out-of-band tree edits (manual, git) may bypass the mutation
58
+ * event; listeners drop caches and invalidate downstream catalogs. No
59
+ * payload — it is a bare "re-read" signal, never a mutation record. */
60
+ 'evolution/skills-refresh'(): void;
61
+ 'evolution/review-error'(event: EvolutionReviewErrorEvent): void;
50
62
  }
51
63
  }
52
64
  //# sourceMappingURL=events.d.ts.map
package/lib/types/io.d.ts CHANGED
@@ -36,6 +36,14 @@ export interface EvolutionIoLike {
36
36
  * the path does not exist). Consumers treat `null` as "let it through".
37
37
  */
38
38
  isSymlink?(this: void, path: string): Promise<boolean | null>;
39
+ /**
40
+ * Optional mtime-generation probe (0.3.18, E-71): the path's mtime in
41
+ * milliseconds since epoch, or `null` when unknown (unsupported backend,
42
+ * missing path, stat failure). Consumers use it as a cheap invalidation
43
+ * stamp for a cached directory listing; a backend without it keeps
44
+ * event-driven invalidation only.
45
+ */
46
+ mtime?(this: void, path: string): Promise<number | null>;
39
47
  }
40
48
  /**
41
49
  * Run `task` inside `io.transact` when the backend provides it; otherwise fall
@@ -24,6 +24,13 @@ export interface SkillHealthThresholds {
24
24
  export declare const DEFAULT_HEALTH_THRESHOLDS: SkillHealthThresholds;
25
25
  /** Stamp regex shared by health assessment and the maintenance probe (single source, 011). */
26
26
  export declare const HEALTH_STAMP_RE: RegExp;
27
+ /**
28
+ * Bodies below this size skip stamp-density assessment: a few dates or shas
29
+ * in a short body are ordinary documentation, not log-like content. With the
30
+ * 1KB density floor a 3-date sentence in a small skill measured 3.0/KB and
31
+ * warned on a perfectly healthy body (audit 2026-08-31 X1).
32
+ */
33
+ export declare const MIN_STAMP_BODY_CHARS = 2000;
27
34
  export type SkillHealthVerdict = 'healthy' | 'warn' | 'needs-restructure';
28
35
  /** Facts a caller already has; assessors never do IO. */
29
36
  export interface SkillHealthSnapshot {
@@ -32,6 +32,9 @@ export interface SkillActionResult {
32
32
  /** Frontmatter keys auto-quoted for catalog-loadable YAML (0.3.11) — set
33
33
  * only when the write path modified the block. */
34
34
  normalizedFrontmatterFields?: string[];
35
+ /** 0.3.18 (E-68): patch produced byte-identical content (old===new) — no
36
+ * write, no audit, no mutation event; callers must not count a patch. */
37
+ noop?: boolean;
35
38
  }
36
39
  /**
37
40
  * One section move of a restructure proposal (008 batch B): a body section
@@ -81,6 +84,14 @@ export interface ArchiveOptions {
81
84
  allowBundled?: boolean;
82
85
  }
83
86
  export declare function skillsRoot(env?: NodeJS.ProcessEnv): string;
87
+ /** 0.3.18 (S4.1, E-30): the ONE root resolution for every member that reads
88
+ * the skills tree — tool-skill-manage / evolution-skill-catalog / skill-usage
89
+ * / evolution-learning-graph used to each resolve `config.root || skillsRoot()`
90
+ * (and the graph ignored config entirely). Empty/whitespace config falls
91
+ * through to the default; callers pass their raw Config. */
92
+ export declare function resolveSkillsRoot(config?: {
93
+ root?: string;
94
+ }): string;
84
95
  /**
85
96
  * Map a requesting session onto the two origin surfaces (rc.44 plan M2-2.3):
86
97
  * the APPROVAL surface treats every delegated subagent as the autonomous
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.3.17",
4
+ "version": "0.3.18",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },