From 9d9e3d1555eb257ea1b5834402c093a3a973358d Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Sun, 18 Jan 2026 19:51:05 -0800 Subject: [PATCH] Stabilize skill-creator CI and package format (#17001) --- .../skill-creator-scripts.test.ts | 15 +++++++++++---- .../skill-creator/scripts/package_skill.cjs | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/integration-tests/skill-creator-scripts.test.ts b/integration-tests/skill-creator-scripts.test.ts index fe58ed9d90..82bc8c4ece 100644 --- a/integration-tests/skill-creator-scripts.test.ts +++ b/integration-tests/skill-creator-scripts.test.ts @@ -66,14 +66,15 @@ describe('skill-creator scripts e2e', () => { // 4. Fix SKILL.md (remove TODOs) let content = fs.readFileSync(path.join(skillDir, 'SKILL.md'), 'utf8'); - content = content.replace(/TODO: .+/g, 'Fixed'); - content = content.replace(/\[TODO: .+/g, 'Fixed'); + // More aggressive global replace for all TODO patterns + content = content.replace(/TODO:[^\n]*/g, 'Fixed'); + content = content.replace(/\[TODO:[^\]]*\]/g, 'Fixed'); fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content); // Also remove TODOs from example scripts const exampleScriptPath = path.join(skillDir, 'scripts/example_script.cjs'); let scriptContent = fs.readFileSync(exampleScriptPath, 'utf8'); - scriptContent = scriptContent.replace(/TODO: .+/g, 'Fixed'); + scriptContent = scriptContent.replace(/TODO:[^\n]*/g, 'Fixed'); fs.writeFileSync(exampleScriptPath, scriptContent); // 4. Validate again (should pass now) @@ -90,7 +91,13 @@ describe('skill-creator scripts e2e', () => { expect(fs.existsSync(skillFile)).toBe(true); // 6. Verify zip content (should NOT have nested directory) - const zipList = execSync(`unzip -l "${skillFile}"`, { encoding: 'utf8' }); + // Use unzip -l if available, otherwise fallback to tar -tf (common on Windows) + let zipList: string; + try { + zipList = execSync(`unzip -l "${skillFile}"`, { encoding: 'utf8' }); + } catch { + zipList = execSync(`tar -tf "${skillFile}"`, { encoding: 'utf8' }); + } expect(zipList).toContain('SKILL.md'); expect(zipList).not.toContain(`${skillName}/SKILL.md`); }); diff --git a/packages/core/src/skills/builtin/skill-creator/scripts/package_skill.cjs b/packages/core/src/skills/builtin/skill-creator/scripts/package_skill.cjs index c01edff4f7..875a6f95cc 100644 --- a/packages/core/src/skills/builtin/skill-creator/scripts/package_skill.cjs +++ b/packages/core/src/skills/builtin/skill-creator/scripts/package_skill.cjs @@ -64,17 +64,32 @@ async function main() { // -r: recursive // -x: exclude patterns // Run the zip command from within the directory to avoid parent folder nesting - const zipProcess = spawnSync('zip', ['-r', outputFilename, '.'], { + let zipProcess = spawnSync('zip', ['-r', outputFilename, '.'], { cwd: skillPath, stdio: 'inherit', }); + if (zipProcess.error || zipProcess.status !== 0) { + // Fallback to tar --format=zip if zip is not available (common on Windows) + console.log('zip command not found, falling back to tar...'); + zipProcess = spawnSync( + 'tar', + ['-a', '-c', '--format=zip', '-f', outputFilename, '.'], + { + cwd: skillPath, + stdio: 'inherit', + }, + ); + } + if (zipProcess.error) { throw zipProcess.error; } if (zipProcess.status !== 0) { - throw new Error(`zip command failed with exit code ${zipProcess.status}`); + throw new Error( + `Packaging command failed with exit code ${zipProcess.status}`, + ); } console.log(`✅ Successfully packaged skill to: ${outputFilename}`);