@lmzhen/dsh-evolution-core 0.1.0-rc.36 → 0.1.0-rc.38
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 +95 -19
- package/lib/types/curator.d.ts +2 -0
- package/lib/types/skill-store.d.ts +44 -2
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -377,6 +377,7 @@ function lifecycleCandidate(name, record, config, bundled) {
|
|
|
377
377
|
function computeScopeView(usage, config, protectedNames) {
|
|
378
378
|
const managed = [];
|
|
379
379
|
const watched = [];
|
|
380
|
+
const qualityWarned = [];
|
|
380
381
|
const exempted = [];
|
|
381
382
|
const protectedSet = /* @__PURE__ */ new Set();
|
|
382
383
|
for (const [name, record] of usage) {
|
|
@@ -390,11 +391,13 @@ function computeScopeView(usage, config, protectedNames) {
|
|
|
390
391
|
if (lifecycleCandidate(name, record, config, bundled)) {
|
|
391
392
|
managed.push(name);
|
|
392
393
|
if (record.state === "stale" || record.quality_warn === true) watched.push(name);
|
|
394
|
+
if (record.quality_warn === true) qualityWarned.push(name);
|
|
393
395
|
}
|
|
394
396
|
}
|
|
395
397
|
return {
|
|
396
398
|
managed: managed.sort(),
|
|
397
399
|
watched: watched.sort(),
|
|
400
|
+
qualityWarned: qualityWarned.sort(),
|
|
398
401
|
exempted: exempted.sort(),
|
|
399
402
|
protected: [...protectedSet].sort()
|
|
400
403
|
};
|
|
@@ -1588,6 +1591,8 @@ const DEFAULT_SKILL_LIMITS = {
|
|
|
1588
1591
|
maxSkillContentChars: MAX_SKILL_CONTENT_CHARS,
|
|
1589
1592
|
maxSkillFileBytes: MAX_SKILL_FILE_BYTES
|
|
1590
1593
|
};
|
|
1594
|
+
/** Extra file name carried inside a snapshot's `extras/` directory. */
|
|
1595
|
+
const SNAPSHOT_EXTRA_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
1591
1596
|
function skillsRoot(env = process.env) {
|
|
1592
1597
|
return join(env.DSH_HOME ?? join(homedir(), ".dsh"), "skills");
|
|
1593
1598
|
}
|
|
@@ -2241,7 +2246,13 @@ var SkillLibrary = class {
|
|
|
2241
2246
|
path: target
|
|
2242
2247
|
};
|
|
2243
2248
|
}
|
|
2244
|
-
|
|
2249
|
+
/**
|
|
2250
|
+
* Snapshot the recoverable skills state: active tree, usage/suppression
|
|
2251
|
+
* sidecars, `.archive/` and caller-supplied extras. `extras` are opaque
|
|
2252
|
+
* side files the Snapshot owner cares about (curator state); they are
|
|
2253
|
+
* listed in the manifest and only those names are ever read back.
|
|
2254
|
+
*/
|
|
2255
|
+
async snapshotAll(reason = "pre-mutation", extras = []) {
|
|
2245
2256
|
const dest = join(join(this.root, ".backups"), `skills-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
|
|
2246
2257
|
const names = await listNames(this.root, this.io);
|
|
2247
2258
|
for (const name of names) await this.io.copy(skillDir(this.root, name), join(dest, name));
|
|
@@ -2251,15 +2262,47 @@ var SkillLibrary = class {
|
|
|
2251
2262
|
await this.io.copy(sidecar, join(dest, name));
|
|
2252
2263
|
sidecars.push(name);
|
|
2253
2264
|
}
|
|
2265
|
+
const archiveRoot = join(this.root, ".archive");
|
|
2266
|
+
let hasArchive = false;
|
|
2267
|
+
if (await this.io.exists(archiveRoot)) {
|
|
2268
|
+
await this.io.copy(archiveRoot, join(dest, ".archive"));
|
|
2269
|
+
hasArchive = true;
|
|
2270
|
+
}
|
|
2271
|
+
const extraNames = [];
|
|
2272
|
+
for (const extra of extras) {
|
|
2273
|
+
if (!SNAPSHOT_EXTRA_NAME_RE.test(extra.name)) continue;
|
|
2274
|
+
await this.io.writeText(join(dest, "extras", extra.name), extra.content);
|
|
2275
|
+
extraNames.push(extra.name);
|
|
2276
|
+
}
|
|
2254
2277
|
await this.io.writeText(join(dest, "manifest.json"), JSON.stringify({
|
|
2255
2278
|
reason,
|
|
2256
2279
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2257
2280
|
skills: names,
|
|
2258
|
-
sidecars
|
|
2281
|
+
sidecars,
|
|
2282
|
+
hasArchive,
|
|
2283
|
+
extras: extraNames
|
|
2259
2284
|
}, null, 2));
|
|
2260
2285
|
await this.retainSnapshots(5);
|
|
2261
2286
|
return dest;
|
|
2262
2287
|
}
|
|
2288
|
+
/** Read and normalize a snapshot manifest; null when the file is missing or unparsable. */
|
|
2289
|
+
async readSnapshotManifest(path) {
|
|
2290
|
+
const raw = await this.io.readText(join(path, "manifest.json"));
|
|
2291
|
+
if (raw === null) return null;
|
|
2292
|
+
try {
|
|
2293
|
+
const manifest = JSON.parse(raw);
|
|
2294
|
+
return {
|
|
2295
|
+
reason: typeof manifest.reason === "string" ? manifest.reason : "",
|
|
2296
|
+
createdAt: typeof manifest.createdAt === "string" ? manifest.createdAt : "",
|
|
2297
|
+
skills: Array.isArray(manifest.skills) ? manifest.skills : [],
|
|
2298
|
+
sidecars: Array.isArray(manifest.sidecars) ? manifest.sidecars : [],
|
|
2299
|
+
...typeof manifest.hasArchive === "boolean" ? { hasArchive: manifest.hasArchive } : {},
|
|
2300
|
+
extras: Array.isArray(manifest.extras) ? manifest.extras : []
|
|
2301
|
+
};
|
|
2302
|
+
} catch {
|
|
2303
|
+
return null;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2263
2306
|
/** Keep only the newest N snapshots (Hermes keep=5 parity); oldest folded into .backups history. */
|
|
2264
2307
|
async retainSnapshots(keep) {
|
|
2265
2308
|
const snapshots = await this.listSnapshots();
|
|
@@ -2278,36 +2321,69 @@ var SkillLibrary = class {
|
|
|
2278
2321
|
const out = [];
|
|
2279
2322
|
for (const name of entries.sort().reverse()) {
|
|
2280
2323
|
if (!name.startsWith("skills-")) continue;
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
reason: manifest.reason ?? ""
|
|
2289
|
-
});
|
|
2290
|
-
} catch {}
|
|
2324
|
+
const manifest = await this.readSnapshotManifest(join(backupRoot, name));
|
|
2325
|
+
if (manifest === null) continue;
|
|
2326
|
+
out.push({
|
|
2327
|
+
path: join(backupRoot, name),
|
|
2328
|
+
createdAt: manifest.createdAt,
|
|
2329
|
+
reason: manifest.reason
|
|
2330
|
+
});
|
|
2291
2331
|
}
|
|
2292
2332
|
return out;
|
|
2293
2333
|
}
|
|
2294
|
-
|
|
2334
|
+
/**
|
|
2335
|
+
* Read the extras of a snapshot, restricted to the names declared in the
|
|
2336
|
+
* manifest — an `extras/` directory is never listed directly, so unknown
|
|
2337
|
+
* files cannot leak back as state on the next restore.
|
|
2338
|
+
*/
|
|
2339
|
+
async readSnapshotExtras(path) {
|
|
2340
|
+
const manifest = await this.readSnapshotManifest(path);
|
|
2341
|
+
if (manifest === null) return [];
|
|
2342
|
+
const extras = [];
|
|
2343
|
+
for (const name of manifest.extras) {
|
|
2344
|
+
if (!SNAPSHOT_EXTRA_NAME_RE.test(name)) continue;
|
|
2345
|
+
const content = await this.io.readText(join(path, "extras", name));
|
|
2346
|
+
if (content !== null) extras.push({
|
|
2347
|
+
name,
|
|
2348
|
+
content
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
return extras;
|
|
2352
|
+
}
|
|
2353
|
+
/**
|
|
2354
|
+
* Manifest-driven restore of the latest snapshot: active tree, sidecars,
|
|
2355
|
+
* `.archive/` and (for full-state snapshots) the extras read back by the
|
|
2356
|
+
* caller. `extras` are additionally written into the pre-rollback safety
|
|
2357
|
+
* snapshot so the rollback itself is undoable with the same state.
|
|
2358
|
+
*/
|
|
2359
|
+
async restoreLatestSnapshot(extras = []) {
|
|
2295
2360
|
const latest = (await this.listSnapshots())[0];
|
|
2296
2361
|
if (!latest) return {
|
|
2297
2362
|
ok: false,
|
|
2298
2363
|
message: "No skill snapshot available."
|
|
2299
2364
|
};
|
|
2300
|
-
await this.snapshotAll("pre-rollback");
|
|
2365
|
+
await this.snapshotAll("pre-rollback", extras);
|
|
2301
2366
|
for (const name of await listNames(this.root, this.io)) await this.io.remove(skillDir(this.root, name));
|
|
2302
|
-
const
|
|
2303
|
-
for (const entry of
|
|
2304
|
-
if (entry === "manifest.json") continue;
|
|
2367
|
+
const manifest = await this.readSnapshotManifest(latest.path);
|
|
2368
|
+
if (manifest === null) for (const entry of await this.io.list(latest.path)) {
|
|
2369
|
+
if (entry === "manifest.json" || entry === "extras") continue;
|
|
2305
2370
|
await this.io.copy(join(latest.path, entry), join(this.root, entry));
|
|
2306
2371
|
}
|
|
2372
|
+
else {
|
|
2373
|
+
for (const name of manifest.skills) await this.io.copy(join(latest.path, name), join(this.root, name));
|
|
2374
|
+
for (const sidecar of manifest.sidecars) await this.io.copy(join(latest.path, sidecar), join(this.root, sidecar));
|
|
2375
|
+
const archiveRoot = join(this.root, ".archive");
|
|
2376
|
+
if (manifest.hasArchive === true) {
|
|
2377
|
+
await this.io.remove(archiveRoot);
|
|
2378
|
+
await this.io.copy(join(latest.path, ".archive"), archiveRoot);
|
|
2379
|
+
} else if (manifest.hasArchive === false) await this.io.remove(archiveRoot);
|
|
2380
|
+
}
|
|
2381
|
+
const snapshotExtras = await this.readSnapshotExtras(latest.path);
|
|
2307
2382
|
return {
|
|
2308
2383
|
ok: true,
|
|
2309
2384
|
message: `Restored skill tree from ${latest.path}`,
|
|
2310
|
-
path: latest.path
|
|
2385
|
+
path: latest.path,
|
|
2386
|
+
...snapshotExtras.length === 0 ? {} : { extras: snapshotExtras }
|
|
2311
2387
|
};
|
|
2312
2388
|
}
|
|
2313
2389
|
};
|
|
@@ -2377,4 +2453,4 @@ var JsonState = class JsonState {
|
|
|
2377
2453
|
}
|
|
2378
2454
|
};
|
|
2379
2455
|
//#endregion
|
|
2380
|
-
export { COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, 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, 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, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, advanceReview, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpUse, bumpView, computeDedupGroups, computeLifecycleTransitions, computeQualityScores, computeScopeView, contentHash, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutationsFile, nodeEvolutionIo, observeEvent, parseCuratorNominations, parseFrontmatter, recordMutation, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, usageFile, validateFrontmatter, verifyPromptBundle };
|
|
2456
|
+
export { COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, 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, 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, emptyRecord, evaluateThreat, evolutionHome, evolutionIoAdapter, foldTurn, getRecord, latestActivityAt, lifecycleCandidate, loadMutations, loadSuppressedNames, loadUsage, markAgentCreated, memoryRoot, mutationsFile, nodeEvolutionIo, observeEvent, parseCuratorNominations, parseFrontmatter, recordMutation, reviewPrompt, saveSuppressedNames, saveUsage, scanContentThreats, scanMemoryThreats, scanThreats, skillsRoot, suppressedFile, usageFile, validateFrontmatter, verifyPromptBundle };
|
package/lib/types/curator.d.ts
CHANGED
|
@@ -101,6 +101,8 @@ export interface ScopeView {
|
|
|
101
101
|
managed: string[];
|
|
102
102
|
/** Managed skills already flagged stale or quality-warned — the ones to watch. */
|
|
103
103
|
watched: string[];
|
|
104
|
+
/** Managed skills flagged low quality (subset of `watched`) — consolidation candidates. */
|
|
105
|
+
qualityWarned: string[];
|
|
104
106
|
/** Explicitly exempted by excludeSkillNames / referencedSkillNames. */
|
|
105
107
|
exempted: string[];
|
|
106
108
|
/** Carrying a protection marker (pinned / bundled / hub-installed). */
|
|
@@ -28,6 +28,26 @@ export interface SkillActionResult {
|
|
|
28
28
|
message: string;
|
|
29
29
|
path?: string;
|
|
30
30
|
}
|
|
31
|
+
/** Extra file name carried inside a snapshot's `extras/` directory. */
|
|
32
|
+
export declare const SNAPSHOT_EXTRA_NAME_RE: RegExp;
|
|
33
|
+
/** An opaque side file stored under a snapshot's `extras/` (curator state etc.). */
|
|
34
|
+
export interface SnapshotExtra {
|
|
35
|
+
name: string;
|
|
36
|
+
content: string;
|
|
37
|
+
}
|
|
38
|
+
/** Normalized manifest of a skills snapshot. */
|
|
39
|
+
export interface SnapshotManifest {
|
|
40
|
+
reason: string;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
/** Active skill names at snapshot time. */
|
|
43
|
+
skills: string[];
|
|
44
|
+
/** Co-copied sidecar file names (usage/suppression). */
|
|
45
|
+
sidecars: string[];
|
|
46
|
+
/** Whether `.archive/` was co-copied; absent on legacy manifests (do not touch archive on restore). */
|
|
47
|
+
hasArchive?: boolean;
|
|
48
|
+
/** Extras declared under `extras/`; only these names are ever read back. */
|
|
49
|
+
extras: string[];
|
|
50
|
+
}
|
|
31
51
|
/** Who is writing: a foreground user-directed tool call, or the autonomous review/curator pipeline. */
|
|
32
52
|
export type WriteOrigin = 'foreground' | 'subagent' | 'background_review';
|
|
33
53
|
/** Options for `SkillLibrary.archive`. The absorbed-into name and the archival reason are distinct fields. */
|
|
@@ -99,7 +119,15 @@ export declare class SkillLibrary {
|
|
|
99
119
|
restoreFromArchive(name: string): Promise<SkillActionResult>;
|
|
100
120
|
writeSupportFile(name: string, filePath: string, content: string, origin?: WriteOrigin): Promise<SkillActionResult>;
|
|
101
121
|
removeSupportFile(name: string, filePath: string, origin?: WriteOrigin): Promise<SkillActionResult>;
|
|
102
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Snapshot the recoverable skills state: active tree, usage/suppression
|
|
124
|
+
* sidecars, `.archive/` and caller-supplied extras. `extras` are opaque
|
|
125
|
+
* side files the Snapshot owner cares about (curator state); they are
|
|
126
|
+
* listed in the manifest and only those names are ever read back.
|
|
127
|
+
*/
|
|
128
|
+
snapshotAll(reason?: string, extras?: SnapshotExtra[]): Promise<string>;
|
|
129
|
+
/** Read and normalize a snapshot manifest; null when the file is missing or unparsable. */
|
|
130
|
+
readSnapshotManifest(path: string): Promise<SnapshotManifest | null>;
|
|
103
131
|
/** Keep only the newest N snapshots (Hermes keep=5 parity); oldest folded into .backups history. */
|
|
104
132
|
private retainSnapshots;
|
|
105
133
|
listSnapshots(): Promise<Array<{
|
|
@@ -107,6 +135,20 @@ export declare class SkillLibrary {
|
|
|
107
135
|
createdAt: string;
|
|
108
136
|
reason: string;
|
|
109
137
|
}>>;
|
|
110
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Read the extras of a snapshot, restricted to the names declared in the
|
|
140
|
+
* manifest — an `extras/` directory is never listed directly, so unknown
|
|
141
|
+
* files cannot leak back as state on the next restore.
|
|
142
|
+
*/
|
|
143
|
+
readSnapshotExtras(path: string): Promise<SnapshotExtra[]>;
|
|
144
|
+
/**
|
|
145
|
+
* Manifest-driven restore of the latest snapshot: active tree, sidecars,
|
|
146
|
+
* `.archive/` and (for full-state snapshots) the extras read back by the
|
|
147
|
+
* caller. `extras` are additionally written into the pre-rollback safety
|
|
148
|
+
* snapshot so the rollback itself is undoable with the same state.
|
|
149
|
+
*/
|
|
150
|
+
restoreLatestSnapshot(extras?: SnapshotExtra[]): Promise<SkillActionResult & {
|
|
151
|
+
extras?: SnapshotExtra[];
|
|
152
|
+
}>;
|
|
111
153
|
}
|
|
112
154
|
//# sourceMappingURL=skill-store.d.ts.map
|
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.
|
|
4
|
+
"version": "0.1.0-rc.38",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|