@lmzhen/dsh-evolution-maintenance 0.4.1 → 0.5.0
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HEALTH_STAMP_RE, MIN_STAMP_BODY_CHARS, computeDedupGroups, computePrefixClusters, duplicateHeadings, frontmatterCatalogInvalid, isPresent, missingSupportPointers, narrowNameMatches, overlongLines, parseFrontmatter, usageObserved } from "@lmzhen/dsh-evolution-core";
|
|
1
|
+
import { HEALTH_STAMP_RE, MIN_STAMP_BODY_CHARS, bodyCost, computeDedupGroups, computePrefixClusters, duplicateHeadings, frontmatterCatalogInvalid, idleDays, isPresent, missingSupportPointers, narrowNameMatches, overlongLines, parseFrontmatter, resolveCitations, retirementReport, usageObserved } from "@lmzhen/dsh-evolution-core";
|
|
2
2
|
//#region lib/types/drift-scan.js
|
|
3
3
|
/**
|
|
4
4
|
* Skill-library snapshot assembly for drift scanning.
|
|
@@ -28,15 +28,25 @@ async function snapshotFromLibrary(library, options = {}) {
|
|
|
28
28
|
options.onReadError?.(entry.name, /* @__PURE__ */ new Error("no readable SKILL.md (missing file, or the directory name is not a valid skill name)"));
|
|
29
29
|
continue;
|
|
30
30
|
}
|
|
31
|
+
const supportFiles = options.supportFiles?.get(entry.name);
|
|
31
32
|
snapshots.push({
|
|
32
33
|
name: entry.name,
|
|
33
34
|
body,
|
|
34
35
|
description: options.descriptions?.get(entry.name),
|
|
35
|
-
supportFiles
|
|
36
|
+
supportFiles,
|
|
37
|
+
cost: bodyCost(body),
|
|
38
|
+
...supportFiles === void 0 ? {} : { citations: resolveCitations({
|
|
39
|
+
content: body,
|
|
40
|
+
file: "SKILL.md",
|
|
41
|
+
files: supportFiles
|
|
42
|
+
}) },
|
|
36
43
|
quality: options.quality?.get(entry.name),
|
|
37
44
|
...options.protected?.get(entry.name) !== void 0 ? { protected: options.protected.get(entry.name) } : {},
|
|
38
45
|
...options.catalogInvalid?.get(entry.name) === true ? { catalogInvalid: true } : {},
|
|
39
|
-
...options.usageObserved !== void 0 ? { usageObserved: options.usageObserved } : {}
|
|
46
|
+
...options.usageObserved !== void 0 ? { usageObserved: options.usageObserved } : {},
|
|
47
|
+
...options.demand?.get(entry.name) === void 0 ? {} : { demand: options.demand.get(entry.name) },
|
|
48
|
+
...options.liveness?.get(entry.name) === void 0 ? {} : { liveness: options.liveness.get(entry.name) },
|
|
49
|
+
...options.supportChars?.get(entry.name) === void 0 ? {} : { supportChars: options.supportChars.get(entry.name) }
|
|
40
50
|
});
|
|
41
51
|
}
|
|
42
52
|
return snapshots;
|
|
@@ -60,6 +70,8 @@ const PROBE_SIGNALS = [
|
|
|
60
70
|
"dup_heading",
|
|
61
71
|
"overlong_line",
|
|
62
72
|
"pointer_missing",
|
|
73
|
+
"citation_resolution",
|
|
74
|
+
"demand",
|
|
63
75
|
"narrow_name",
|
|
64
76
|
"description_chars",
|
|
65
77
|
"quality_low"
|
|
@@ -110,6 +122,25 @@ function computeProbe(signal, target, snapshots) {
|
|
|
110
122
|
case "dup_heading": return result(signal, duplicateHeadings(body).map((d) => `${d.heading} x${d.count}`), target);
|
|
111
123
|
case "overlong_line": return result(signal, overlongLines(body).map((line) => `line ${line.lineNo}: ${line.chars} chars`), target);
|
|
112
124
|
case "pointer_missing": return result(signal, missingSupportPointers(body, snapshot.supportFiles ?? []).map((path) => `no body reference: ${path}`), target);
|
|
125
|
+
case "citation_resolution": {
|
|
126
|
+
const citations = snapshot.citations;
|
|
127
|
+
if (citations === void 0) return result(signal, ["not-scanned (support files not enumerated)"], target);
|
|
128
|
+
if (citations.truncated) return result(signal, [`scan truncated at ${citations.refs.length} refs`], target);
|
|
129
|
+
const cited = citations.refs.filter((ref) => ref.kind === "citation");
|
|
130
|
+
const dangling = citations.dangling.map((ref) => `${ref.target ?? ref.raw}:${ref.line}`);
|
|
131
|
+
const unverified = citations.unverified.map((ref) => `${ref.target ?? ref.raw}:${ref.line}`);
|
|
132
|
+
return result(signal, dangling.length > 0 ? dangling.map((entry) => `dangling ${entry}`) : [`dangling=0 of ${cited.length} (foreign=${citations.foreign.length}${unverified.length === 0 ? "" : `, unverified=${unverified.length}: ${unverified.slice(0, 3).join(", ")}`})`], target);
|
|
133
|
+
}
|
|
134
|
+
case "demand": {
|
|
135
|
+
if (!snapshot.supportFiles || snapshot.supportFiles.length === 0) return result(signal, ["not-enumerated (support files unknown)"], target);
|
|
136
|
+
if (snapshot.usageObserved !== true) return result(signal, ["window-closed (no observed reads yet)"], target);
|
|
137
|
+
const demand = snapshot.demand ?? {};
|
|
138
|
+
const cold = snapshot.supportFiles.filter((path) => (demand[path] ?? 0) === 0);
|
|
139
|
+
const retire = retirementReport(snapshot, snapshot.supportFiles);
|
|
140
|
+
const coldLines = cold.length === 0 ? [`cold=0 of ${snapshot.supportFiles.length}`] : cold.map((path) => `never read: ${path}`);
|
|
141
|
+
const retireLines = retire.candidates.length === 0 ? [`retire: ${retire.status}`] : retire.candidates.map((candidate) => `retire candidate: ${candidate.path} (idle ${candidate.idleDays}d)`);
|
|
142
|
+
return result(signal, [...coldLines, ...retireLines], target);
|
|
143
|
+
}
|
|
113
144
|
case "narrow_name": return result(signal, narrowNameMatches(snapshot.name).map((shape) => `narrow shape: ${shape}`), target);
|
|
114
145
|
case "description_chars": {
|
|
115
146
|
const desc = snapshot.description;
|
|
@@ -143,6 +174,10 @@ async function buildEnrichment(ctx, library) {
|
|
|
143
174
|
const quality = /* @__PURE__ */ new Map();
|
|
144
175
|
const protectedMap = /* @__PURE__ */ new Map();
|
|
145
176
|
const catalogInvalid = /* @__PURE__ */ new Map();
|
|
177
|
+
const demand = /* @__PURE__ */ new Map();
|
|
178
|
+
const liveness = /* @__PURE__ */ new Map();
|
|
179
|
+
const supportChars = /* @__PURE__ */ new Map();
|
|
180
|
+
const now = /* @__PURE__ */ new Date();
|
|
146
181
|
for (const entry of await library.list()) {
|
|
147
182
|
if (entry.protectionUnknown) protectedMap.set(entry.name, "unknown");
|
|
148
183
|
else if (entry.protectedBy) protectedMap.set(entry.name, entry.protectedBy);
|
|
@@ -162,6 +197,11 @@ async function buildEnrichment(ctx, library) {
|
|
|
162
197
|
if (isPresent(files) && files.value.length > 0) supportFiles.set(entry.name, files.value);
|
|
163
198
|
const record = usageMap?.get(entry.name);
|
|
164
199
|
if (typeof record?.quality_score === "number") quality.set(entry.name, record.quality_score);
|
|
200
|
+
const reads = record?.support_reads;
|
|
201
|
+
if (reads !== void 0 && Object.keys(reads).length > 0) demand.set(entry.name, reads);
|
|
202
|
+
if (record !== void 0) liveness.set(entry.name, { idleDays: idleDays(record, now) });
|
|
203
|
+
const big = await library.supportFileChars(entry.name);
|
|
204
|
+
if (big !== null && Object.keys(big).length > 0) supportChars.set(entry.name, big);
|
|
165
205
|
}
|
|
166
206
|
return {
|
|
167
207
|
descriptions,
|
|
@@ -169,7 +209,10 @@ async function buildEnrichment(ctx, library) {
|
|
|
169
209
|
quality,
|
|
170
210
|
usageObservedValue,
|
|
171
211
|
protected: protectedMap,
|
|
172
|
-
catalogInvalid
|
|
212
|
+
catalogInvalid,
|
|
213
|
+
demand,
|
|
214
|
+
liveness,
|
|
215
|
+
supportChars
|
|
173
216
|
};
|
|
174
217
|
}
|
|
175
218
|
//#endregion
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-
|
|
1
|
+
import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-CjZmhRI9.js";
|
|
2
2
|
import { AUTHORING_DESCRIPTION_BAR, DEFAULT_CURATOR_MODEL, DEFAULT_HEALTH_THRESHOLDS, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_TIMER_DELAY_MS, MIN_STAMP_BODY_CHARS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, computeDriftSignals, findDriftSignal, redactSecrets, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
//#region lib/types/render-facts.js
|
|
@@ -414,6 +414,9 @@ async function runMaintain(runtime, options = {}) {
|
|
|
414
414
|
protected: options.protected ? options.protected() : void 0,
|
|
415
415
|
catalogInvalid: options.catalogInvalid ? options.catalogInvalid() : void 0,
|
|
416
416
|
usageObserved,
|
|
417
|
+
...options.demand === void 0 ? {} : { demand: options.demand() },
|
|
418
|
+
...options.liveness === void 0 ? {} : { liveness: options.liveness() },
|
|
419
|
+
...options.supportChars === void 0 ? {} : { supportChars: options.supportChars() },
|
|
417
420
|
onReadError: (name, error) => {
|
|
418
421
|
readFailures.push(name);
|
|
419
422
|
runtime.logger?.warn(`evolution-maintenance: skipping unreadable skill "${name}": ${error instanceof Error ? error.message : String(error)}`);
|
package/lib/tools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-
|
|
1
|
+
import { i as snapshotFromLibrary, n as PROBE_SIGNALS, r as computeProbe, t as buildEnrichment } from "./enrichment-CjZmhRI9.js";
|
|
2
2
|
import { assertSkillsRootAliasRetired, newSkillLibrary, redactSecrets, resolveRootConfig } from "@lmzhen/dsh-evolution-core";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
4
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
@@ -34,7 +34,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
34
34
|
},
|
|
35
35
|
target: {
|
|
36
36
|
type: "string",
|
|
37
|
-
description: "Skill name for skill-level signals (required for stamp_density/body_size/dup_heading/overlong_line/pointer_missing/narrow_name/description_chars/quality_low)."
|
|
37
|
+
description: "Skill name for skill-level signals (required for stamp_density/body_size/dup_heading/overlong_line/pointer_missing/citation_resolution/demand/narrow_name/description_chars/quality_low)."
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
output: {
|
|
@@ -76,7 +76,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
76
76
|
quality: enrichment.quality,
|
|
77
77
|
protected: enrichment.protected,
|
|
78
78
|
catalogInvalid: enrichment.catalogInvalid,
|
|
79
|
-
usageObserved: enrichment.usageObservedValue
|
|
79
|
+
usageObserved: enrichment.usageObservedValue,
|
|
80
|
+
demand: enrichment.demand
|
|
80
81
|
}));
|
|
81
82
|
const redacted = redactSecrets(probe.detail.join("\n"));
|
|
82
83
|
return {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* when absent they stay `undefined`, which the signal layer reports as
|
|
8
8
|
* `unknown` (never a fabricated verdict).
|
|
9
9
|
*/
|
|
10
|
-
import type
|
|
10
|
+
import { type DriftSkillSnapshot, type SkillLiveness } from '@lmzhen/dsh-evolution-core';
|
|
11
11
|
/** Minimal SkillLibrary surface needed for snapshot assembly. */
|
|
12
12
|
export interface SkillLibraryLike {
|
|
13
13
|
list(): Promise<ReadonlyArray<{
|
|
@@ -34,6 +34,12 @@ export interface SnapshotOptions {
|
|
|
34
34
|
* the snapshot (E-36): without it the probe always answered 'unknown' while
|
|
35
35
|
* the facts block reported the enrichment value. */
|
|
36
36
|
usageObserved?: boolean | undefined;
|
|
37
|
+
/** Per-support-file read counts per skill (design §5.5). */
|
|
38
|
+
demand?: ReadonlyMap<string, Readonly<Record<string, number>>> | undefined;
|
|
39
|
+
/** Idle age per skill (design §5.6). */
|
|
40
|
+
liveness?: ReadonlyMap<string, SkillLiveness> | undefined;
|
|
41
|
+
/** Support-file char counts for files that can exceed the cap (design §16.6). */
|
|
42
|
+
supportChars?: ReadonlyMap<string, Readonly<Record<string, number>>> | undefined;
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
39
45
|
* Assemble drift snapshots from a library reader and optional enrichment.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* enrichment at all).
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
-
import { type SkillLibrary } from '@lmzhen/dsh-evolution-core';
|
|
10
|
+
import { type SkillLibrary, type SkillLiveness } from '@lmzhen/dsh-evolution-core';
|
|
11
11
|
export interface Enrichment {
|
|
12
12
|
descriptions: ReadonlyMap<string, string>;
|
|
13
13
|
supportFiles: ReadonlyMap<string, readonly string[]>;
|
|
@@ -17,6 +17,16 @@ export interface Enrichment {
|
|
|
17
17
|
protected: ReadonlyMap<string, string>;
|
|
18
18
|
/** Skills whose frontmatter the strict-YAML platform catalog cannot load. */
|
|
19
19
|
catalogInvalid: ReadonlyMap<string, boolean>;
|
|
20
|
+
/** Per-support-file read counts (design §5.5) — present only where the usage
|
|
21
|
+
* sidecar recorded at least one read, so absence stays "no evidence". */
|
|
22
|
+
demand: ReadonlyMap<string, Readonly<Record<string, number>>>;
|
|
23
|
+
/** Idle age per skill (design §5.6) — present only for skills with a usage
|
|
24
|
+
* record, so a skill the sidecar never saw stays "no age evidence". */
|
|
25
|
+
liveness: ReadonlyMap<string, SkillLiveness>;
|
|
26
|
+
/** Support-file char counts for files big enough to possibly exceed the cap
|
|
27
|
+
* (design §16.6) — present only where at least one such file exists, so the
|
|
28
|
+
* common measured-and-clean case adds no field. */
|
|
29
|
+
supportChars: ReadonlyMap<string, Readonly<Record<string, number>>>;
|
|
20
30
|
}
|
|
21
31
|
export declare function buildEnrichment(ctx: Context, library: SkillLibrary): Promise<Enrichment>;
|
|
22
32
|
//# sourceMappingURL=enrichment.d.ts.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* text. Deterministic parts stay pure; the subagent call is the only
|
|
5
5
|
* external dependency (injected, fake-able in tests).
|
|
6
6
|
*/
|
|
7
|
-
import { type DriftReport, type DriftSkillSnapshot } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import { type DriftReport, type DriftSkillSnapshot, type SkillLiveness } from '@lmzhen/dsh-evolution-core';
|
|
8
8
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
9
9
|
import { type SkillLibraryLike } from './drift-scan.ts';
|
|
10
10
|
/** I-5 (v37): the platform's `SubagentStartRequest` fields this orchestrator sends.
|
|
@@ -98,6 +98,12 @@ export interface MaintainOptions {
|
|
|
98
98
|
protected?: () => ReadonlyMap<string, string>;
|
|
99
99
|
catalogInvalid?: () => ReadonlyMap<string, boolean>;
|
|
100
100
|
usageObserved?: () => boolean | undefined;
|
|
101
|
+
/** Per-support-file read counts per skill (design §5.5). */
|
|
102
|
+
demand?: () => ReadonlyMap<string, Readonly<Record<string, number>>>;
|
|
103
|
+
/** Idle age per skill (design §5.6). */
|
|
104
|
+
liveness?: () => ReadonlyMap<string, SkillLiveness>;
|
|
105
|
+
/** Support-file char counts for files that can exceed the cap (design §16.6). */
|
|
106
|
+
supportChars?: () => ReadonlyMap<string, Readonly<Record<string, number>>>;
|
|
101
107
|
}
|
|
102
108
|
export interface MaintainOutcome {
|
|
103
109
|
ok: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-maintenance",
|
|
3
3
|
"description": "Deterministic maintenance-scan surface: skill-library snapshot assembly, drift signals and mechanical-facts rendering (design 011) (community build)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.5.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
|