(task: Task, chunks: Chunk[], evalResult: CreateEvalResult)
| 193 | } |
| 194 | |
| 195 | async generate(task: Task, chunks: Chunk[], evalResult: CreateEvalResult): Promise<Skill> { |
| 196 | const conversationText = this.buildConversationText(chunks); |
| 197 | |
| 198 | // ── Step 1: Generate SKILL.md (primary, largest output) ── |
| 199 | this.ctx.log.info(`SkillGenerator: Step 1/4 — generating SKILL.md for "${evalResult.suggestedName}"`); |
| 200 | let skillMdContent = await this.step1GenerateSkillMd(task, conversationText, evalResult); |
| 201 | |
| 202 | const skillsStoreDir = path.join(this.ctx.stateDir, "skills-store"); |
| 203 | const dirPath = path.join(skillsStoreDir, evalResult.suggestedName); |
| 204 | fs.mkdirSync(dirPath, { recursive: true }); |
| 205 | fs.writeFileSync(path.join(dirPath, "SKILL.md"), skillMdContent, "utf-8"); |
| 206 | |
| 207 | // ── Step 2: Extract scripts (parallel with refs) ── |
| 208 | this.ctx.log.info(`SkillGenerator: Step 2/4 — extracting scripts and references`); |
| 209 | const [scripts, references] = await Promise.all([ |
| 210 | this.step2ExtractScripts(skillMdContent, conversationText), |
| 211 | this.step2bExtractReferences(skillMdContent, conversationText), |
| 212 | ]); |
| 213 | |
| 214 | if (scripts.length > 0) { |
| 215 | const scriptsDir = path.join(dirPath, "scripts"); |
| 216 | fs.mkdirSync(scriptsDir, { recursive: true }); |
| 217 | for (const s of scripts) { |
| 218 | fs.writeFileSync(path.join(scriptsDir, s.filename), s.content, "utf-8"); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | if (references.length > 0) { |
| 223 | const refsDir = path.join(dirPath, "references"); |
| 224 | fs.mkdirSync(refsDir, { recursive: true }); |
| 225 | for (const r of references) { |
| 226 | fs.writeFileSync(path.join(refsDir, r.filename), r.content, "utf-8"); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | // Ensure SKILL.md has companion files section |
| 231 | if (scripts.length > 0 || references.length > 0) { |
| 232 | const hasCompanionSection = /## Companion files|## 附属文件|## 辅助文件/.test(skillMdContent); |
| 233 | if (!hasCompanionSection) { |
| 234 | const companionLines: string[] = ["\n\n## Companion files\n"]; |
| 235 | for (const s of scripts) { |
| 236 | companionLines.push(`- \`scripts/${s.filename}\` — automation script`); |
| 237 | } |
| 238 | for (const r of references) { |
| 239 | companionLines.push(`- \`references/${r.filename}\` — reference documentation`); |
| 240 | } |
| 241 | skillMdContent += companionLines.join("\n"); |
| 242 | fs.writeFileSync(path.join(dirPath, "SKILL.md"), skillMdContent, "utf-8"); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // ── Step 3: Generate evals ── |
| 247 | this.ctx.log.info(`SkillGenerator: Step 3/4 — generating eval test cases`); |
| 248 | const evals = await this.step3GenerateEvals(skillMdContent); |
| 249 | |
| 250 | if (evals.length > 0) { |
| 251 | const evalsDir = path.join(dirPath, "evals"); |
| 252 | fs.mkdirSync(evalsDir, { recursive: true }); |
no test coverage detected