mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-21 06:29:34 +08:00
* feat(skills): publish workflow skills to skills.sh Commit the 12 OpenSpec workflow skills as static skills/<name>/SKILL.md so `npx skills add Fission-AI/OpenSpec` can install them (skills.sh reads static files from the repo; OpenSpec otherwise only generates skills at init time). Files are generated from the existing templates via `pnpm generate:skills`, not hand-copied, and skillssh-parity.test.ts fails CI if a template changes without regenerating. The volatile generatedBy frontmatter line is stripped so the committed copies stay byte-stable across releases. Closes #1258 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(skills): force LF on committed skills/ so Windows CI parity holds The skills.sh distribution files are generated LF-only and compared byte-for-byte by skillssh-parity.test.ts. Windows autocrlf checked them out as CRLF, failing the parity assertion. A scoped .gitattributes pins them to LF on checkout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(skills): reject symlinks and assert the exact committed skill set Review feedback (alfred): the parity test only visited expected templates, so an extra or renamed skills/ directory shipped with green CI, and the generator would write through a pre-existing symlinked skill directory to anywhere on disk. - generator: refuse to run if skills/ contains any symlink (checked before any deletion, so a bad tree is left intact), validate dirNames against a path-segment allowlist, and lstat the target before writing. - parity test: assert skills/ holds exactly README.md plus one real directory per template, each containing a single real SKILL.md. - focused tests cover symlink refusal (no partial deletion), traversal names, and stale-directory cleanup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(templates): abort archive on Cancel, honest summary, fence languages CodeRabbit review on #1357, fixed at the template source and regenerated: - archive-change: choosing "Cancel" at the sync prompt now stops the flow instead of archiving anyway (skill + command templates). - archive-change skill: the success output no longer hardcodes "All artifacts complete. All tasks complete." when archiving incomplete work. - archive/bulk-archive/sync-specs/verify-change: language identifiers on previously plain code fences (MD040), skill and command twins alike. Golden hashes in skill-templates-parity.test.ts recomputed from dist/; skills/ regenerated via pnpm generate:skills. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
1.5 KiB
JavaScript
43 lines
1.5 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Generate the static skills.sh distribution of the OpenSpec workflow skills.
|
|
*
|
|
* skills.sh installs skills by reading committed `SKILL.md` files straight from
|
|
* a GitHub repo (`npx skills add Fission-AI/OpenSpec`). OpenSpec normally
|
|
* *generates* these skills into a user's project via `openspec init`, so this
|
|
* script mirrors that same output into a committed `skills/<name>/SKILL.md`
|
|
* tree that skills.sh can discover.
|
|
*
|
|
* The committed copies are kept honest by `test/core/templates/skillssh-parity.test.ts`,
|
|
* which regenerates and diffs against disk. Run this after any skill-template
|
|
* change: `pnpm build && pnpm generate:skills`.
|
|
*/
|
|
|
|
import { writeFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { getSkillTemplates, generateSkillContent } from '../dist/core/shared/skill-generation.js';
|
|
import {
|
|
cleanSkillSubdirectories,
|
|
prepareSkillDirectory,
|
|
stripVolatileFrontmatter,
|
|
SKILLS_DIR,
|
|
} from './skillssh-shared.mjs';
|
|
|
|
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
const outDir = join(repoRoot, SKILLS_DIR);
|
|
|
|
cleanSkillSubdirectories(outDir);
|
|
|
|
let count = 0;
|
|
for (const { template, dirName } of getSkillTemplates()) {
|
|
const content = stripVolatileFrontmatter(generateSkillContent(template, 'skills.sh'));
|
|
const skillDir = prepareSkillDirectory(outDir, dirName);
|
|
writeFileSync(join(skillDir, 'SKILL.md'), content, 'utf8');
|
|
count++;
|
|
}
|
|
|
|
console.log(`Generated ${count} skills into ${SKILLS_DIR}/`);
|