cognitive-core 0.2.3 → 0.2.4
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/dist/bin/cognitive-core.js +0 -0
- package/dist/learning/pipeline.d.ts +89 -0
- package/dist/learning/pipeline.d.ts.map +1 -0
- package/dist/learning/pipeline.js +236 -0
- package/dist/learning/pipeline.js.map +1 -0
- package/dist/surfacing/skill-publisher.d.ts.map +1 -1
- package/dist/surfacing/skill-publisher.js +74 -23
- package/dist/surfacing/skill-publisher.js.map +1 -1
- package/dist/types/playbook.d.ts +11 -0
- package/dist/types/playbook.d.ts.map +1 -1
- package/dist/types/playbook.js +2 -0
- package/dist/types/playbook.js.map +1 -1
- package/dist/workspace/templates/index.d.ts +1 -0
- package/dist/workspace/templates/index.d.ts.map +1 -1
- package/dist/workspace/templates/index.js +2 -0
- package/dist/workspace/templates/index.js.map +1 -1
- package/dist/workspace/templates/skill-enrichment.d.ts +48 -0
- package/dist/workspace/templates/skill-enrichment.d.ts.map +1 -0
- package/dist/workspace/templates/skill-enrichment.js +175 -0
- package/dist/workspace/templates/skill-enrichment.js.map +1 -0
- package/package.json +3 -3
- package/src/surfacing/skill-publisher.ts +92 -26
- package/src/types/playbook.ts +15 -0
- package/src/workspace/templates/index.ts +7 -0
- package/src/workspace/templates/skill-enrichment.ts +275 -0
- package/tests/surfacing/skill-publisher.test.ts +81 -8
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Enrichment Template
|
|
3
|
+
*
|
|
4
|
+
* Agent-in-the-loop step between playbook extraction and skill publishing.
|
|
5
|
+
* Given a Playbook and its procedurally generated SKILL.md skeleton, an agent
|
|
6
|
+
* enriches the skill with narrative prose, worked examples, cross-references,
|
|
7
|
+
* and a trigger-rich description — things a procedural converter cannot produce.
|
|
8
|
+
*
|
|
9
|
+
* Complexity routing:
|
|
10
|
+
* - Simple playbooks (<=3 tactics, low complexity) → heuristic: keep procedural output as-is
|
|
11
|
+
* - Standard/complex playbooks → agent enrichment
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { WorkspaceHandle } from 'agent-workspace';
|
|
15
|
+
import type { Playbook } from '../../types/index.js';
|
|
16
|
+
import { convertPlaybookToSkill } from '../../surfacing/skill-publisher.js';
|
|
17
|
+
import type {
|
|
18
|
+
AgenticTaskTemplate,
|
|
19
|
+
AnalysisComplexity,
|
|
20
|
+
ResourceSpec,
|
|
21
|
+
} from '../types.js';
|
|
22
|
+
import type { ComputeRequirements } from '../../runtime/compute-provider.js';
|
|
23
|
+
|
|
24
|
+
// ============================================================
|
|
25
|
+
// Input / Output Types
|
|
26
|
+
// ============================================================
|
|
27
|
+
|
|
28
|
+
export interface SkillEnrichmentInput {
|
|
29
|
+
/** The playbook to enrich into a skill */
|
|
30
|
+
playbook: Playbook;
|
|
31
|
+
/** Optional: existing hand-written SKILL.md to use as style reference */
|
|
32
|
+
styleReference?: string;
|
|
33
|
+
/** Optional: related skill names for cross-referencing */
|
|
34
|
+
relatedSkills?: Array<{ name: string; description: string }>;
|
|
35
|
+
/** Optional: domain context (e.g. "swarmkit ecosystem", "TypeScript tooling") */
|
|
36
|
+
domainContext?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SkillEnrichmentOutput {
|
|
40
|
+
/** Enriched SKILL.md frontmatter description (trigger-rich, under 1024 chars) */
|
|
41
|
+
description: string;
|
|
42
|
+
/** Enriched markdown body (replaces the procedural instructions) */
|
|
43
|
+
instructions: string;
|
|
44
|
+
/** Worked examples the agent generated */
|
|
45
|
+
examples: Array<{
|
|
46
|
+
title: string;
|
|
47
|
+
scenario: string;
|
|
48
|
+
steps: string;
|
|
49
|
+
}>;
|
|
50
|
+
/** Cross-references to related skills */
|
|
51
|
+
seeAlso: Array<{
|
|
52
|
+
name: string;
|
|
53
|
+
relation: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Agent's assessment of the playbook's completeness */
|
|
56
|
+
gaps: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================
|
|
60
|
+
// Template Implementation
|
|
61
|
+
// ============================================================
|
|
62
|
+
|
|
63
|
+
export const skillEnrichmentTemplate: AgenticTaskTemplate<
|
|
64
|
+
SkillEnrichmentInput,
|
|
65
|
+
SkillEnrichmentOutput
|
|
66
|
+
> = {
|
|
67
|
+
taskType: 'skill-enrichment',
|
|
68
|
+
domain: 'skill-publishing',
|
|
69
|
+
description: 'Enrich a procedurally generated SKILL.md with narrative prose, examples, and cross-references',
|
|
70
|
+
|
|
71
|
+
assessComplexity(input: SkillEnrichmentInput): AnalysisComplexity {
|
|
72
|
+
const pb = input.playbook;
|
|
73
|
+
const tacticCount = pb.guidance.tactics.length;
|
|
74
|
+
const hasSteps = (pb.guidance.steps?.length ?? 0) > 0;
|
|
75
|
+
const isComplex = pb.complexity === 'complex';
|
|
76
|
+
|
|
77
|
+
if (tacticCount <= 3 && !hasSteps && !isComplex) return 'heuristic';
|
|
78
|
+
if (isComplex || tacticCount > 8) return 'thorough';
|
|
79
|
+
return 'standard';
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
async heuristicFallback(input: SkillEnrichmentInput): Promise<SkillEnrichmentOutput> {
|
|
83
|
+
const skill = convertPlaybookToSkill(input.playbook);
|
|
84
|
+
return {
|
|
85
|
+
description: skill.description,
|
|
86
|
+
instructions: skill.instructions,
|
|
87
|
+
examples: [],
|
|
88
|
+
seeAlso: (input.relatedSkills ?? []).map((r) => ({
|
|
89
|
+
name: r.name,
|
|
90
|
+
relation: `Related: ${r.description}`,
|
|
91
|
+
})),
|
|
92
|
+
gaps: [],
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
async prepareWorkspace(
|
|
97
|
+
input: SkillEnrichmentInput,
|
|
98
|
+
handle: WorkspaceHandle,
|
|
99
|
+
): Promise<void> {
|
|
100
|
+
// Write the playbook as structured JSON
|
|
101
|
+
await handle.writeJson('input', 'playbook.json', {
|
|
102
|
+
name: input.playbook.name,
|
|
103
|
+
applicability: input.playbook.applicability,
|
|
104
|
+
guidance: input.playbook.guidance,
|
|
105
|
+
verification: input.playbook.verification,
|
|
106
|
+
complexity: input.playbook.complexity,
|
|
107
|
+
confidence: input.playbook.confidence,
|
|
108
|
+
evolution: {
|
|
109
|
+
version: input.playbook.evolution.version,
|
|
110
|
+
successCount: input.playbook.evolution.successCount,
|
|
111
|
+
failureCount: input.playbook.evolution.failureCount,
|
|
112
|
+
refinements: input.playbook.evolution.refinements,
|
|
113
|
+
},
|
|
114
|
+
provenance: input.playbook.provenance,
|
|
115
|
+
userInvocable: input.playbook.userInvocable,
|
|
116
|
+
publishMetadata: input.playbook.publishMetadata,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Write the procedural skeleton for the agent to improve upon
|
|
120
|
+
const skill = convertPlaybookToSkill(input.playbook);
|
|
121
|
+
await handle.writeRaw('input', 'skeleton.md', skill.instructions);
|
|
122
|
+
await handle.writeRaw('input', 'skeleton-description.txt', skill.description);
|
|
123
|
+
|
|
124
|
+
// Style reference (if provided)
|
|
125
|
+
if (input.styleReference) {
|
|
126
|
+
await handle.writeRaw('input', 'style-reference.md', input.styleReference);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Related skills for cross-referencing
|
|
130
|
+
if (input.relatedSkills && input.relatedSkills.length > 0) {
|
|
131
|
+
await handle.writeJson('input', 'related-skills.json', input.relatedSkills);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Domain context
|
|
135
|
+
if (input.domainContext) {
|
|
136
|
+
await handle.writeRaw('input', 'domain-context.txt', input.domainContext);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
buildTaskPrompt(input: SkillEnrichmentInput): string {
|
|
141
|
+
const parts: string[] = [
|
|
142
|
+
`Enrich the skill "${input.playbook.name}" into a high-quality SKILL.md that an AI agent will load and follow.`,
|
|
143
|
+
'',
|
|
144
|
+
'## Inputs',
|
|
145
|
+
'',
|
|
146
|
+
'- `input/playbook.json` — the structured playbook (applicability, guidance, verification)',
|
|
147
|
+
'- `input/skeleton.md` — procedurally generated markdown body (your starting point)',
|
|
148
|
+
'- `input/skeleton-description.txt` — procedurally generated frontmatter description',
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
if (input.styleReference) {
|
|
152
|
+
parts.push('- `input/style-reference.md` — a hand-written SKILL.md to match in tone and structure');
|
|
153
|
+
}
|
|
154
|
+
if (input.relatedSkills && input.relatedSkills.length > 0) {
|
|
155
|
+
parts.push('- `input/related-skills.json` — related skills to cross-reference');
|
|
156
|
+
}
|
|
157
|
+
if (input.domainContext) {
|
|
158
|
+
parts.push('- `input/domain-context.txt` — domain context for accurate terminology');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
parts.push(
|
|
162
|
+
'',
|
|
163
|
+
'## What to produce',
|
|
164
|
+
'',
|
|
165
|
+
'Read all inputs, then write `output/enrichment.json` with this schema:',
|
|
166
|
+
'',
|
|
167
|
+
'```json',
|
|
168
|
+
'{',
|
|
169
|
+
' "description": "Trigger-rich frontmatter description, under 1024 chars. Structure: [What it does] + [Use when ...trigger phrases...]. Include concrete phrases users would say.",',
|
|
170
|
+
' "instructions": "Full markdown body. Must include:\\n- ## When to use (bulleted)\\n- ## When not to use (bulleted)\\n- ## Workflow (numbered tactics expanded into full prose — explain WHY each step matters, not just WHAT to do)\\n- ## Inputs / ## Outputs\\n- ## Verification (bulleted success/failure indicators)\\n- ## Edge cases (narrative, with recovery paths)\\n- ## Examples (2-3 worked scenarios with concrete commands)\\n- ## See also (cross-references)",',
|
|
171
|
+
' "examples": [{"title": "...", "scenario": "...", "steps": "..."}],',
|
|
172
|
+
' "seeAlso": [{"name": "skill-name", "relation": "why it is related"}],',
|
|
173
|
+
' "gaps": ["anything missing from the playbook that the skill should cover but cannot without more data"]',
|
|
174
|
+
'}',
|
|
175
|
+
'```',
|
|
176
|
+
'',
|
|
177
|
+
'## Guidelines',
|
|
178
|
+
'',
|
|
179
|
+
'- The skeleton is your starting point — improve it, do not discard it.',
|
|
180
|
+
'- Expand each tactic in the Workflow section into a paragraph that explains the rationale, not just the command.',
|
|
181
|
+
'- Generate 2-3 realistic examples with concrete tool calls (MCP tools, CLI commands, or library calls as appropriate for the domain).',
|
|
182
|
+
'- Cross-reference related skills by name in the See Also section.',
|
|
183
|
+
'- Flag gaps honestly — if the playbook is missing triggers, anti-patterns, or verification criteria, say so in the gaps array.',
|
|
184
|
+
'- Keep the description under 1024 characters. It must include trigger phrases users would actually say.',
|
|
185
|
+
'- Do not invent capabilities the playbook does not describe. Enrich what is there; do not fabricate.',
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (input.styleReference) {
|
|
189
|
+
parts.push(
|
|
190
|
+
'- Match the tone, section structure, and level of detail from the style reference.',
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return parts.join('\n');
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
getSkills() { return []; },
|
|
198
|
+
getResources(input: SkillEnrichmentInput): ResourceSpec[] {
|
|
199
|
+
const resources: ResourceSpec[] = [];
|
|
200
|
+
if (input.domainContext) {
|
|
201
|
+
resources.push({
|
|
202
|
+
type: 'file',
|
|
203
|
+
path: 'domain-context.txt',
|
|
204
|
+
source: input.domainContext,
|
|
205
|
+
description: 'Domain context for accurate terminology',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return resources;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
outputConfig: {
|
|
212
|
+
files: [
|
|
213
|
+
{
|
|
214
|
+
path: 'enrichment.json',
|
|
215
|
+
format: 'json' as const,
|
|
216
|
+
required: true,
|
|
217
|
+
description: 'Enriched skill content with description, instructions, examples, cross-refs, and gaps',
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
async collectOutput(handle: WorkspaceHandle): Promise<SkillEnrichmentOutput> {
|
|
223
|
+
const raw = await handle.readJson('output', 'enrichment.json') as Record<string, unknown>;
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
description: typeof raw.description === 'string'
|
|
227
|
+
? raw.description.slice(0, 1024)
|
|
228
|
+
: '',
|
|
229
|
+
instructions: typeof raw.instructions === 'string'
|
|
230
|
+
? raw.instructions
|
|
231
|
+
: '',
|
|
232
|
+
examples: Array.isArray(raw.examples)
|
|
233
|
+
? (raw.examples as Array<Record<string, unknown>>).map((e) => ({
|
|
234
|
+
title: String(e.title ?? ''),
|
|
235
|
+
scenario: String(e.scenario ?? ''),
|
|
236
|
+
steps: String(e.steps ?? ''),
|
|
237
|
+
}))
|
|
238
|
+
: [],
|
|
239
|
+
seeAlso: Array.isArray(raw.seeAlso)
|
|
240
|
+
? (raw.seeAlso as Array<Record<string, unknown>>).map((s) => ({
|
|
241
|
+
name: String(s.name ?? ''),
|
|
242
|
+
relation: String(s.relation ?? ''),
|
|
243
|
+
}))
|
|
244
|
+
: [],
|
|
245
|
+
gaps: Array.isArray(raw.gaps)
|
|
246
|
+
? (raw.gaps as Array<unknown>).map(String)
|
|
247
|
+
: [],
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
async processOutput(): Promise<void> {
|
|
252
|
+
// Caller handles merging the enriched output back into the Skill
|
|
253
|
+
// and publishing via SkillPublisher.
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
computeRequirements: {
|
|
257
|
+
mode: 'local',
|
|
258
|
+
complexity: 'standard',
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
getComputeRequirements(
|
|
262
|
+
_input: SkillEnrichmentInput,
|
|
263
|
+
complexity: AnalysisComplexity,
|
|
264
|
+
): ComputeRequirements {
|
|
265
|
+
return {
|
|
266
|
+
mode: 'local',
|
|
267
|
+
complexity,
|
|
268
|
+
timeout: complexity === 'thorough' ? 300_000 : 180_000,
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
agentType: 'claude-code',
|
|
273
|
+
timeout: 180_000,
|
|
274
|
+
captureToolCalls: true,
|
|
275
|
+
};
|
|
@@ -77,10 +77,73 @@ describe('SkillPublisher', () => {
|
|
|
77
77
|
const playbook = createSamplePlaybook();
|
|
78
78
|
const skill = convertPlaybookToSkill(playbook);
|
|
79
79
|
|
|
80
|
-
expect(skill.description).
|
|
80
|
+
expect(skill.description).toContain('TypeScript import fails with TS2307');
|
|
81
81
|
expect(skill.instructions).toContain('TypeScript import fails with TS2307');
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
it('should surface trigger phrases inline in description for lightweight matching', () => {
|
|
85
|
+
const playbook = createSamplePlaybook();
|
|
86
|
+
const skill = convertPlaybookToSkill(playbook);
|
|
87
|
+
|
|
88
|
+
// Lead situation still present.
|
|
89
|
+
expect(skill.description).toContain('TypeScript import fails with TS2307');
|
|
90
|
+
// Explicit "Use when" clause listing each trigger phrase, quoted.
|
|
91
|
+
expect(skill.description).toContain('Use when');
|
|
92
|
+
expect(skill.description).toContain('"TS2307"');
|
|
93
|
+
expect(skill.description).toContain('"Cannot find module"');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should omit trigger clause when no triggers are declared', () => {
|
|
97
|
+
const playbook = createSamplePlaybook({
|
|
98
|
+
applicability: {
|
|
99
|
+
situations: ['Some situation'],
|
|
100
|
+
triggers: [],
|
|
101
|
+
antiPatterns: [],
|
|
102
|
+
domains: [],
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const skill = convertPlaybookToSkill(playbook);
|
|
106
|
+
|
|
107
|
+
expect(skill.description).toBe('Some situation.');
|
|
108
|
+
expect(skill.description).not.toContain('Use when');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should cap description at 1024 chars', () => {
|
|
112
|
+
const playbook = createSamplePlaybook({
|
|
113
|
+
applicability: {
|
|
114
|
+
situations: ['X'.repeat(500)],
|
|
115
|
+
// Many long triggers would push past 1024 chars combined.
|
|
116
|
+
triggers: Array.from({ length: 20 }, (_, i) => 'trigger-phrase-' + 'y'.repeat(50) + `-${i}`),
|
|
117
|
+
antiPatterns: [],
|
|
118
|
+
domains: [],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
const skill = convertPlaybookToSkill(playbook);
|
|
122
|
+
|
|
123
|
+
expect(skill.description.length).toBeLessThanOrEqual(1024);
|
|
124
|
+
expect(skill.description).toMatch(/\.\.\.$/);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should use provenance.curatedBy as author when set', () => {
|
|
128
|
+
const playbook = createSamplePlaybook({
|
|
129
|
+
provenance: {
|
|
130
|
+
origin: 'curated',
|
|
131
|
+
curatedBy: 'swarmkit-skills',
|
|
132
|
+
recordedAt: new Date(),
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const skill = convertPlaybookToSkill(playbook);
|
|
136
|
+
|
|
137
|
+
expect(skill.author).toBe('swarmkit-skills');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should fall back to cognitive-core as author when provenance.curatedBy is absent', () => {
|
|
141
|
+
const playbook = createSamplePlaybook();
|
|
142
|
+
const skill = convertPlaybookToSkill(playbook);
|
|
143
|
+
|
|
144
|
+
expect(skill.author).toBe('cognitive-core');
|
|
145
|
+
});
|
|
146
|
+
|
|
84
147
|
it('should build instructions from strategy, tactics, and steps', () => {
|
|
85
148
|
const playbook = createSamplePlaybook();
|
|
86
149
|
const skill = convertPlaybookToSkill(playbook);
|
|
@@ -92,15 +155,25 @@ describe('SkillPublisher', () => {
|
|
|
92
155
|
expect(skill.instructions).toContain('Verify with tsc --noEmit');
|
|
93
156
|
});
|
|
94
157
|
|
|
95
|
-
it('should include verification and
|
|
158
|
+
it('should include structured verification, when-to-use, and when-not-to-use sections', () => {
|
|
96
159
|
const playbook = createSamplePlaybook();
|
|
97
160
|
const skill = convertPlaybookToSkill(playbook);
|
|
98
161
|
|
|
99
|
-
|
|
100
|
-
expect(skill.instructions).toContain('
|
|
101
|
-
expect(skill.instructions).toContain('
|
|
102
|
-
expect(skill.instructions).toContain('Refinements:');
|
|
162
|
+
// Verification indicators rendered as bulleted lists
|
|
163
|
+
expect(skill.instructions).toContain('Success:\n- Build passes');
|
|
164
|
+
expect(skill.instructions).toContain('Failure:\n- Same error persists');
|
|
103
165
|
expect(skill.instructions).toContain('Rollback:');
|
|
166
|
+
|
|
167
|
+
// Anti-patterns now live in their own section, not in Notes
|
|
168
|
+
expect(skill.instructions).toContain('## When not to use');
|
|
169
|
+
expect(skill.instructions).toContain('- Module genuinely does not exist');
|
|
170
|
+
|
|
171
|
+
// Situations get their own section
|
|
172
|
+
expect(skill.instructions).toContain('## When to use');
|
|
173
|
+
expect(skill.instructions).toContain('- TypeScript import fails with TS2307');
|
|
174
|
+
|
|
175
|
+
// Refinements remain in Notes
|
|
176
|
+
expect(skill.instructions).toContain('Refinements:');
|
|
104
177
|
});
|
|
105
178
|
|
|
106
179
|
it('should compute metrics from evolution data', () => {
|
|
@@ -277,12 +350,12 @@ describe('SkillPublisher', () => {
|
|
|
277
350
|
expect(result.error).toContain('not found');
|
|
278
351
|
});
|
|
279
352
|
|
|
280
|
-
it('should preserve existing
|
|
353
|
+
it('should preserve existing content when deprecating', async () => {
|
|
281
354
|
const publisher = new SkillPublisher(storage);
|
|
282
355
|
await publisher.publishPlaybook(createSamplePlaybook({ id: 'notes-1' }));
|
|
283
356
|
|
|
284
357
|
const before = await storage.getSkill('notes-1');
|
|
285
|
-
expect(before!.instructions).toContain('
|
|
358
|
+
expect(before!.instructions).toContain('## When not to use');
|
|
286
359
|
|
|
287
360
|
await publisher.deprecateSkill('notes-1', 'Superseded');
|
|
288
361
|
|