dsh-continual-evolve 0.10.6 → 0.12.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.
Files changed (126) hide show
  1. package/README.md +35 -122
  2. package/README.zh.md +26 -97
  3. package/cordis.patch.yml +2 -2
  4. package/lib/index.d.ts +44 -241
  5. package/lib/index.js +30 -253
  6. package/lib/memory-guide.d.ts +13 -14
  7. package/lib/memory-guide.js +32 -21
  8. package/lib/memory-section.d.ts +77 -0
  9. package/lib/memory-section.js +189 -0
  10. package/package.json +2 -17
  11. package/lib/apply.d.ts +0 -22
  12. package/lib/apply.js +0 -157
  13. package/lib/approval.d.ts +0 -72
  14. package/lib/approval.js +0 -127
  15. package/lib/auto.d.ts +0 -236
  16. package/lib/auto.js +0 -773
  17. package/lib/autocase.d.ts +0 -38
  18. package/lib/autocase.js +0 -76
  19. package/lib/benchmark-command.d.ts +0 -9
  20. package/lib/benchmark-command.js +0 -357
  21. package/lib/benchmark.d.ts +0 -151
  22. package/lib/benchmark.js +0 -250
  23. package/lib/command.d.ts +0 -49
  24. package/lib/command.js +0 -568
  25. package/lib/consolidate.d.ts +0 -90
  26. package/lib/consolidate.js +0 -180
  27. package/lib/copy.d.ts +0 -74
  28. package/lib/copy.js +0 -160
  29. package/lib/declines.d.ts +0 -101
  30. package/lib/declines.js +0 -175
  31. package/lib/evaluate.d.ts +0 -74
  32. package/lib/evaluate.js +0 -271
  33. package/lib/evolve-event.d.ts +0 -40
  34. package/lib/evolve-event.js +0 -59
  35. package/lib/failures.d.ts +0 -42
  36. package/lib/failures.js +0 -171
  37. package/lib/fate.d.ts +0 -130
  38. package/lib/fate.js +0 -350
  39. package/lib/goal-command.d.ts +0 -7
  40. package/lib/goal-command.js +0 -37
  41. package/lib/goal.d.ts +0 -72
  42. package/lib/goal.js +0 -72
  43. package/lib/inject.d.ts +0 -196
  44. package/lib/inject.js +0 -412
  45. package/lib/llm-text.d.ts +0 -104
  46. package/lib/llm-text.js +0 -140
  47. package/lib/logfile.d.ts +0 -71
  48. package/lib/logfile.js +0 -159
  49. package/lib/memory-agent.d.ts +0 -129
  50. package/lib/memory-agent.js +0 -568
  51. package/lib/memory-benchmark.d.ts +0 -76
  52. package/lib/memory-benchmark.js +0 -170
  53. package/lib/memory-index.d.ts +0 -114
  54. package/lib/memory-index.js +0 -197
  55. package/lib/message-source.d.ts +0 -23
  56. package/lib/message-source.js +0 -12
  57. package/lib/mount-command.d.ts +0 -10
  58. package/lib/mount-command.js +0 -48
  59. package/lib/mount.d.ts +0 -42
  60. package/lib/mount.js +0 -212
  61. package/lib/notify.d.ts +0 -59
  62. package/lib/notify.js +0 -97
  63. package/lib/plan.d.ts +0 -16
  64. package/lib/plan.js +0 -126
  65. package/lib/planner.d.ts +0 -61
  66. package/lib/planner.js +0 -144
  67. package/lib/pool.d.ts +0 -7
  68. package/lib/pool.js +0 -25
  69. package/lib/prefix-cache.d.ts +0 -78
  70. package/lib/prefix-cache.js +0 -168
  71. package/lib/project.d.ts +0 -28
  72. package/lib/project.js +0 -66
  73. package/lib/projection.d.ts +0 -22
  74. package/lib/projection.js +0 -112
  75. package/lib/promotion.d.ts +0 -94
  76. package/lib/promotion.js +0 -171
  77. package/lib/recall.d.ts +0 -85
  78. package/lib/recall.js +0 -142
  79. package/lib/record-language.d.ts +0 -85
  80. package/lib/record-language.js +0 -152
  81. package/lib/render.d.ts +0 -13
  82. package/lib/render.js +0 -80
  83. package/lib/review-scheduler.d.ts +0 -38
  84. package/lib/review-scheduler.js +0 -191
  85. package/lib/review.d.ts +0 -60
  86. package/lib/review.js +0 -145
  87. package/lib/rollback.d.ts +0 -9
  88. package/lib/rollback.js +0 -68
  89. package/lib/rubric.d.ts +0 -29
  90. package/lib/rubric.js +0 -119
  91. package/lib/runtime.d.ts +0 -24
  92. package/lib/runtime.js +0 -87
  93. package/lib/score.d.ts +0 -64
  94. package/lib/score.js +0 -191
  95. package/lib/search.d.ts +0 -83
  96. package/lib/search.js +0 -136
  97. package/lib/service.d.ts +0 -49
  98. package/lib/service.js +0 -180
  99. package/lib/skill-render.d.ts +0 -23
  100. package/lib/skill-render.js +0 -69
  101. package/lib/skill.d.ts +0 -15
  102. package/lib/skill.js +0 -80
  103. package/lib/skillquality.d.ts +0 -85
  104. package/lib/skillquality.js +0 -320
  105. package/lib/source.d.ts +0 -30
  106. package/lib/source.js +0 -43
  107. package/lib/state.d.ts +0 -26
  108. package/lib/state.js +0 -151
  109. package/lib/store.d.ts +0 -62
  110. package/lib/store.js +0 -164
  111. package/lib/token-usage.d.ts +0 -68
  112. package/lib/token-usage.js +0 -211
  113. package/lib/tool.d.ts +0 -34
  114. package/lib/tool.js +0 -320
  115. package/lib/turn-snapshot.d.ts +0 -72
  116. package/lib/turn-snapshot.js +0 -238
  117. package/lib/types.d.ts +0 -208
  118. package/lib/types.js +0 -127
  119. package/lib/usage.d.ts +0 -36
  120. package/lib/usage.js +0 -98
  121. package/lib/validate.d.ts +0 -28
  122. package/lib/validate.js +0 -181
  123. package/lib/wrapup-command.d.ts +0 -10
  124. package/lib/wrapup-command.js +0 -243
  125. package/lib/wrapup.d.ts +0 -260
  126. package/lib/wrapup.js +0 -539
@@ -1,69 +0,0 @@
1
- /** Convert a harness entry id (underscore slug) to a kebab-case skill name. */
2
- export function skillNameOf(id) {
3
- return id.toLowerCase().replace(/_/g, "-");
4
- }
5
- /**
6
- * First content line usable as a routing hint: non-empty, not a Markdown
7
- * heading, not a list marker, not frontmatter. Undefined when the body is
8
- * effectively empty.
9
- */
10
- function routingHint(content) {
11
- for (const rawLine of content.split("\n")) {
12
- const line = rawLine.trim();
13
- if (line.length === 0)
14
- continue;
15
- if (line.startsWith("#") || line.startsWith("---") || line.startsWith("-") || line.startsWith("*"))
16
- continue;
17
- return oneLine(line);
18
- }
19
- return undefined;
20
- }
21
- /** Max rendered frontmatter description length (loaders truncate anyway). */
22
- const MAX_DESCRIPTION_LENGTH = 240;
23
- /**
24
- * Render a harness skill entry as a discoverable SKILL.md document.
25
- *
26
- * 2026-08-22: the frontmatter description now carries a ROUTING HINT —
27
- * title plus the first meaningful content line — instead of the bare title.
28
- * The skill catalog matches on description; a title-only description gave
29
- * loaders nothing to route on (observed: materialized skills were 7-line
30
- * stubs with a one-line description and no use-when signal).
31
- */
32
- export function renderSkillMarkdown(entry) {
33
- const hint = routingHint(entry.content);
34
- const base = oneLine(entry.title);
35
- let description;
36
- if (base.length === 0) {
37
- description = (hint ?? "").slice(0, MAX_DESCRIPTION_LENGTH);
38
- }
39
- else if (hint !== undefined && !base.toLowerCase().includes(hint.toLowerCase())) {
40
- description = `${base} — use when: ${hint}`.slice(0, MAX_DESCRIPTION_LENGTH);
41
- }
42
- else {
43
- description = base.slice(0, MAX_DESCRIPTION_LENGTH);
44
- }
45
- const lines = [
46
- "---",
47
- `name: ${skillNameOf(entry.id)}`,
48
- `description: ${description}`,
49
- "---",
50
- "",
51
- entry.content.trim(),
52
- ];
53
- const reference = entry.reference;
54
- if (reference && typeof reference === "object" && Object.keys(reference).length > 0) {
55
- lines.push("", "## Invocation");
56
- for (const [key, value] of Object.entries(reference)) {
57
- lines.push(`- ${key}: ${JSON.stringify(value)}`);
58
- }
59
- }
60
- const args = entry.arguments;
61
- if (args && typeof args === "object" && Object.keys(args).length > 0) {
62
- lines.push("", "## Arguments", "```json", JSON.stringify(entry.arguments, null, 2), "```");
63
- }
64
- return `${lines.join("\n").trimEnd()}\n`;
65
- }
66
- function oneLine(text) {
67
- return text.replace(/\s+/g, " ").trim();
68
- }
69
- //# sourceMappingURL=skill-render.js.map
package/lib/skill.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { RefinementResult } from "./types.js";
2
- export { renderSkillMarkdown, skillNameOf } from "./skill-render.js";
3
- /** Resolve and defend the skill directory for an entry id. */
4
- export declare function skillDir(skillsRoot: string, id: string): string;
5
- /**
6
- * Apply the skill-kind edits of an applied refinement to the skills root.
7
- * Returns materialization warnings (rendered-SKILL.md mechanical problems
8
- * and dangling resource references) — the file is still written, but the
9
- * caller should surface them: a rendered file that fails the platform's
10
- * frontmatter rules would be IGNORED by the skill loader, and a body
11
- * referencing resources the entry does not ship would load with broken
12
- * links.
13
- */
14
- export declare function syncSkillsFromResult(skillsRoot: string, result: RefinementResult): string[];
15
- //# sourceMappingURL=skill.d.ts.map
package/lib/skill.js DELETED
@@ -1,80 +0,0 @@
1
- /**
2
- * Skill materializer: syncs skill-kind harness entries to the DSH skills
3
- * filesystem (`$DSH_HOME/skills/<kebab-name>/SKILL.md`) so the `skill` tool
4
- * and catalog can discover and load them. Writes are atomic (tmp + rename)
5
- * so the filesystem watcher never sees a partial file.
6
- *
7
- * Skill names must be kebab-case (the harness store ids are underscore slugs;
8
- * the materialized name converts `_` → `-`).
9
- */
10
- import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
11
- import { join, resolve, sep } from "node:path";
12
- import { renderSkillMarkdown, skillNameOf } from "./skill-render.js";
13
- import { skillResourceRefs, validateRenderedSkill } from "./skillquality.js";
14
- export { renderSkillMarkdown, skillNameOf } from "./skill-render.js";
15
- /** Resolve and defend the skill directory for an entry id. */
16
- export function skillDir(skillsRoot, id) {
17
- const root = resolve(skillsRoot);
18
- const dir = resolve(join(root, skillNameOf(id)));
19
- if (dir !== root && !dir.startsWith(`${root}/`) && !dir.startsWith(`${root}${sep}`)) {
20
- throw new Error(`skill path escapes skills root: ${dir}`);
21
- }
22
- return dir;
23
- }
24
- /**
25
- * Apply the skill-kind edits of an applied refinement to the skills root.
26
- * Returns materialization warnings (rendered-SKILL.md mechanical problems
27
- * and dangling resource references) — the file is still written, but the
28
- * caller should surface them: a rendered file that fails the platform's
29
- * frontmatter rules would be IGNORED by the skill loader, and a body
30
- * referencing resources the entry does not ship would load with broken
31
- * links.
32
- */
33
- export function syncSkillsFromResult(skillsRoot, result) {
34
- const warnings = [];
35
- for (const edit of result.appliedEdits) {
36
- if (edit.kind !== "skill" || !edit.applied)
37
- continue;
38
- if (edit.action === "delete" || !edit.after) {
39
- removeSkill(skillsRoot, edit.id);
40
- continue;
41
- }
42
- warnings.push(...writeSkill(skillsRoot, edit.after));
43
- }
44
- return warnings;
45
- }
46
- /** Write one skill entry as a SKILL.md; returns materialization warnings. */
47
- function writeSkill(skillsRoot, entry) {
48
- const dir = skillDir(skillsRoot, entry.id);
49
- mkdirSync(dir, { recursive: true });
50
- const temp = join(dir, `SKILL.md.${process.pid}.tmp`);
51
- writeFileSync(temp, renderSkillMarkdown(entry), "utf8");
52
- renameSync(temp, join(dir, "SKILL.md"));
53
- return materializationWarnings(dir, entry);
54
- }
55
- /** Post-write checks on the exact file that landed on disk. */
56
- function materializationWarnings(dir, entry) {
57
- const warnings = [];
58
- for (const problem of validateRenderedSkill(entry)) {
59
- warnings.push(`skill ${entry.id}: rendered SKILL.md would be ignored by the platform: ${problem}`);
60
- }
61
- const root = resolve(dir);
62
- for (const ref of skillResourceRefs(entry.content)) {
63
- const target = resolve(root, ref);
64
- if (target !== root && !target.startsWith(`${root}${sep}`)) {
65
- warnings.push(`skill ${entry.id}: body resource reference escapes the skill directory: ${ref}`);
66
- continue;
67
- }
68
- if (!existsSync(target)) {
69
- warnings.push(`skill ${entry.id}: body references missing resource ${ref} (expected at ${target})`);
70
- }
71
- }
72
- return warnings;
73
- }
74
- function removeSkill(skillsRoot, id) {
75
- const dir = skillDir(skillsRoot, id);
76
- if (existsSync(dir)) {
77
- rmSync(dir, { recursive: true, force: true });
78
- }
79
- }
80
- //# sourceMappingURL=skill.js.map
@@ -1,85 +0,0 @@
1
- import type { HarnessEntry } from "./types.js";
2
- /** Relative location of the skill-creator template facts. */
3
- export declare const SKILL_CREATOR_TEMPLATE_REL: string;
4
- /**
5
- * Read the skill-creator template facts
6
- * (`<skillsRoot>/skill-creator/references/template.md`; facts distilled
7
- * from the official deepseek-harness skills). Returns null when the skills
8
- * are not installed — callers fall back to the builtin distilled guide.
9
- * Reading is a runtime reference, never a copy: template updates in the
10
- * skill are picked up automatically.
11
- */
12
- export declare function readSkillCreatorTemplate(skillsRoot: string): string | null;
13
- /**
14
- * Builtin distilled skill-quality guide (fallback when the skill-creator
15
- * template is not installed). Condenses the template facts — frontmatter
16
- * schema, the 7 structural features, paragraph skeleton, and the
17
- * no-duplication / real-trigger rules — so a planner still authors skills
18
- * to the standard on installs without the skill-creator / skill-audit
19
- * skills.
20
- */
21
- export declare const BUILTIN_SKILL_QUALITY_GUIDE = "DSH skill quality standard (distilled by the author from the official deepseek-harness 11 skills; the full facts live in <skillsRoot>/skill-creator/references/template.md when installed):\n\nFrontmatter schema (platform-enforced; violations make the platform IGNORE the whole file):\n- name: required, kebab-case only (^[a-z0-9]+(?:-[a-z0-9]+)*$)\n- description: required, non-empty; write \"use when / do not use when\" routing so the model can select it correctly\n- invocation booleans accept true/false/yes/no/on/off/1/0; legacy camelCase keys (disableModelInvocation / modelInvocable / userInvocable) are rejected\n- whenToUse (optional): non-empty string; metadata (optional): object\n\nThe 7 structural features of the official deepseek-harness skills:\n1. Frontmatter is routing metadata, not a summary (description = when to use / when not to use)\n2. Opens with a boundary declaration (guidance, not a script; mechanical flow skills may omit the disclaimer)\n3. Prerequisites + exclusions: explicit required input, stop when missing (report the required input and stop), excluded scenarios\n4. Layered information: Sources of truth (link only, do not re-summarize) -> numbered blocking requirements -> manual checks -> verification commands -> report format; all executable, no slogans\n5. Skill interlinks: reference a single source of truth instead of duplicating it\n6. Verifiable completion criteria: explicit verification commands and report format\n7. Real use + iteration: a real trigger scenario must exist; calibration conclusions distill into references/\n\nParagraph skeleton (writing order): frontmatter -> H1 + boundary declaration -> Sources of truth -> numbered requirements / workflow (full commands) -> exclusions / stop conditions -> verification and report.\n\nCreation rules: only create a skill for a REAL trigger scenario (who, in what real task, what signal) grounded in the trajectory \u2014 never invent one to pad the store; do not duplicate the official 11 skills or existing entries; skill bodies should be a SKILL.md document (this is what materializes under <skillsRoot>/<kebab-name>/SKILL.md).";
22
- export interface SkillQualityGuide {
23
- /** Where the guide text came from: the on-disk template or the builtin guide. */
24
- source: "template" | "builtin";
25
- text: string;
26
- }
27
- /**
28
- * The quality guide handed to the planner: the skill-creator template facts
29
- * when the skills are installed, otherwise the builtin distilled guide.
30
- * Never throws — a missing/unreadable template degrades to the builtin.
31
- */
32
- export declare function skillQualityGuide(skillsRoot: string | undefined): SkillQualityGuide;
33
- /** Split frontmatter out of a raw SKILL.md. Returns { yaml, body } or null when delimiters are missing. */
34
- export declare function splitFrontmatter(raw: string): {
35
- yaml: string;
36
- body: string;
37
- } | null;
38
- /**
39
- * Mechanical frontmatter validation of a rendered SKILL.md, mirroring
40
- * `skill-creator/scripts/validate-frontmatter.mjs` (and the platform's
41
- * skill-filesystem rules): delimiter structure, name kebab-case, non-empty
42
- * description, invocation-boolean spellings, legacy camelCase key rejection,
43
- * whenToUse/metadata types. Returns human-readable problems; an empty array
44
- * means the file would load.
45
- */
46
- export declare function validateRenderedSkillMarkdown(markdown: string): string[];
47
- /**
48
- * Mechanical validation of a skill entry's raw `content` (the SKILL.md body
49
- * that materializes under the generated frontmatter). Code-enforced at
50
- * apply time so a bad entry never reaches the store:
51
- * - empty content is rejected;
52
- * - content must not open with a `---` block: the materializer generates
53
- * its own frontmatter, and a second frontmatter in the body would be
54
- * parsed instead of the generated one (the platform reads the FIRST
55
- * closing `---`), so the file could be ignored or routed wrongly;
56
- * - resource references (`references/…`, `scripts/…`) must be skill-local
57
- * relative paths — an embedded `/../` after the category escapes the skill
58
- * directory and is rejected. A leading `../` instead addresses a sibling
59
- * skill (cross-skill interlink, e.g. `../skill-creator/…`) and is allowed:
60
- * it is not dangling-checked since cross-skill targets live outside this
61
- * entry's shipped files (see skillResourceRefs).
62
- * Returns human-readable problems; an empty array means the content is
63
- * mechanically acceptable.
64
- */
65
- export declare function validateSkillEntryContent(content: string): string[];
66
- /**
67
- * Validate the FULL rendered SKILL.md of an entry (generated frontmatter +
68
- * body) — the exact bytes that materialize on disk. Used as the final
69
- * code-enforced line after materialization; problems here mean the platform
70
- * would refuse to load the file.
71
- */
72
- export declare function validateRenderedSkill(entry: HarnessEntry): string[];
73
- /**
74
- * Resource references (`references/…`, `scripts/…`) found in a skill body —
75
- * the same scanning policy as validate-frontmatter.mjs: markdown link
76
- * targets starting with the category, plus backticked/prose paths carrying
77
- * a filename extension. Used after materialization to warn about dangling
78
- * references (a body referencing a resource the entry never ships).
79
- * Leading-`../` cross-skill interlinks are skipped, not warned: they resolve
80
- * against sibling skill directories, never against shipped files.
81
- */
82
- export declare function skillResourceRefs(content: string): string[];
83
- /** Kebab-case name under which the entry materializes (exported for diagnostics). */
84
- export { skillNameOf } from "./skill-render.js";
85
- //# sourceMappingURL=skillquality.d.ts.map
@@ -1,320 +0,0 @@
1
- /**
2
- * Skill-quality integration: makes the DSH skill quality standard — carried
3
- * by the author-distilled skills skill-creator / skill-audit (distilled
4
- * from the official deepseek-harness 11 skills, facts verified against
5
- * deepseek-harness 47f9438) — usable INSIDE the self-evolution loop.
6
- *
7
- * The planner and review gate are raw `ctx.llm` calls — they do not live in
8
- * an agent session, so they cannot load skills through the `skill` tool.
9
- * The skill-creator / skill-audit skills stay the single source of truth on
10
- * disk; this module only:
11
- *
12
- * 1. reads the template facts at runtime
13
- * (`<skillsRoot>/skill-creator/references/template.md`, 85 lines) and
14
- * hands them to the planner as a `<skill_quality_standard>` block —
15
- * the on-disk template wins, a built-in distilled guide is the fallback
16
- * for installs without these skills;
17
- * 2. code-enforces the mechanical frontmatter rules of
18
- * `skill-creator/scripts/validate-frontmatter.mjs` (the platform would
19
- * IGNORE a file that fails them), so a skill entry can never materialize
20
- * a SKILL.md the platform refuses to load.
21
- */
22
- import { existsSync, readFileSync } from "node:fs";
23
- import { join } from "node:path";
24
- import { renderSkillMarkdown } from "./skill-render.js";
25
- /** Skill-name regex the platform enforces (skill-filesystem). */
26
- const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
27
- const TRUE_WORDS = new Set(["true", "yes", "on", "1"]);
28
- const FALSE_WORDS = new Set(["false", "no", "off", "0"]);
29
- const LEGACY_KEYS = ["disableModelInvocation", "modelInvocable", "userInvocable"];
30
- const CANONICAL_KEYS = {
31
- disableModelInvocation: "disable-model-invocation",
32
- modelInvocable: "disable-model-invocation",
33
- userInvocable: "user-invocable",
34
- };
35
- /** Relative location of the skill-creator template facts. */
36
- export const SKILL_CREATOR_TEMPLATE_REL = join("skill-creator", "references", "template.md");
37
- /**
38
- * Read the skill-creator template facts
39
- * (`<skillsRoot>/skill-creator/references/template.md`; facts distilled
40
- * from the official deepseek-harness skills). Returns null when the skills
41
- * are not installed — callers fall back to the builtin distilled guide.
42
- * Reading is a runtime reference, never a copy: template updates in the
43
- * skill are picked up automatically.
44
- */
45
- export function readSkillCreatorTemplate(skillsRoot) {
46
- const path = join(skillsRoot, SKILL_CREATOR_TEMPLATE_REL);
47
- try {
48
- if (!existsSync(path))
49
- return null;
50
- const text = readFileSync(path, "utf8");
51
- return text.trim().length > 0 ? text : null;
52
- }
53
- catch {
54
- return null;
55
- }
56
- }
57
- /**
58
- * Builtin distilled skill-quality guide (fallback when the skill-creator
59
- * template is not installed). Condenses the template facts — frontmatter
60
- * schema, the 7 structural features, paragraph skeleton, and the
61
- * no-duplication / real-trigger rules — so a planner still authors skills
62
- * to the standard on installs without the skill-creator / skill-audit
63
- * skills.
64
- */
65
- export const BUILTIN_SKILL_QUALITY_GUIDE = `DSH skill quality standard (distilled by the author from the official deepseek-harness 11 skills; the full facts live in <skillsRoot>/skill-creator/references/template.md when installed):
66
-
67
- Frontmatter schema (platform-enforced; violations make the platform IGNORE the whole file):
68
- - name: required, kebab-case only (^[a-z0-9]+(?:-[a-z0-9]+)*$)
69
- - description: required, non-empty; write "use when / do not use when" routing so the model can select it correctly
70
- - invocation booleans accept true/false/yes/no/on/off/1/0; legacy camelCase keys (disableModelInvocation / modelInvocable / userInvocable) are rejected
71
- - whenToUse (optional): non-empty string; metadata (optional): object
72
-
73
- The 7 structural features of the official deepseek-harness skills:
74
- 1. Frontmatter is routing metadata, not a summary (description = when to use / when not to use)
75
- 2. Opens with a boundary declaration (guidance, not a script; mechanical flow skills may omit the disclaimer)
76
- 3. Prerequisites + exclusions: explicit required input, stop when missing (report the required input and stop), excluded scenarios
77
- 4. Layered information: Sources of truth (link only, do not re-summarize) -> numbered blocking requirements -> manual checks -> verification commands -> report format; all executable, no slogans
78
- 5. Skill interlinks: reference a single source of truth instead of duplicating it
79
- 6. Verifiable completion criteria: explicit verification commands and report format
80
- 7. Real use + iteration: a real trigger scenario must exist; calibration conclusions distill into references/
81
-
82
- Paragraph skeleton (writing order): frontmatter -> H1 + boundary declaration -> Sources of truth -> numbered requirements / workflow (full commands) -> exclusions / stop conditions -> verification and report.
83
-
84
- Creation rules: only create a skill for a REAL trigger scenario (who, in what real task, what signal) grounded in the trajectory — never invent one to pad the store; do not duplicate the official 11 skills or existing entries; skill bodies should be a SKILL.md document (this is what materializes under <skillsRoot>/<kebab-name>/SKILL.md).`;
85
- /**
86
- * The quality guide handed to the planner: the skill-creator template facts
87
- * when the skills are installed, otherwise the builtin distilled guide.
88
- * Never throws — a missing/unreadable template degrades to the builtin.
89
- */
90
- export function skillQualityGuide(skillsRoot) {
91
- if (skillsRoot) {
92
- const template = readSkillCreatorTemplate(skillsRoot);
93
- if (template !== null) {
94
- return {
95
- source: "template",
96
- text: `The skill-creator template facts (distilled from the official deepseek-harness 11 skills, verified against deepseek-harness 47f9438; single source of truth, read from <skillsRoot>/skill-creator/references/template.md):\n\n${template}`,
97
- };
98
- }
99
- }
100
- return { source: "builtin", text: BUILTIN_SKILL_QUALITY_GUIDE };
101
- }
102
- /** Split frontmatter out of a raw SKILL.md. Returns { yaml, body } or null when delimiters are missing. */
103
- export function splitFrontmatter(raw) {
104
- const lines = raw.split(/\r?\n/);
105
- if (lines[0] !== "---")
106
- return null;
107
- const close = lines.indexOf("---", 1);
108
- if (close < 0)
109
- return null;
110
- return { yaml: lines.slice(1, close).join("\n"), body: lines.slice(close + 1).join("\n") };
111
- }
112
- /** Parse one scalar in the YAML subset the platform's schema keys use. */
113
- function parseScalar(raw, lineNo) {
114
- const value = raw.trim();
115
- if (value === "")
116
- return "";
117
- if (value.startsWith("'")) {
118
- if (!value.endsWith("'"))
119
- throw new Error(`unterminated single-quoted scalar at line ${lineNo}`);
120
- return value.slice(1, -1).replace(/''/g, "'");
121
- }
122
- if (value.startsWith('"')) {
123
- if (!value.endsWith('"'))
124
- throw new Error(`unterminated double-quoted scalar at line ${lineNo}`);
125
- return value
126
- .slice(1, -1)
127
- .replace(/\\n/g, "\n")
128
- .replace(/\\t/g, "\t")
129
- .replace(/\\"/g, '"')
130
- .replace(/\\\\/g, "\\");
131
- }
132
- return value;
133
- }
134
- /** Minimal YAML-subset parser for the flat schema the platform reads (mirrors validate-frontmatter.mjs). */
135
- function parseMiniYaml(text) {
136
- const data = {};
137
- const lines = text.split("\n");
138
- let i = 0;
139
- while (i < lines.length) {
140
- const line = lines[i] ?? "";
141
- const lineNo = i + 1;
142
- i += 1;
143
- const trimmed = line.trim();
144
- if (trimmed === "" || trimmed.startsWith("#"))
145
- continue;
146
- if (line.length - line.trimStart().length > 0) {
147
- throw new Error(`unsupported indented construct at line ${lineNo}: ${trimmed}`);
148
- }
149
- const match = /^([A-Za-z0-9_-]+):(?:\s+(.*))?$/.exec(trimmed);
150
- if (!match)
151
- throw new Error(`unparseable line ${lineNo}: ${trimmed}`);
152
- const key = match[1] ?? "";
153
- let value = match[2] ?? "";
154
- if (value === "|" || value === ">") {
155
- const block = [];
156
- while (i < lines.length && (lines[i] ?? "").trim() !== "" && (lines[i] ?? "").startsWith(" ")) {
157
- block.push(lines[i] ?? "");
158
- i += 1;
159
- }
160
- data[key] = block.join("\n");
161
- continue;
162
- }
163
- if (value === "" && i < lines.length && (lines[i] ?? "").startsWith(" ") && (lines[i] ?? "").trim() !== "") {
164
- const nested = {};
165
- while (i < lines.length && (lines[i] ?? "").trim() !== "" && (lines[i] ?? "").startsWith(" ")) {
166
- const nm = /^([A-Za-z0-9_-]+):(?:\s+(.*))?$/.exec((lines[i] ?? "").trim());
167
- if (!nm)
168
- throw new Error(`unparseable nested line ${i + 1}: ${(lines[i] ?? "").trim()}`);
169
- nested[nm[1] ?? ""] = parseScalar(nm[2] ?? "", i + 1);
170
- i += 1;
171
- }
172
- data[key] = nested;
173
- continue;
174
- }
175
- data[key] = parseScalar(value, lineNo);
176
- }
177
- return data;
178
- }
179
- /**
180
- * Mechanical frontmatter validation of a rendered SKILL.md, mirroring
181
- * `skill-creator/scripts/validate-frontmatter.mjs` (and the platform's
182
- * skill-filesystem rules): delimiter structure, name kebab-case, non-empty
183
- * description, invocation-boolean spellings, legacy camelCase key rejection,
184
- * whenToUse/metadata types. Returns human-readable problems; an empty array
185
- * means the file would load.
186
- */
187
- export function validateRenderedSkillMarkdown(markdown) {
188
- const problems = [];
189
- const split = splitFrontmatter(markdown);
190
- if (!split) {
191
- return [
192
- "missing YAML frontmatter (first line `---` with a closing `---`) — platform would IGNORE this file",
193
- ];
194
- }
195
- let data;
196
- try {
197
- data = parseMiniYaml(split.yaml);
198
- }
199
- catch (cause) {
200
- return [`invalid YAML frontmatter: ${cause instanceof Error ? cause.message : String(cause)} — platform would IGNORE this file`];
201
- }
202
- const name = typeof data["name"] === "string" && data["name"].length > 0 ? data["name"] : undefined;
203
- if (name === undefined) {
204
- problems.push("frontmatter requires non-empty `name` — platform would IGNORE this file");
205
- }
206
- else if (!NAME_RE.test(name)) {
207
- problems.push(`invalid skill name "${name}" (must match ${NAME_RE}) — platform would IGNORE this file`);
208
- }
209
- const description = typeof data["description"] === "string" && data["description"].length > 0 ? data["description"] : undefined;
210
- if (description === undefined) {
211
- problems.push("frontmatter requires non-empty `description` — platform would IGNORE this file");
212
- }
213
- for (const legacy of LEGACY_KEYS) {
214
- if (Object.hasOwn(data, legacy)) {
215
- problems.push(`legacy key "${legacy}" is unsupported; use "${CANONICAL_KEYS[legacy]}" — platform would IGNORE this file`);
216
- }
217
- }
218
- for (const key of ["disable-model-invocation", "user-invocable"]) {
219
- if (!Object.hasOwn(data, key))
220
- continue;
221
- const value = data[key];
222
- if (typeof value === "boolean")
223
- continue;
224
- const word = String(value).toLowerCase();
225
- if (TRUE_WORDS.has(word) || FALSE_WORDS.has(word))
226
- continue;
227
- problems.push(`frontmatter field "${key}" must be a boolean (accepted: true/false/yes/no/on/off/1/0), got ${JSON.stringify(value)} — platform would IGNORE this file`);
228
- }
229
- if (Object.hasOwn(data, "whenToUse") && !(typeof data["whenToUse"] === "string" && data["whenToUse"].length > 0)) {
230
- problems.push("`whenToUse` must be a non-empty string when present");
231
- }
232
- if (Object.hasOwn(data, "metadata") && (typeof data["metadata"] !== "object" || data["metadata"] === null || Array.isArray(data["metadata"]))) {
233
- problems.push("`metadata` must be an object when present");
234
- }
235
- return problems;
236
- }
237
- /**
238
- * Mechanical validation of a skill entry's raw `content` (the SKILL.md body
239
- * that materializes under the generated frontmatter). Code-enforced at
240
- * apply time so a bad entry never reaches the store:
241
- * - empty content is rejected;
242
- * - content must not open with a `---` block: the materializer generates
243
- * its own frontmatter, and a second frontmatter in the body would be
244
- * parsed instead of the generated one (the platform reads the FIRST
245
- * closing `---`), so the file could be ignored or routed wrongly;
246
- * - resource references (`references/…`, `scripts/…`) must be skill-local
247
- * relative paths — an embedded `/../` after the category escapes the skill
248
- * directory and is rejected. A leading `../` instead addresses a sibling
249
- * skill (cross-skill interlink, e.g. `../skill-creator/…`) and is allowed:
250
- * it is not dangling-checked since cross-skill targets live outside this
251
- * entry's shipped files (see skillResourceRefs).
252
- * Returns human-readable problems; an empty array means the content is
253
- * mechanically acceptable.
254
- */
255
- export function validateSkillEntryContent(content) {
256
- const problems = [];
257
- const trimmed = content.trim();
258
- if (trimmed.length === 0) {
259
- problems.push("skill content is empty");
260
- return problems;
261
- }
262
- if (trimmed.startsWith("---")) {
263
- problems.push("skill content must not start with a `---` frontmatter block (the materializer generates frontmatter from id/title; a body-level `---` would shadow it and the platform could IGNORE the file)");
264
- }
265
- for (const match of trimmed.matchAll(/(?<![\w])(references|scripts)\/[^\s)]+/g)) {
266
- // match[0] always starts with `references/` or `scripts/` (the regex
267
- // anchors it there), so only an embedded `/../` can escape the skill
268
- // directory — the ../-prefix, absolute-path, and drive-letter
269
- // spellings can never match and are intentionally not checked.
270
- const ref = match[0] ?? "";
271
- if (ref.includes("/../")) {
272
- problems.push(`skill content resource reference escapes the skill directory: ${ref}`);
273
- }
274
- }
275
- return problems;
276
- }
277
- /**
278
- * Validate the FULL rendered SKILL.md of an entry (generated frontmatter +
279
- * body) — the exact bytes that materialize on disk. Used as the final
280
- * code-enforced line after materialization; problems here mean the platform
281
- * would refuse to load the file.
282
- */
283
- export function validateRenderedSkill(entry) {
284
- return validateRenderedSkillMarkdown(renderSkillMarkdown(entry));
285
- }
286
- /**
287
- * Resource references (`references/…`, `scripts/…`) found in a skill body —
288
- * the same scanning policy as validate-frontmatter.mjs: markdown link
289
- * targets starting with the category, plus backticked/prose paths carrying
290
- * a filename extension. Used after materialization to warn about dangling
291
- * references (a body referencing a resource the entry never ships).
292
- * Leading-`../` cross-skill interlinks are skipped, not warned: they resolve
293
- * against sibling skill directories, never against shipped files.
294
- */
295
- export function skillResourceRefs(content) {
296
- const refs = new Set();
297
- for (const match of content.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
298
- const target = ((match[1] ?? "").trim().split(/\s+/)[0] ?? "").trim();
299
- if (/^(references|scripts)\/[\w./-]+$/.test(target) && !target.startsWith("../")) {
300
- refs.add(target);
301
- }
302
- }
303
- const stripped = content.replace(/\[[^\]]*\]\([^)]*\)/g, "");
304
- for (const match of stripped.matchAll(/(?<![\w])(references|scripts)\/[\w./-]+\.\w+/g)) {
305
- const path = match[0] ?? "";
306
- // Cross-skill interlinks (`../skill-creator/...`) resolve against the
307
- // sibling skill's directory, not this one — skip references whose
308
- // prose prefix walks up a directory (mirrors validate-frontmatter.mjs).
309
- let cursor = (match.index ?? 0) - 1;
310
- while (cursor >= 0 && /[\w./-]/.test(stripped[cursor] ?? ""))
311
- cursor -= 1;
312
- if (!stripped.slice(cursor + 1, match.index ?? 0).includes("..")) {
313
- refs.add(path);
314
- }
315
- }
316
- return [...refs];
317
- }
318
- /** Kebab-case name under which the entry materializes (exported for diagnostics). */
319
- export { skillNameOf } from "./skill-render.js";
320
- //# sourceMappingURL=skillquality.js.map
package/lib/source.d.ts DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * Trajectory citations: where distilled entries came from in the session
3
- * log. DSH sessions are event-sourced with contiguous seq numbers, so a
4
- * citation of (sessionId, seqs) expands back to the exact original
5
- * conversation rows (the durable log under `<dshHome>/sessions/...`).
6
- *
7
- * The extraction reads the live session log through the reader the running
8
- * harness generation exposes (duck-typed, no dsh-session dependency) and only
9
- * ever selects direct human messages — injected plugin context and tool
10
- * results never become citations.
11
- */
12
- import type { EntrySource } from "./types.js";
13
- import { type AgentLike } from "./inject.js";
14
- /** At most this many source user messages are cited per entry. */
15
- export declare const MAX_SOURCE_MESSAGES = 3;
16
- /**
17
- * The seqs of the agent's most recent direct user messages, in log order.
18
- * Returns [] when the agent has no readable log or no qualifying messages —
19
- * callers then simply omit the citation.
20
- */
21
- export declare function recentUserSeqs(agent: AgentLike | undefined, opts?: {
22
- maxMessages?: number;
23
- }): number[];
24
- /**
25
- * Build the citation for an apply call: the session id plus the seqs of the
26
- * most recent direct user messages. Returns undefined when neither can be
27
- * determined, so callers can pass it straight through without special cases.
28
- */
29
- export declare function entrySourceOf(agent: AgentLike | undefined, sessionId: string | undefined): EntrySource | undefined;
30
- //# sourceMappingURL=source.d.ts.map
package/lib/source.js DELETED
@@ -1,43 +0,0 @@
1
- import { sessionEventsOf } from "./inject.js";
2
- /** At most this many source user messages are cited per entry. */
3
- export const MAX_SOURCE_MESSAGES = 3;
4
- /**
5
- * The seqs of the agent's most recent direct user messages, in log order.
6
- * Returns [] when the agent has no readable log or no qualifying messages —
7
- * callers then simply omit the citation.
8
- */
9
- export function recentUserSeqs(agent, opts) {
10
- const events = sessionEventsOf(agent);
11
- if (events.length === 0) {
12
- return [];
13
- }
14
- const maxMessages = opts?.maxMessages ?? MAX_SOURCE_MESSAGES;
15
- const seqs = [];
16
- for (let i = events.length - 1; i >= 0 && seqs.length < maxMessages; i -= 1) {
17
- const row = events[i];
18
- if (row?.type !== "user/message") {
19
- continue;
20
- }
21
- const source = row.data?.source;
22
- if (source && source.kind !== "user") {
23
- continue;
24
- }
25
- if (typeof row.seq === "number" && Number.isInteger(row.seq)) {
26
- seqs.unshift(row.seq);
27
- }
28
- }
29
- return seqs;
30
- }
31
- /**
32
- * Build the citation for an apply call: the session id plus the seqs of the
33
- * most recent direct user messages. Returns undefined when neither can be
34
- * determined, so callers can pass it straight through without special cases.
35
- */
36
- export function entrySourceOf(agent, sessionId) {
37
- if (!sessionId) {
38
- return undefined;
39
- }
40
- const seqs = recentUserSeqs(agent);
41
- return seqs.length > 0 ? { sessionId, seqs } : { sessionId };
42
- }
43
- //# sourceMappingURL=source.js.map